github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/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 "unsafe" 20 21 "github.com/docker/docker/daemon/logger" 22 "github.com/sirupsen/logrus" 23 "golang.org/x/sys/windows" 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 ( 39 modAdvapi32 = windows.NewLazySystemDLL("Advapi32.dll") 40 procEventRegister = modAdvapi32.NewProc("EventRegister") 41 procEventWriteString = modAdvapi32.NewProc("EventWriteString") 42 procEventUnregister = modAdvapi32.NewProc("EventUnregister") 43 ) 44 var providerHandle windows.Handle 45 var refCount int 46 var mu sync.Mutex 47 48 func init() { 49 providerHandle = windows.InvalidHandle 50 if err := logger.RegisterLogDriver(name, New); err != nil { 51 logrus.Fatal(err) 52 } 53 } 54 55 // New creates a new etwLogs logger for the given container and registers the EWT provider. 56 func New(info logger.Info) (logger.Logger, error) { 57 if err := registerETWProvider(); err != nil { 58 return nil, err 59 } 60 logrus.Debugf("logging driver etwLogs configured for container: %s.", info.ContainerID) 61 62 return &etwLogs{ 63 containerName: info.Name(), 64 imageName: info.ContainerImageName, 65 containerID: info.ContainerID, 66 imageID: info.ContainerImageID, 67 }, nil 68 } 69 70 // Log logs the message to the ETW stream. 71 func (etwLogger *etwLogs) Log(msg *logger.Message) error { 72 if providerHandle == windows.InvalidHandle { 73 // This should never be hit, if it is, it indicates a programming error. 74 errorMessage := "ETWLogs cannot log the message, because the event provider has not been registered." 75 logrus.Error(errorMessage) 76 return errors.New(errorMessage) 77 } 78 m := createLogMessage(etwLogger, msg) 79 logger.PutMessage(msg) 80 return callEventWriteString(m) 81 } 82 83 // Close closes the logger by unregistering the ETW provider. 84 func (etwLogger *etwLogs) Close() error { 85 unregisterETWProvider() 86 return nil 87 } 88 89 func (etwLogger *etwLogs) Name() string { 90 return name 91 } 92 93 func createLogMessage(etwLogger *etwLogs, msg *logger.Message) string { 94 return fmt.Sprintf("container_name: %s, image_name: %s, container_id: %s, image_id: %s, source: %s, log: %s", 95 etwLogger.containerName, 96 etwLogger.imageName, 97 etwLogger.containerID, 98 etwLogger.imageID, 99 msg.Source, 100 msg.Line) 101 } 102 103 func registerETWProvider() error { 104 mu.Lock() 105 defer mu.Unlock() 106 if refCount == 0 { 107 var err error 108 if err = callEventRegister(); err != nil { 109 return err 110 } 111 } 112 113 refCount++ 114 return nil 115 } 116 117 func unregisterETWProvider() { 118 mu.Lock() 119 defer mu.Unlock() 120 if refCount == 1 { 121 if callEventUnregister() { 122 refCount-- 123 providerHandle = windows.InvalidHandle 124 } 125 // Not returning an error if EventUnregister fails, because etwLogs will continue to work 126 } else { 127 refCount-- 128 } 129 } 130 131 func callEventRegister() error { 132 // The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd} 133 guid := windows.GUID{ 134 Data1: 0xa3693192, 135 Data2: 0x9ed6, 136 Data3: 0x46d2, 137 Data4: [8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd}, 138 } 139 140 ret, _, _ := procEventRegister.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle))) 141 if ret != win32CallSuccess { 142 errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret) 143 logrus.Error(errorMessage) 144 return errors.New(errorMessage) 145 } 146 return nil 147 } 148 149 func callEventWriteString(message string) error { 150 utf16message, err := windows.UTF16FromString(message) 151 152 if err != nil { 153 return err 154 } 155 156 ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(&utf16message[0]))) 157 if ret != win32CallSuccess { 158 errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret) 159 logrus.Error(errorMessage) 160 return errors.New(errorMessage) 161 } 162 return nil 163 } 164 165 func callEventUnregister() bool { 166 ret, _, _ := procEventUnregister.Call(uintptr(providerHandle)) 167 return ret == win32CallSuccess 168 }