gopkg.in/dotcloud/docker.v1@v1.13.1/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(ctx logger.Context) (logger.Logger, error) { 58 if err := registerETWProvider(); err != nil { 59 return nil, err 60 } 61 logrus.Debugf("logging driver etwLogs configured for container: %s.", ctx.ContainerID) 62 63 return &etwLogs{ 64 containerName: fixContainerName(ctx.ContainerName), 65 imageName: ctx.ContainerImageName, 66 containerID: ctx.ContainerID, 67 imageID: ctx.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 // fixContainerName removes the initial '/' from the container name. 103 func fixContainerName(cntName string) string { 104 if len(cntName) > 0 && cntName[0] == '/' { 105 cntName = cntName[1:] 106 } 107 return cntName 108 } 109 110 func registerETWProvider() error { 111 mu.Lock() 112 defer mu.Unlock() 113 if refCount == 0 { 114 var err error 115 if err = callEventRegister(); err != nil { 116 return err 117 } 118 } 119 120 refCount++ 121 return nil 122 } 123 124 func unregisterETWProvider() { 125 mu.Lock() 126 defer mu.Unlock() 127 if refCount == 1 { 128 if callEventUnregister() { 129 refCount-- 130 providerHandle = syscall.InvalidHandle 131 } 132 // Not returning an error if EventUnregister fails, because etwLogs will continue to work 133 } else { 134 refCount-- 135 } 136 } 137 138 func callEventRegister() error { 139 // The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd} 140 guid := syscall.GUID{ 141 0xa3693192, 0x9ed6, 0x46d2, 142 [8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd}, 143 } 144 145 ret, _, _ := procEventRegister.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle))) 146 if ret != win32CallSuccess { 147 errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret) 148 logrus.Error(errorMessage) 149 return errors.New(errorMessage) 150 } 151 return nil 152 } 153 154 func callEventWriteString(message string) error { 155 ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(message)))) 156 if ret != win32CallSuccess { 157 errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret) 158 logrus.Error(errorMessage) 159 return errors.New(errorMessage) 160 } 161 return nil 162 } 163 164 func callEventUnregister() bool { 165 ret, _, _ := procEventUnregister.Call(uintptr(providerHandle)) 166 if ret != win32CallSuccess { 167 return false 168 } 169 return true 170 }