feat(inputs.win_eventlog): Add state-persistence capabilities (#12790)

This commit is contained in:
Sven Rebhan 2023-03-07 19:53:25 +01:00 committed by GitHub
parent 7b2d48e041
commit 0e1b637414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 56 deletions

View File

@ -65,6 +65,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
</QueryList>
'''
## When true, event logs are read from the beginning; otherwise only future events
## will be logged.
# from_beginning = false
## System field names:
## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated",
## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName",

View File

@ -40,6 +40,10 @@
</QueryList>
'''
## When true, event logs are read from the beginning; otherwise only future events
## will be logged.
# from_beginning = false
## System field names:
## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated",
## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName",

View File

@ -23,7 +23,9 @@ type EvtSubscribeFlag uint32
// EVT_SUBSCRIBE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx
const (
EvtSubscribeToFutureEvents EvtSubscribeFlag = 1
EvtSubscribeToFutureEvents EvtSubscribeFlag = 1
EvtSubscribeStartAtOldestRecord EvtSubscribeFlag = 2
EvtSubscribeStartAfterBookmark EvtSubscribeFlag = 3
)
// EvtRenderFlag uint32
@ -32,9 +34,9 @@ type EvtRenderFlag uint32
// EVT_RENDER_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx
const (
//revive:disable:var-naming
// Render the event as an XML string. For details on the contents of the
// XML string, see the Event schema.
EvtRenderEventXml EvtRenderFlag = 1
//revive:enable:var-naming
// Render bookmark
EvtRenderBookmark EvtRenderFlag = 2
)

View File

@ -29,52 +29,102 @@ var sampleConfig string
// WinEventLog config
type WinEventLog struct {
Locale uint32 `toml:"locale"`
EventlogName string `toml:"eventlog_name"`
Query string `toml:"xpath_query"`
ProcessUserData bool `toml:"process_userdata"`
ProcessEventData bool `toml:"process_eventdata"`
Separator string `toml:"separator"`
OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"`
TimeStampFromEvent bool `toml:"timestamp_from_event"`
EventTags []string `toml:"event_tags"`
EventFields []string `toml:"event_fields"`
ExcludeFields []string `toml:"exclude_fields"`
ExcludeEmpty []string `toml:"exclude_empty"`
subscription EvtHandle
buf []byte
Log telegraf.Logger
Locale uint32 `toml:"locale"`
EventlogName string `toml:"eventlog_name"`
Query string `toml:"xpath_query"`
FromBeginning bool `toml:"from_beginning"`
ProcessUserData bool `toml:"process_userdata"`
ProcessEventData bool `toml:"process_eventdata"`
Separator string `toml:"separator"`
OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"`
TimeStampFromEvent bool `toml:"timestamp_from_event"`
EventTags []string `toml:"event_tags"`
EventFields []string `toml:"event_fields"`
ExcludeFields []string `toml:"exclude_fields"`
ExcludeEmpty []string `toml:"exclude_empty"`
Log telegraf.Logger `toml:"-"`
subscription EvtHandle
subscriptionFlag EvtSubscribeFlag
bookmark EvtHandle
}
var bufferSize = 1 << 14
const bufferSize = 1 << 14
func (*WinEventLog) SampleConfig() string {
return sampleConfig
}
// Gather Windows Event Log entries
func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
var err error
if w.subscription == 0 {
w.subscription, err = w.evtSubscribe(w.EventlogName, w.Query)
if err != nil {
return fmt.Errorf("subscription of Windows Event Log failed: %w", err)
}
func (w *WinEventLog) Init() error {
w.subscriptionFlag = EvtSubscribeToFutureEvents
if w.FromBeginning {
w.subscriptionFlag = EvtSubscribeStartAtOldestRecord
}
bookmark, err := _EvtCreateBookmark(nil)
if err != nil {
return err
}
w.bookmark = bookmark
return nil
}
func (w *WinEventLog) Start(_ telegraf.Accumulator) error {
subscription, err := w.evtSubscribe()
if err != nil {
return fmt.Errorf("subscription of Windows Event Log failed: %w", err)
}
w.subscription = subscription
w.Log.Debug("Subscription handle id:", w.subscription)
loop:
return nil
}
func (w *WinEventLog) Stop() {
_ = _EvtClose(w.subscription)
}
func (w *WinEventLog) GetState() interface{} {
bookmarkXML, err := w.renderBookmark(w.bookmark)
if err != nil {
w.Log.Errorf("State-persistence failed, cannot render bookmark: %w", err)
return ""
}
return bookmarkXML
}
func (w *WinEventLog) SetState(state interface{}) error {
bookmarkXML, ok := state.(string)
if !ok {
return fmt.Errorf("invalid type %T for state", state)
}
ptr, err := syscall.UTF16PtrFromString(bookmarkXML)
if err != nil {
return fmt.Errorf("convertion to pointer failed: %w", err)
}
bookmark, err := _EvtCreateBookmark(ptr)
if err != nil {
return fmt.Errorf("creating bookmark failed: %w", err)
}
w.bookmark = bookmark
w.subscriptionFlag = EvtSubscribeStartAfterBookmark
return nil
}
// Gather Windows Event Log entries
func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
for {
events, err := w.fetchEvents(w.subscription)
if err != nil {
switch {
case err == ERROR_NO_MORE_ITEMS:
break loop
case err != nil:
w.Log.Errorf("Error getting events: %v", err)
return err
if err == ERROR_NO_MORE_ITEMS {
break
}
w.Log.Errorf("Error getting events: %v", err)
return err
}
for _, event := range events {
@ -244,27 +294,28 @@ func (w *WinEventLog) shouldExcludeEmptyField(field string, fieldType string, fi
return false
}
func (w *WinEventLog) evtSubscribe(logName, xquery string) (EvtHandle, error) {
var logNamePtr, xqueryPtr *uint16
func (w *WinEventLog) evtSubscribe() (EvtHandle, error) {
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, err
}
defer windows.CloseHandle(sigEvent)
logNamePtr, err = syscall.UTF16PtrFromString(logName)
logNamePtr, err := syscall.UTF16PtrFromString(w.EventlogName)
if err != nil {
return 0, err
}
xqueryPtr, err = syscall.UTF16PtrFromString(xquery)
xqueryPtr, err := syscall.UTF16PtrFromString(w.Query)
if err != nil {
return 0, err
}
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr,
0, 0, 0, EvtSubscribeToFutureEvents)
var bookmark EvtHandle
if w.subscriptionFlag == EvtSubscribeStartAfterBookmark {
bookmark = w.bookmark
}
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, bookmark, 0, 0, w.subscriptionFlag)
if err != nil {
return 0, err
}
@ -299,34 +350,55 @@ func (w *WinEventLog) fetchEvents(subsHandle EvtHandle) ([]Event, error) {
return nil, err
}
var evterr error
for _, eventHandle := range eventHandles {
if eventHandle != 0 {
event, err := w.renderEvent(eventHandle)
if err == nil {
events = append(events, event)
}
if eventHandle == 0 {
continue
}
if event, err := w.renderEvent(eventHandle); err == nil {
events = append(events, event)
}
if err := _EvtUpdateBookmark(w.bookmark, eventHandle); err != nil && evterr == nil {
evterr = err
}
if err := _EvtClose(eventHandle); err != nil && evterr == nil {
evterr = err
}
}
return events, evterr
}
func (w *WinEventLog) renderBookmark(bookmark EvtHandle) (string, error) {
var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize)
err := _EvtRender(0, bookmark, EvtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil {
return "", err
}
for i := 0; i < len(eventHandles); i++ {
err := _EvtClose(eventHandles[i])
if err != nil {
return events, err
}
x, err := DecodeUTF16(buf[:bufferUsed])
if err != nil {
return "", err
}
return events, nil
if x[len(x)-1] == 0 {
x = x[:len(x)-1]
}
return string(x), err
}
func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) {
var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize)
event := Event{}
err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(w.buf)), &w.buf[0], &bufferUsed, &propertyCount)
err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil {
return event, err
}
eventXML, err := DecodeUTF16(w.buf[:bufferUsed])
eventXML, err := DecodeUTF16(buf[:bufferUsed])
if err != nil {
return event, err
}
@ -483,7 +555,6 @@ func openPublisherMetadata(
func init() {
inputs.Add("win_eventlog", func() telegraf.Input {
return &WinEventLog{
buf: make([]byte, bufferSize),
ProcessUserData: true,
ProcessEventData: true,
Separator: "_",

View File

@ -75,6 +75,8 @@ var (
procEvtNext = modwevtapi.NewProc("EvtNext")
procEvtFormatMessage = modwevtapi.NewProc("EvtFormatMessage")
procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata")
procEvtCreateBookmark = modwevtapi.NewProc("EvtCreateBookmark")
procEvtUpdateBookmark = modwevtapi.NewProc("EvtUpdateBookmark")
)
func _EvtSubscribe(
@ -231,3 +233,26 @@ func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity *uint16, log
}
return
}
func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) {
r0, _, e1 := syscall.Syscall(procEvtCreateBookmark.Addr(), 1, uintptr(unsafe.Pointer(bookmarkXML)), 0, 0)
handle := EvtHandle(r0)
if handle != 0 {
return handle, nil
}
if e1 != 0 {
return handle, errnoErr(e1)
}
return handle, syscall.EINVAL
}
func _EvtUpdateBookmark(bookmark, event EvtHandle) error {
r0, _, e1 := syscall.Syscall(procEvtUpdateBookmark.Addr(), 2, uintptr(bookmark), uintptr(event), 0)
if r0 != 0 {
return nil
}
if e1 != 0 {
return errnoErr(e1)
}
return syscall.EINVAL
}