etwlogs_windows.go 4.6 KB

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