github.com/rawahars/moby@v24.0.4+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 // import "github.com/docker/docker/daemon/logger/etwlogs" 14 15 import ( 16 "fmt" 17 "sync" 18 "unsafe" 19 20 "github.com/Microsoft/go-winio/pkg/etw" 21 "github.com/Microsoft/go-winio/pkg/guid" 22 "github.com/docker/docker/daemon/logger" 23 "github.com/sirupsen/logrus" 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 providerGUID = `a3693192-9ed6-46d2-a981-f8226c8363bd` 37 win32CallSuccess = 0 38 ) 39 40 var ( 41 modAdvapi32 = windows.NewLazySystemDLL("Advapi32.dll") 42 procEventWriteString = modAdvapi32.NewProc("EventWriteString") 43 ) 44 45 var ( 46 providerHandle windows.Handle 47 mu sync.Mutex 48 refCount int 49 provider *etw.Provider 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 logrus.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 // TODO(thaJeztah): log structured events instead and use provider.WriteEvent(). 77 m := createLogMessage(etwLogger, msg) 78 logger.PutMessage(msg) 79 return callEventWriteString(m) 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 provider, err = callEventRegister() 108 if 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 err := callEventUnregister(); err != nil { 122 // Not returning an error if EventUnregister fails, because etwLogs will continue to work 123 return 124 } 125 refCount-- 126 provider = nil 127 providerHandle = windows.InvalidHandle 128 } else { 129 refCount-- 130 } 131 } 132 133 func callEventRegister() (*etw.Provider, error) { 134 providerID, _ := guid.FromString(providerGUID) 135 p, err := etw.NewProviderWithOptions("", etw.WithID(providerID)) 136 if err != nil { 137 logrus.WithError(err).Error("Failed to register ETW provider") 138 return nil, fmt.Errorf("failed to register ETW provider: %v", err) 139 } 140 return p, nil 141 } 142 143 // TODO(thaJeztah): port this function to github.com/Microsoft/go-winio/pkg/etw. 144 func callEventWriteString(message string) error { 145 utf16message, err := windows.UTF16FromString(message) 146 if err != nil { 147 return err 148 } 149 150 ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(&utf16message[0]))) 151 if ret != win32CallSuccess { 152 logrus.WithError(err).Error("ETWLogs provider failed to log message") 153 return fmt.Errorf("ETWLogs provider failed to log message: %v", err) 154 } 155 return nil 156 } 157 158 func callEventUnregister() error { 159 return provider.Close() 160 }