Tizen_4.0 base
[platform/upstream/docker-engine.git] / daemon / logger / etwlogs / etwlogs_windows.go
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(info logger.Info) (logger.Logger, error) {
58         if err := registerETWProvider(); err != nil {
59                 return nil, err
60         }
61         logrus.Debugf("logging driver etwLogs configured for container: %s.", info.ContainerID)
62
63         return &etwLogs{
64                 containerName: info.Name(),
65                 imageName:     info.ContainerImageName,
66                 containerID:   info.ContainerID,
67                 imageID:       info.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         m := createLogMessage(etwLogger, msg)
80         logger.PutMessage(msg)
81         return callEventWriteString(m)
82 }
83
84 // Close closes the logger by unregistering the ETW provider.
85 func (etwLogger *etwLogs) Close() error {
86         unregisterETWProvider()
87         return nil
88 }
89
90 func (etwLogger *etwLogs) Name() string {
91         return name
92 }
93
94 func createLogMessage(etwLogger *etwLogs, msg *logger.Message) string {
95         return fmt.Sprintf("container_name: %s, image_name: %s, container_id: %s, image_id: %s, source: %s, log: %s",
96                 etwLogger.containerName,
97                 etwLogger.imageName,
98                 etwLogger.containerID,
99                 etwLogger.imageID,
100                 msg.Source,
101                 msg.Line)
102 }
103
104 func registerETWProvider() error {
105         mu.Lock()
106         defer mu.Unlock()
107         if refCount == 0 {
108                 var err error
109                 if err = callEventRegister(); err != nil {
110                         return err
111                 }
112         }
113
114         refCount++
115         return nil
116 }
117
118 func unregisterETWProvider() {
119         mu.Lock()
120         defer mu.Unlock()
121         if refCount == 1 {
122                 if callEventUnregister() {
123                         refCount--
124                         providerHandle = syscall.InvalidHandle
125                 }
126                 // Not returning an error if EventUnregister fails, because etwLogs will continue to work
127         } else {
128                 refCount--
129         }
130 }
131
132 func callEventRegister() error {
133         // The provider's GUID is {a3693192-9ed6-46d2-a981-f8226c8363bd}
134         guid := syscall.GUID{
135                 Data1: 0xa3693192,
136                 Data2: 0x9ed6,
137                 Data3: 0x46d2,
138                 Data4: [8]byte{0xa9, 0x81, 0xf8, 0x22, 0x6c, 0x83, 0x63, 0xbd},
139         }
140
141         ret, _, _ := procEventRegister.Call(uintptr(unsafe.Pointer(&guid)), 0, 0, uintptr(unsafe.Pointer(&providerHandle)))
142         if ret != win32CallSuccess {
143                 errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
144                 logrus.Error(errorMessage)
145                 return errors.New(errorMessage)
146         }
147         return nil
148 }
149
150 func callEventWriteString(message string) error {
151         utf16message, err := syscall.UTF16FromString(message)
152
153         if err != nil {
154                 return err
155         }
156
157         ret, _, _ := procEventWriteString.Call(uintptr(providerHandle), 0, 0, uintptr(unsafe.Pointer(&utf16message[0])))
158         if ret != win32CallSuccess {
159                 errorMessage := fmt.Sprintf("ETWLogs provider failed to log message. Error: %d", ret)
160                 logrus.Error(errorMessage)
161                 return errors.New(errorMessage)
162         }
163         return nil
164 }
165
166 func callEventUnregister() bool {
167         ret, _, _ := procEventUnregister.Call(uintptr(providerHandle))
168         return ret == win32CallSuccess
169 }