github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/daemon/logger/etwlogs/etwlogs_windows.go (about)

     1  // Package etwlogs provides a log driver for forwarding container logs
     2  // as ETW events.(ETW stands for Event Tracing for Windows)
     3  // A client can then create an ETW listener to listen for events that are sent
     4  // by the ETW provider that we register, using the provider's GUID "a3693192-9ed6-46d2-a981-f8226c8363bd".
     5  // Here is an example of how to do this using the logman utility:
     6  // 1. logman start -ets DockerContainerLogs -p {a3693192-9ed6-46d2-a981-f8226c8363bd} 0 0 -o trace.etl
     7  // 2. Run container(s) and generate log messages
     8  // 3. logman stop -ets DockerContainerLogs
     9  // 4. You can then convert the etl log file to XML using: tracerpt -y trace.etl
    10  //
    11  // Each container log message generates an ETW event that also contains:
    12  // the container name and ID, the timestamp, and the stream type.
    13  package etwlogs
    14  
    15  import (
    16  	"errors"
    17  	"fmt"
    18  	"sync"
    19  	"syscall"
    20  	"unsafe"
    21  
    22  	"github.com/Sirupsen/logrus"
    23  	"github.com/docker/docker/daemon/logger"
    24  	"golang.org/x/sys/windows"
    25  )
    26  
    27  type etwLogs struct {
    28  	containerName string
    29  	imageName     string
    30  	containerID   string
    31  	imageID       string
    32  }
    33  
    34  const (
    35  	name             = "etwlogs"
    36  	win32CallSuccess = 0
    37  )
    38  
    39  var (
    40  	modAdvapi32          = windows.NewLazySystemDLL("Advapi32.dll")
    41  	procEventRegister    = modAdvapi32.NewProc("EventRegister")
    42  	procEventWriteString = modAdvapi32.NewProc("EventWriteString")
    43  	procEventUnregister  = modAdvapi32.NewProc("EventUnregister")
    44  )
    45  var providerHandle syscall.Handle
    46  var refCount int
    47  var mu sync.Mutex
    48  
    49  func init() {
    50  	providerHandle = syscall.InvalidHandle
    51  	if err := logger.RegisterLogDriver(name, New); err != nil {
    52  		logrus.Fatal(err)
    53  	}
    54  }
    55  
    56  // New creates a new etwLogs logger for the given container and registers the EWT provider.
    57  func New(info logger.Info) (logger.Logger, error) {
    58  	if err := registerETWProvider(); err != nil {
    59  		return nil, err
    60  	}
    61  	logrus.Debugf("logging driver etwLogs configured for container: %s.", info.ContainerID)
    62  
    63  	return &etwLogs{
    64  		containerName: info.Name(),
    65  		imageName:     info.ContainerImageName,
    66  		containerID:   info.ContainerID,
    67  		imageID:       info.ContainerImageID,
    68  	}, nil
    69  }
    70  
    71  // Log logs the message to the ETW stream.
    72  func (etwLogger *etwLogs) Log(msg *logger.Message) error {
    73  	if providerHandle == syscall.InvalidHandle {
    74  		// This should never be hit, if it is, it indicates a programming error.
    75  		errorMessage := "ETWLogs cannot log the message, because the event provider has not been registered."
    76  		logrus.Error(errorMessage)
    77  		return errors.New(errorMessage)
    78  	}
    79  	m := createLogMessage(etwLogger, msg)
    80  	logger.PutMessage(msg)
    81  	return callEventWriteString(m)
    82  }
    83  
    84  // Close closes the logger by unregistering the ETW provider.
    85  func (etwLogger *etwLogs) Close() error {
    86  	unregisterETWProvider()
    87  	return nil
    88  }
    89  
    90  func (etwLogger *etwLogs) Name() string {
    91  	return name
    92  }
    93  
    94  func createLogMessage(etwLogger *etwLogs, msg *logger.Message) string {
    95  	return fmt.Sprintf("container_name: %s, image_name: %s, container_id: %s, image_id: %s, source: %s, log: %s",
    96  		etwLogger.containerName,
    97  		etwLogger.imageName,
    98  		etwLogger.containerID,
    99  		etwLogger.imageID,
   100  		msg.Source,
   101  		msg.Line)
   102  }
   103  
   104  func registerETWProvider() error {
   105  	mu.Lock()
   106  	defer mu.Unlock()
   107  	if refCount == 0 {
   108  		var err error
   109  		if err = callEventRegister(); err != nil {
   110  			return err
   111  		}
   112  	}
   113  
   114  	refCount++
   115  	return nil
   116  }
   117  
   118  func unregisterETWProvider() {
   119  	mu.Lock()
   120  	defer mu.Unlock()
   121  	if refCount == 1 {
   122  		if callEventUnregister() {
   123  			refCount--
   124  			providerHandle = syscall.InvalidHandle
   125  		}
   126  		// Not returning an error if EventUnregister fails, because etwLogs will continue to work
   127  	} else {
   128  		refCount--
   129  	}
   130  }
   131  
   132  func callEventRegister() error {
   133  	// The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd}
   134  	guid := syscall.GUID{
   135  		Data1: 0xa3693192,
   136  		Data2: 0x9ed6,
   137  		Data3: 0x46d2,
   138  		Data4: [8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd},
   139  	}
   140  
   141  	ret, _, _ := procEventRegister.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle)))
   142  	if ret != win32CallSuccess {
   143  		errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
   144  		logrus.Error(errorMessage)
   145  		return errors.New(errorMessage)
   146  	}
   147  	return nil
   148  }
   149  
   150  func callEventWriteString(message string) error {
   151  	utf16message, err := syscall.UTF16FromString(message)
   152  
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(&utf16message[0])))
   158  	if ret != win32CallSuccess {
   159  		errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret)
   160  		logrus.Error(errorMessage)
   161  		return errors.New(errorMessage)
   162  	}
   163  	return nil
   164  }
   165  
   166  func callEventUnregister() bool {
   167  	ret, _, _ := procEventUnregister.Call(uintptr(providerHandle))
   168  	return ret == win32CallSuccess
   169  }