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 }