github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/builtin/input/windows/operator.go (about) 1 // +build windows 2 3 package windows 4 5 import ( 6 "context" 7 "fmt" 8 "sync" 9 "time" 10 11 "github.com/observiq/carbon/operator" 12 "github.com/observiq/carbon/operator/helper" 13 ) 14 15 func init() { 16 operator.Register("windows_eventlog_input", NewDefaultConfig) 17 } 18 19 // EventLogConfig is the configuration of a windows event log operator. 20 type EventLogConfig struct { 21 helper.InputConfig `yaml:",inline"` 22 Channel string `json:"channel" yaml:"channel"` 23 MaxReads int `json:"max_reads,omitempty" yaml:"max_reads,omitempty"` 24 StartAt string `json:"start_at,omitempty" yaml:"start_at,omitempty"` 25 PollInterval operator.Duration `json:"poll_interval,omitempty" yaml:"poll_interval,omitempty"` 26 } 27 28 // Build will build a windows event log operator. 29 func (c *EventLogConfig) Build(context operator.BuildContext) (operator.Operator, error) { 30 inputOperator, err := c.InputConfig.Build(context) 31 if err != nil { 32 return nil, err 33 } 34 35 if c.Channel == "" { 36 return nil, fmt.Errorf("missing required `channel` field") 37 } 38 39 if c.MaxReads < 1 { 40 return nil, fmt.Errorf("the `max_reads` field must be greater than zero") 41 } 42 43 if c.StartAt != "end" && c.StartAt != "beginning" { 44 return nil, fmt.Errorf("the `start_at` field must be set to `beginning` or `end`") 45 } 46 47 offsets := helper.NewScopedDBPersister(context.Database, c.ID()) 48 49 eventLogInput := &EventLogInput{ 50 InputOperator: inputOperator, 51 buffer: NewBuffer(), 52 channel: c.Channel, 53 maxReads: c.MaxReads, 54 startAt: c.StartAt, 55 pollInterval: c.PollInterval, 56 offsets: offsets, 57 } 58 return eventLogInput, nil 59 } 60 61 // NewDefaultConfig will return an event log config with default values. 62 func NewDefaultConfig() operator.Builder { 63 return &EventLogConfig{ 64 InputConfig: helper.NewInputConfig("", "windows_eventlog_input"), 65 MaxReads: 100, 66 StartAt: "end", 67 PollInterval: operator.Duration{ 68 Duration: 1 * time.Second, 69 }, 70 } 71 } 72 73 // EventLogInput is an operator that creates entries using the windows event log api. 74 type EventLogInput struct { 75 helper.InputOperator 76 bookmark Bookmark 77 subscription Subscription 78 buffer Buffer 79 channel string 80 maxReads int 81 startAt string 82 pollInterval operator.Duration 83 offsets helper.Persister 84 cancel context.CancelFunc 85 wg *sync.WaitGroup 86 } 87 88 // Start will start reading events from a subscription. 89 func (e *EventLogInput) Start() error { 90 ctx, cancel := context.WithCancel(context.Background()) 91 e.cancel = cancel 92 e.wg = &sync.WaitGroup{} 93 94 e.bookmark = NewBookmark() 95 offsetXML, err := e.getBookmarkOffset() 96 if err != nil { 97 return fmt.Errorf("failed to retrieve bookmark offset: %s", err) 98 } 99 100 if offsetXML != "" { 101 if err := e.bookmark.Open(offsetXML); err != nil { 102 return fmt.Errorf("failed to open bookmark: %s", err) 103 } 104 } 105 106 e.subscription = NewSubscription() 107 if err := e.subscription.Open(e.channel, e.startAt, e.bookmark); err != nil { 108 return fmt.Errorf("failed to open subscription: %s", err) 109 } 110 111 e.wg.Add(1) 112 go e.readOnInterval(ctx) 113 return nil 114 } 115 116 // Stop will stop reading events from a subscription. 117 func (e *EventLogInput) Stop() error { 118 e.cancel() 119 e.wg.Wait() 120 121 if err := e.subscription.Close(); err != nil { 122 return fmt.Errorf("failed to close subscription: %s", err) 123 } 124 125 if err := e.bookmark.Close(); err != nil { 126 return fmt.Errorf("failed to close bookmark: %s", err) 127 } 128 129 return nil 130 } 131 132 // readOnInterval will read events with respect to the polling interval. 133 func (e *EventLogInput) readOnInterval(ctx context.Context) { 134 defer e.wg.Done() 135 136 ticker := time.NewTicker(e.pollInterval.Raw()) 137 defer ticker.Stop() 138 139 for { 140 select { 141 case <-ctx.Done(): 142 return 143 case <-ticker.C: 144 e.readToEnd(ctx) 145 } 146 } 147 } 148 149 // readToEnd will read events from the subscription until it reaches the end of the channel. 150 func (e *EventLogInput) readToEnd(ctx context.Context) { 151 for { 152 select { 153 case <-ctx.Done(): 154 return 155 default: 156 if count := e.read(ctx); count == 0 { 157 return 158 } 159 } 160 } 161 } 162 163 // read will read events from the subscription. 164 func (e *EventLogInput) read(ctx context.Context) int { 165 events, err := e.subscription.Read(e.maxReads) 166 if err != nil { 167 e.Errorf("Failed to read events from subscription: %s", err) 168 return 0 169 } 170 171 for i, event := range events { 172 e.processEvent(ctx, event) 173 if len(events) == i+1 { 174 e.updateBookmarkOffset(event) 175 } 176 event.Close() 177 } 178 179 return len(events) 180 } 181 182 // processEvent will process and send an event retrieved from windows event log. 183 func (e *EventLogInput) processEvent(ctx context.Context, event Event) { 184 simpleEvent, err := event.RenderSimple(e.buffer) 185 if err != nil { 186 e.Errorf("Failed to render simple event: %s", err) 187 return 188 } 189 190 publisher := NewPublisher() 191 if err := publisher.Open(simpleEvent.Provider.Name); err != nil { 192 e.Errorf("Failed to open publisher: %s") 193 e.sendEvent(ctx, simpleEvent) 194 return 195 } 196 defer publisher.Close() 197 198 formattedEvent, err := event.RenderFormatted(e.buffer, publisher) 199 if err != nil { 200 e.Errorf("Failed to render formatted event: %s", err) 201 e.sendEvent(ctx, simpleEvent) 202 return 203 } 204 205 e.sendEvent(ctx, formattedEvent) 206 } 207 208 // sendEvent will send EventXML as an entry to the operator's output. 209 func (e *EventLogInput) sendEvent(ctx context.Context, eventXML EventXML) { 210 record := eventXML.parseRecord() 211 entry, err := e.NewEntry(record) 212 if err != nil { 213 e.Errorf("Failed to create entry: %s", err) 214 return 215 } 216 217 entry.Timestamp = eventXML.parseTimestamp() 218 entry.Severity = eventXML.parseSeverity() 219 e.Write(ctx, entry) 220 } 221 222 // getBookmarkXML will get the bookmark xml from the offsets database. 223 func (e *EventLogInput) getBookmarkOffset() (string, error) { 224 if err := e.offsets.Load(); err != nil { 225 return "", fmt.Errorf("failed to load offsets database: %s", err) 226 } 227 228 bytes := e.offsets.Get(e.channel) 229 return string(bytes), nil 230 } 231 232 // updateBookmark will update the bookmark xml and save it in the offsets database. 233 func (e *EventLogInput) updateBookmarkOffset(event Event) { 234 if err := e.bookmark.Update(event); err != nil { 235 e.Errorf("Failed to update bookmark from event: %s", err) 236 return 237 } 238 239 bookmarkXML, err := e.bookmark.Render(e.buffer) 240 if err != nil { 241 e.Errorf("Failed to render bookmark xml: %s", err) 242 return 243 } 244 245 e.offsets.Set(e.channel, []byte(bookmarkXML)) 246 if err := e.offsets.Sync(); err != nil { 247 e.Errorf("failed to sync offsets database: %s", err) 248 return 249 } 250 }