178 lines
4.7 KiB
Go
178 lines
4.7 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
||
|
// the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing,
|
||
|
// software distributed under the License is distributed on an
|
||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
// KIND, either express or implied. See the License for the
|
||
|
// specific language governing permissions and limitations
|
||
|
// under the License.
|
||
|
|
||
|
/*
|
||
|
Package beater provides the implementation of the libbeat Beater interface for
|
||
|
Winlogbeat. The main event loop is implemented in this package.
|
||
|
*/
|
||
|
package beater
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/elastic/beats/libbeat/beat"
|
||
|
"github.com/elastic/beats/libbeat/common"
|
||
|
"github.com/elastic/beats/libbeat/logp"
|
||
|
"github.com/elastic/beats/libbeat/paths"
|
||
|
|
||
|
"github.com/elastic/beats/winlogbeat/checkpoint"
|
||
|
"github.com/elastic/beats/winlogbeat/config"
|
||
|
"github.com/elastic/beats/winlogbeat/eventlog"
|
||
|
)
|
||
|
|
||
|
// Debug logging functions for this package.
|
||
|
var (
|
||
|
debugf = logp.MakeDebug("winlogbeat")
|
||
|
)
|
||
|
|
||
|
// Time the application was started.
|
||
|
var startTime = time.Now().UTC()
|
||
|
|
||
|
// Winlogbeat is used to conform to the beat interface
|
||
|
type Winlogbeat struct {
|
||
|
beat *beat.Beat // Common beat information.
|
||
|
config config.WinlogbeatConfig // Configuration settings.
|
||
|
eventLogs []*eventLogger // List of all event logs being monitored.
|
||
|
done chan struct{} // Channel to initiate shutdown of main event loop.
|
||
|
pipeline beat.Pipeline // Interface to publish event.
|
||
|
checkpoint *checkpoint.Checkpoint // Persists event log state to disk.
|
||
|
}
|
||
|
|
||
|
// New returns a new Winlogbeat.
|
||
|
func New(b *beat.Beat, _ *common.Config) (beat.Beater, error) {
|
||
|
// Read configuration.
|
||
|
config := config.DefaultSettings
|
||
|
err := b.BeatConfig.Unpack(&config)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("Error reading configuration file. %v", err)
|
||
|
}
|
||
|
|
||
|
// resolve registry file path
|
||
|
config.RegistryFile = paths.Resolve(paths.Data, config.RegistryFile)
|
||
|
logp.Info("State will be read from and persisted to %s",
|
||
|
config.RegistryFile)
|
||
|
|
||
|
eb := &Winlogbeat{
|
||
|
beat: b,
|
||
|
config: config,
|
||
|
done: make(chan struct{}),
|
||
|
}
|
||
|
|
||
|
if err := eb.init(b); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return eb, nil
|
||
|
}
|
||
|
|
||
|
func (eb *Winlogbeat) init(b *beat.Beat) error {
|
||
|
config := &eb.config
|
||
|
|
||
|
// Create the event logs. This will validate the event log specific
|
||
|
// configuration.
|
||
|
eb.eventLogs = make([]*eventLogger, 0, len(config.EventLogs))
|
||
|
for _, config := range config.EventLogs {
|
||
|
eventLog, err := eventlog.New(config)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("Failed to create new event log. %v", err)
|
||
|
}
|
||
|
debugf("Initialized EventLog[%s]", eventLog.Name())
|
||
|
|
||
|
logger, err := newEventLogger(eventLog, config)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("Failed to create new event log. %v", err)
|
||
|
}
|
||
|
|
||
|
eb.eventLogs = append(eb.eventLogs, logger)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Setup uses the loaded config and creates necessary markers and environment
|
||
|
// settings to allow the beat to be used.
|
||
|
func (eb *Winlogbeat) setup(b *beat.Beat) error {
|
||
|
config := &eb.config
|
||
|
|
||
|
var err error
|
||
|
eb.checkpoint, err = checkpoint.NewCheckpoint(config.RegistryFile, 10, 5*time.Second)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
eb.pipeline = b.Publisher
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Run is used within the beats interface to execute the Winlogbeat workers.
|
||
|
func (eb *Winlogbeat) Run(b *beat.Beat) error {
|
||
|
if err := eb.setup(b); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
persistedState := eb.checkpoint.States()
|
||
|
|
||
|
// Initialize metrics.
|
||
|
initMetrics("total")
|
||
|
|
||
|
// setup global event ACK handler
|
||
|
err := eb.pipeline.SetACKHandler(beat.PipelineACKHandler{
|
||
|
ACKLastEvents: func(data []interface{}) {
|
||
|
for _, datum := range data {
|
||
|
if st, ok := datum.(checkpoint.EventLogState); ok {
|
||
|
eb.checkpoint.PersistState(st)
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var wg sync.WaitGroup
|
||
|
for _, log := range eb.eventLogs {
|
||
|
state, _ := persistedState[log.source.Name()]
|
||
|
|
||
|
// Start a goroutine for each event log.
|
||
|
wg.Add(1)
|
||
|
go eb.processEventLog(&wg, log, state)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
eb.checkpoint.Shutdown()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Stop is used to tell the winlogbeat that it should cease executing.
|
||
|
func (eb *Winlogbeat) Stop() {
|
||
|
logp.Info("Stopping Winlogbeat")
|
||
|
if eb.done != nil {
|
||
|
close(eb.done)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (eb *Winlogbeat) processEventLog(
|
||
|
wg *sync.WaitGroup,
|
||
|
logger *eventLogger,
|
||
|
state checkpoint.EventLogState,
|
||
|
) {
|
||
|
defer wg.Done()
|
||
|
logger.run(eb.done, eb.pipeline, state)
|
||
|
}
|