github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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 return callEventWriteString(createLogMessage(etwLogger, msg)) 80 } 81 82 // Close closes the logger by unregistering the ETW provider. 83 func (etwLogger *etwLogs) Close() error { 84 unregisterETWProvider() 85 return nil 86 } 87 88 func (etwLogger *etwLogs) Name() string { 89 return name 90 } 91 92 func createLogMessage(etwLogger *etwLogs, msg *logger.Message) string { 93 return fmt.Sprintf("container_name: %s, image_name: %s, container_id: %s, image_id: %s, source: %s, log: %s", 94 etwLogger.containerName, 95 etwLogger.imageName, 96 etwLogger.containerID, 97 etwLogger.imageID, 98 msg.Source, 99 msg.Line) 100 } 101 102 func registerETWProvider() error { 103 mu.Lock() 104 defer mu.Unlock() 105 if refCount == 0 { 106 var err error 107 if err = callEventRegister(); err != nil { 108 return err 109 } 110 } 111 112 refCount++ 113 return nil 114 } 115 116 func unregisterETWProvider() { 117 mu.Lock() 118 defer mu.Unlock() 119 if refCount == 1 { 120 if callEventUnregister() { 121 refCount-- 122 providerHandle = syscall.InvalidHandle 123 } 124 // Not returning an error if EventUnregister fails, because etwLogs will continue to work 125 } else { 126 refCount-- 127 } 128 } 129 130 func callEventRegister() error { 131 // The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd} 132 guid := syscall.GUID{ 133 Data1: 0xa3693192, 134 Data2: 0x9ed6, 135 Data3: 0x46d2, 136 Data4: [8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd}, 137 } 138 139 ret, _, _ := procEventRegister.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle))) 140 if ret != win32CallSuccess { 141 errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret) 142 logrus.Error(errorMessage) 143 return errors.New(errorMessage) 144 } 145 return nil 146 } 147 148 func callEventWriteString(message string) error { 149 ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(message)))) 150 if ret != win32CallSuccess { 151 errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret) 152 logrus.Error(errorMessage) 153 return errors.New(errorMessage) 154 } 155 return nil 156 } 157 158 func callEventUnregister() bool { 159 ret, _, _ := procEventUnregister.Call(uintptr(providerHandle)) 160 return ret == win32CallSuccess 161 }