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