...
1
2
3
4 package etwlogrus
5
6 import (
7 "errors"
8 "sort"
9
10 "github.com/sirupsen/logrus"
11
12 "github.com/Microsoft/go-winio/pkg/etw"
13 )
14
15 const defaultEventName = "LogrusEntry"
16
17
18 var ErrNoProvider = errors.New("no ETW registered provider")
19
20
21 type HookOpt func(*Hook) error
22
23
24 type Hook struct {
25 provider *etw.Provider
26 closeProvider bool
27
28 getName func(*logrus.Entry) string
29
30 getEventsOpts func(*logrus.Entry) []etw.EventOpt
31 }
32
33
34
35 func NewHook(providerName string, opts ...HookOpt) (*Hook, error) {
36 opts = append(opts, WithNewETWProvider(providerName))
37
38 return NewHookFromOpts(opts...)
39 }
40
41
42
43 func NewHookFromProvider(provider *etw.Provider, opts ...HookOpt) (*Hook, error) {
44 opts = append(opts, WithExistingETWProvider(provider))
45
46 return NewHookFromOpts(opts...)
47 }
48
49
50
51 func NewHookFromOpts(opts ...HookOpt) (*Hook, error) {
52 h := defaultHook()
53
54 for _, o := range opts {
55 if err := o(h); err != nil {
56 return nil, err
57 }
58 }
59 return h, h.validate()
60 }
61
62 func defaultHook() *Hook {
63 h := &Hook{}
64 return h
65 }
66
67 func (h *Hook) validate() error {
68 if h.provider == nil {
69 return ErrNoProvider
70 }
71 return nil
72 }
73
74
75
76 func (*Hook) Levels() []logrus.Level {
77 return logrus.AllLevels
78 }
79
80 var logrusToETWLevelMap = map[logrus.Level]etw.Level{
81 logrus.PanicLevel: etw.LevelAlways,
82 logrus.FatalLevel: etw.LevelCritical,
83 logrus.ErrorLevel: etw.LevelError,
84 logrus.WarnLevel: etw.LevelWarning,
85 logrus.InfoLevel: etw.LevelInfo,
86 logrus.DebugLevel: etw.LevelVerbose,
87 logrus.TraceLevel: etw.LevelVerbose,
88 }
89
90
91 func (h *Hook) Fire(e *logrus.Entry) error {
92
93
94
95 level := logrusToETWLevelMap[e.Level]
96 if !h.provider.IsEnabledForLevel(level) {
97 return nil
98 }
99
100 name := defaultEventName
101 if h.getName != nil {
102 if n := h.getName(e); n != "" {
103 name = n
104 }
105 }
106
107
108
109 opts := make([]etw.EventOpt, 0, 3)
110 opts = append(opts, etw.WithLevel(level))
111 if h.getEventsOpts != nil {
112 opts = append(opts, h.getEventsOpts(e)...)
113 }
114
115
116
117 names := make([]string, 0, len(e.Data))
118 hasError := false
119 for k := range e.Data {
120 if k == logrus.ErrorKey {
121
122 hasError = true
123 } else {
124 names = append(names, k)
125 }
126 }
127 sort.Strings(names)
128
129
130 fields := make([]etw.FieldOpt, 0, len(e.Data)+2)
131 fields = append(fields, etw.StringField("Message", e.Message))
132 fields = append(fields, etw.Time("Time", e.Time))
133 for _, k := range names {
134 fields = append(fields, etw.SmartField(k, e.Data[k]))
135 }
136 if hasError {
137 fields = append(fields, etw.SmartField(logrus.ErrorKey, e.Data[logrus.ErrorKey]))
138 }
139
140
141
142
143
144
145 _ = h.provider.WriteEvent(name, opts, fields)
146
147 return nil
148 }
149
150
151
152
153 func (h *Hook) Close() error {
154 if h.closeProvider {
155 return h.provider.Close()
156 }
157 return nil
158 }
159
View as plain text