Skip to content

Commit

Permalink
[receiver/windowseventlog] Add suppress_rendering_info parameter and …
Browse files Browse the repository at this point in the history
…simplify internal logic. (#34720)

**Description:**

This PR contains several changes described in #34131. It does not go as
far as breaking out a separate parsing component, but I think it is
enough to satisfy the known use cases.

- Add `suppress_rendering_info` parameter, which acts orthogonally to
`raw` flag.
- Remove `RemoteServer` field from `EventXML`. Instead, set
`attributes["remote_server"]` if remote collection is used.

**Link to tracking Issue:**

Resolves
#34131
  • Loading branch information
djaglowski authored Sep 27, 2024
1 parent e7ebe8a commit da9d94c
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 311 deletions.
29 changes: 29 additions & 0 deletions .chloggen/wel-supress-rendering-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: 'enhancement'

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: windowseventlogreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add 'suppress_rendering_info' option.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34720]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
When this flag is enabled, the receiver will not attempt to resolve rendering info. This can improve performance
but comes at a cost of losing some details in the event log.
# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
33 changes: 33 additions & 0 deletions .chloggen/wel-supress-rendering-info2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: 'breaking'

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: windowseventlogreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: The 'raw' flag no longer suppresses rendering info.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34720]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
Previously, this flag controlled two behaviors simultaneously:
1. Whether or not the body of the log record was an XML string or structured object.
2. Whether or not rendering info was resolved.
A separate 'suppress_rendering_info' option now controls rendering info resolution.
This is considered a breaking change because users setting only the 'raw' flag without also setting the
new 'suppress_rendering_info' flag may see a performance decrease along with more detailed events.
# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
27 changes: 27 additions & 0 deletions .chloggen/wel-supress-rendering-info3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: 'enhancement'

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: windowseventlogreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Move artificial "remote_server" field to 'attributes["server.address"]'.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34720]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
2 changes: 2 additions & 0 deletions pkg/stanza/docs/operators/windows_eventlog_input.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The `windows_eventlog_input` operator reads logs from the windows event log API.
| `max_reads` | 100 | The maximum number of bodies read into memory, before beginning a new batch. |
| `start_at` | `end` | On first startup, where to start reading logs from the API. Options are `beginning` or `end`. |
| `poll_interval` | 1s | The interval at which the channel is checked for new log entries. This check begins again after all new bodies have been read. |
| `raw` | false | If false, the body of emitted log records will contain a structured representation of the event. Otherwise, the body will be the original XML string. |
| `suppress_rendering_info` | false | If false, [additional syscalls](https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtformatmessage#remarks) may be made to retrieve detailed information about the event. Otherwise, some unresolved values may be present in the event. |
| `attributes` | {} | A map of `key: value` pairs to add to the entry's attributes. |
| `resource` | {} | A map of `key: value` pairs to add to the entry's resource. |

Expand Down
17 changes: 9 additions & 8 deletions pkg/stanza/operator/input/windows/config_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ func NewConfigWithID(operatorID string) *Config {

// Config is the configuration of a windows event log operator.
type Config struct {
helper.InputConfig `mapstructure:",squash"`
Channel string `mapstructure:"channel"`
MaxReads int `mapstructure:"max_reads,omitempty"`
StartAt string `mapstructure:"start_at,omitempty"`
PollInterval time.Duration `mapstructure:"poll_interval,omitempty"`
Raw bool `mapstructure:"raw,omitempty"`
ExcludeProviders []string `mapstructure:"exclude_providers,omitempty"`
Remote RemoteConfig `mapstructure:"remote,omitempty"`
helper.InputConfig `mapstructure:",squash"`
Channel string `mapstructure:"channel"`
MaxReads int `mapstructure:"max_reads,omitempty"`
StartAt string `mapstructure:"start_at,omitempty"`
PollInterval time.Duration `mapstructure:"poll_interval,omitempty"`
Raw bool `mapstructure:"raw,omitempty"`
SuppressRenderingInfo bool `mapstructure:"suppress_rendering_info,omitempty"`
ExcludeProviders []string `mapstructure:"exclude_providers,omitempty"`
Remote RemoteConfig `mapstructure:"remote,omitempty"`
}

// RemoteConfig is the configuration for a remote server.
Expand Down
16 changes: 15 additions & 1 deletion pkg/stanza/operator/input/windows/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,24 @@ func (c *Config) Build(set component.TelemetrySettings) (operator.Operator, erro
startAt: c.StartAt,
pollInterval: c.PollInterval,
raw: c.Raw,
excludeProviders: c.ExcludeProviders,
excludeProviders: excludeProvidersSet(c.ExcludeProviders),
remote: c.Remote,
}
input.startRemoteSession = input.defaultStartRemoteSession

if c.SuppressRenderingInfo {
input.processEvent = input.processEventWithoutRenderingInfo
} else {
input.processEvent = input.processEventWithRenderingInfo
}

return input, nil
}

func excludeProvidersSet(providers []string) map[string]struct{} {
set := make(map[string]struct{}, len(providers))
for _, provider := range providers {
set[provider] = struct{}{}
}
return set
}
58 changes: 24 additions & 34 deletions pkg/stanza/operator/input/windows/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import (
"unsafe"
)

// errUnknownNextFrame is an error returned when a systemcall indicates the next frame is 0 bytes.
var errUnknownNextFrame = errors.New("the buffer size needed by the next frame of a render syscall was 0, unable to determine size of next frame")

// systemPropertiesRenderContext stores a custom rendering context to get only the event properties.
var systemPropertiesRenderContext = uintptr(0)
var systemPropertiesRenderContextErr error
Expand Down Expand Up @@ -72,54 +69,54 @@ func utf16PtrToString(s *uint16) string {
return string(utf16.Decode(slice))
}

// NewEvent will create a new event from an event handle.
func NewEvent(handle uintptr) Event {
return Event{
handle: handle,
}
}

// RenderSimple will render the event as EventXML without formatted info.
func (e *Event) RenderSimple(buffer Buffer) (EventXML, error) {
func (e *Event) RenderSimple(buffer Buffer) (*EventXML, error) {
if e.handle == 0 {
return EventXML{}, fmt.Errorf("event handle does not exist")
return nil, fmt.Errorf("event handle does not exist")
}

bufferUsed, err := evtRender(0, e.handle, EvtRenderEventXML, buffer.SizeBytes(), buffer.FirstByte())
if errors.Is(err, ErrorInsufficientBuffer) {
buffer.UpdateSizeBytes(*bufferUsed)
return e.RenderSimple(buffer)
}

if err != nil {
return EventXML{}, fmt.Errorf("syscall to 'EvtRender' failed: %w", err)
if errors.Is(err, ErrorInsufficientBuffer) {
buffer.UpdateSizeBytes(*bufferUsed)
return e.RenderSimple(buffer)
}
return nil, fmt.Errorf("syscall to 'EvtRender' failed: %w", err)
}

bytes, err := buffer.ReadBytes(*bufferUsed)
if err != nil {
return EventXML{}, fmt.Errorf("failed to read bytes from buffer: %w", err)
return nil, fmt.Errorf("failed to read bytes from buffer: %w", err)
}

return unmarshalEventXML(bytes)
}

// RenderFormatted will render the event as EventXML with formatted info.
func (e *Event) RenderFormatted(buffer Buffer, publisher Publisher) (EventXML, error) {
// RenderDeep will render the event as EventXML with all available formatted info.
func (e *Event) RenderDeep(buffer Buffer, publisher Publisher) (*EventXML, error) {
if e.handle == 0 {
return EventXML{}, fmt.Errorf("event handle does not exist")
return nil, fmt.Errorf("event handle does not exist")
}

bufferUsed, err := evtFormatMessage(publisher.handle, e.handle, 0, 0, 0, EvtFormatMessageXML, buffer.SizeWide(), buffer.FirstByte())
if errors.Is(err, ErrorInsufficientBuffer) {
// If the bufferUsed is 0 return an error as we don't want to make a recursive call with no buffer
if *bufferUsed == 0 {
return EventXML{}, errUnknownNextFrame
}

buffer.UpdateSizeWide(*bufferUsed)
return e.RenderFormatted(buffer, publisher)
}

if err != nil {
return EventXML{}, fmt.Errorf("syscall to 'EvtFormatMessage' failed: %w", err)
if errors.Is(err, ErrorInsufficientBuffer) {
buffer.UpdateSizeWide(*bufferUsed)
return e.RenderDeep(buffer, publisher)
}
return nil, fmt.Errorf("syscall to 'EvtFormatMessage' failed: %w", err)
}

bytes, err := buffer.ReadWideChars(*bufferUsed)
if err != nil {
return EventXML{}, fmt.Errorf("failed to read bytes from buffer: %w", err)
return nil, fmt.Errorf("failed to read bytes from buffer: %w", err)
}

return unmarshalEventXML(bytes)
Expand All @@ -138,10 +135,3 @@ func (e *Event) Close() error {
e.handle = 0
return nil
}

// NewEvent will create a new event from an event handle.
func NewEvent(handle uintptr) Event {
return Event{
handle: handle,
}
}
Loading

0 comments on commit da9d94c

Please sign in to comment.