github.com/dpiddy/docker@v1.12.2-rc1/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  )
    25  
    26  type etwLogs struct {
    27  	containerName string
    28  	imageName     string
    29  	containerID   string
    30  	imageID       string
    31  }
    32  
    33  const (
    34  	name             = "etwlogs"
    35  	win32CallSuccess = 0
    36  )
    37  
    38  var win32Lib *syscall.DLL
    39  var providerHandle syscall.Handle
    40  var refCount int
    41  var mu sync.Mutex
    42  
    43  func init() {
    44  	providerHandle = syscall.InvalidHandle
    45  	if err := logger.RegisterLogDriver(name, New); err != nil {
    46  		logrus.Fatal(err)
    47  	}
    48  }
    49  
    50  // New creates a new etwLogs logger for the given container and registers the EWT provider.
    51  func New(ctx logger.Context) (logger.Logger, error) {
    52  	if err := registerETWProvider(); err != nil {
    53  		return nil, err
    54  	}
    55  	logrus.Debugf("logging driver etwLogs configured for container: %s.", ctx.ContainerID)
    56  
    57  	return &etwLogs{
    58  		containerName: fixContainerName(ctx.ContainerName),
    59  		imageName:     ctx.ContainerImageName,
    60  		containerID:   ctx.ContainerID,
    61  		imageID:       ctx.ContainerImageID,
    62  	}, nil
    63  }
    64  
    65  // Log logs the message to the ETW stream.
    66  func (etwLogger *etwLogs) Log(msg *logger.Message) error {
    67  	if providerHandle == syscall.InvalidHandle {
    68  		// This should never be hit, if it is, it indicates a programming error.
    69  		errorMessage := "ETWLogs cannot log the message, because the event provider has not been registered."
    70  		logrus.Error(errorMessage)
    71  		return errors.New(errorMessage)
    72  	}
    73  	return callEventWriteString(createLogMessage(etwLogger, msg))
    74  }
    75  
    76  // Close closes the logger by unregistering the ETW provider.
    77  func (etwLogger *etwLogs) Close() error {
    78  	unregisterETWProvider()
    79  	return nil
    80  }
    81  
    82  func (etwLogger *etwLogs) Name() string {
    83  	return name
    84  }
    85  
    86  func createLogMessage(etwLogger *etwLogs, msg *logger.Message) string {
    87  	return fmt.Sprintf("container_name: %s, image_name: %s, container_id: %s, image_id: %s, source: %s, log: %s",
    88  		etwLogger.containerName,
    89  		etwLogger.imageName,
    90  		etwLogger.containerID,
    91  		etwLogger.imageID,
    92  		msg.Source,
    93  		msg.Line)
    94  }
    95  
    96  // fixContainerName removes the initial '/' from the container name.
    97  func fixContainerName(cntName string) string {
    98  	if len(cntName) > 0 && cntName[0] == '/' {
    99  		cntName = cntName[1:]
   100  	}
   101  	return cntName
   102  }
   103  
   104  func registerETWProvider() error {
   105  	mu.Lock()
   106  	defer mu.Unlock()
   107  	if refCount == 0 {
   108  		var err error
   109  		if win32Lib, err = syscall.LoadDLL("Advapi32.dll"); err != nil {
   110  			return err
   111  		}
   112  		if err = callEventRegister(); err != nil {
   113  			win32Lib.Release()
   114  			win32Lib = nil
   115  			return err
   116  		}
   117  	}
   118  
   119  	refCount++
   120  	return nil
   121  }
   122  
   123  func unregisterETWProvider() {
   124  	mu.Lock()
   125  	defer mu.Unlock()
   126  	if refCount == 1 {
   127  		if callEventUnregister() {
   128  			refCount--
   129  			providerHandle = syscall.InvalidHandle
   130  			win32Lib.Release()
   131  			win32Lib = nil
   132  		}
   133  		// Not returning an error if EventUnregister fails, because etwLogs will continue to work
   134  	} else {
   135  		refCount--
   136  	}
   137  }
   138  
   139  func callEventRegister() error {
   140  	proc, err := win32Lib.FindProc("EventRegister")
   141  	if err != nil {
   142  		return err
   143  	}
   144  	// The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd}
   145  	guid := syscall.GUID{
   146  		0xa3693192, 0x9ed6, 0x46d2,
   147  		[8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd},
   148  	}
   149  
   150  	ret, _, _ := proc.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle)))
   151  	if ret != win32CallSuccess {
   152  		errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
   153  		logrus.Error(errorMessage)
   154  		return errors.New(errorMessage)
   155  	}
   156  	return nil
   157  }
   158  
   159  func callEventWriteString(message string) error {
   160  	proc, err := win32Lib.FindProc("EventWriteString")
   161  	if err != nil {
   162  		return err
   163  	}
   164  	ret, _, _ := proc.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(message))))
   165  	if ret != win32CallSuccess {
   166  		errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret)
   167  		logrus.Error(errorMessage)
   168  		return errors.New(errorMessage)
   169  	}
   170  	return nil
   171  }
   172  
   173  func callEventUnregister() bool {
   174  	proc, err := win32Lib.FindProc("EventUnregister")
   175  	if err != nil {
   176  		return false
   177  	}
   178  	ret, _, _ := proc.Call(uintptr(providerHandle))
   179  	if ret != win32CallSuccess {
   180  		return false
   181  	}
   182  	return true
   183  }