Skip to content

Commit 0fc6a68

Browse files
authored
Add windows events log filtering to CWAgent configuration wizard (#1774)
1 parent 0ed03d7 commit 0fc6a68

File tree

11 files changed

+106
-27
lines changed

11 files changed

+106
-27
lines changed

cmd/config-translator/translator_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ func TestLogWindowsEventConfig(t *testing.T) {
123123
expectedErrorMap6["invalid_type"] = 1
124124
expectedErrorMap6["enum"] = 1
125125
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidFilterType.json", false, expectedErrorMap6)
126-
127126
}
128127

129128
func TestMetricsConfig(t *testing.T) {

plugins/inputs/windows_event_log/wineventlog/wineventlog.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ func (w *windowsEventLog) Init() error {
102102
minEventID = 0
103103
maxEventID = 65535
104104
)
105+
105106
for _, eventID := range w.eventIDs {
106107
if eventID < minEventID || eventID > maxEventID {
107108
return fmt.Errorf("invalid event ID: %d, event IDs must be between %d and %d", eventID, minEventID, maxEventID)
108109
}
109110
}
111+
110112
for _, filter := range w.filters {
111113
if err := filter.init(); err != nil {
112114
return err

tool/data/config/logs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ func (config *Logs) AddLogFile(filePath, logGroupName string, logStream, timesta
4141
config.LogsCollect.AddLogFile(filePath, logGroupName, logStream, timestampFormat, timezone, multiLineStartPattern, encoding, retention, logGroupClass)
4242
}
4343

44-
func (config *Logs) AddWindowsEvent(eventName, logGroupName, logStream, eventFormat string, eventLevels []string, retention int, logGroupClass string) {
44+
func (config *Logs) AddWindowsEvent(eventName, logGroupName, logStream, eventFormat string, eventLevels []string, eventIDs []int, filters []*logs.EventFilter, retention int, logGroupClass string) {
4545
if config.LogsCollect == nil {
4646
config.LogsCollect = &logs.Collection{}
4747
}
48-
config.LogsCollect.AddWindowsEvent(eventName, logGroupName, logStream, eventFormat, eventLevels, retention, logGroupClass)
48+
config.LogsCollect.AddWindowsEvent(eventName, logGroupName, logStream, eventFormat, eventLevels, eventIDs, filters, retention, logGroupClass)
4949
}

tool/data/config/logs/collection.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ func (config *Collection) ToMap(ctx *runtime.Context) (string, map[string]interf
3030
return "logs_collected", resultMap
3131
}
3232

33-
func (config *Collection) AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat string, eventLevels []string, retention int, logGroupClass string) {
33+
func (config *Collection) AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat string, eventLevels []string, eventIDs []int, filters []*EventFilter, retention int, logGroupClass string) {
3434
if config.WinEvents == nil {
3535
config.WinEvents = &Events{}
3636
}
37-
config.WinEvents.AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat, eventLevels, retention, logGroupClass)
37+
config.WinEvents.AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat, eventLevels, eventIDs, filters, retention, logGroupClass)
3838
}
3939

4040
func (config *Collection) AddLogFile(filePath, logGroupName, logStreamName string, timestampFormat, timezone, multiLineStartPattern, encoding string, retention int, logGroupClass string) {

tool/data/config/logs/eventConfig.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,41 @@ package logs
55

66
import "github.com/aws/amazon-cloudwatch-agent/tool/runtime"
77

8+
type EventFilter struct {
9+
Type string `json:"type"`
10+
Expression string `json:"expression"`
11+
}
812
type EventConfig struct {
9-
EventName string `event_name`
10-
EventLevels []string `event_levels`
11-
EventFormat string `event_format`
12-
LogGroup string `log_group_name`
13-
LogStream string `log_stream_name`
14-
LogGroupClass string `log_group_class`
15-
Retention int `retention_in_days`
13+
EventName string `json:"event_name"`
14+
EventLevels []string `json:"event_levels"`
15+
EventIDs []int `json:"event_ids"`
16+
Filters []*EventFilter `json:"filters"`
17+
EventFormat string `json:"event_format"`
18+
LogGroup string `json:"log_group_name"`
19+
LogStream string `json:"log_stream_name"`
20+
LogGroupClass string `json:"log_group_class"`
21+
Retention int `json:"retention_in_days"`
1622
}
1723

1824
func (config *EventConfig) ToMap(ctx *runtime.Context) (string, map[string]interface{}) {
1925
resultMap := make(map[string]interface{})
2026
resultMap["event_name"] = config.EventName
21-
if config.EventLevels != nil && len(config.EventLevels) > 0 {
27+
if len(config.EventLevels) > 0 {
2228
resultMap["event_levels"] = config.EventLevels
2329
}
30+
if len(config.EventIDs) > 0 {
31+
resultMap["event_ids"] = config.EventIDs
32+
}
33+
if len(config.Filters) > 0 {
34+
filters := make([]map[string]interface{}, len(config.Filters))
35+
for i, filter := range config.Filters {
36+
filters[i] = map[string]interface{}{
37+
"type": filter.Type,
38+
"expression": filter.Expression,
39+
}
40+
}
41+
resultMap["filters"] = filters
42+
}
2443
if config.EventFormat != "" {
2544
resultMap["event_format"] = config.EventFormat
2645
}

tool/data/config/logs/eventConfig_test.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,24 @@ func TestEventConfig_ToMap(t *testing.T) {
1919
LogStream: "SystemStream",
2020
LogGroupClass: util.InfrequentAccessLogGroupClass,
2121
EventLevels: []string{"INFORMATION", "WARNING", "ERROR", "SUCCESS"},
22-
Retention: 1,
22+
EventIDs: []int{1001, 1002, 4624, 4625},
23+
Filters: []*EventFilter{
24+
{Type: "include", Expression: "P(UT|OST)"},
25+
{Type: "exclude", Expression: ".*INFORMATION"},
26+
},
27+
Retention: 1,
2328
}
2429
ctx := &runtime.Context{}
2530
key, value := conf.ToMap(ctx)
2631
assert.Equal(t, "", key)
2732
assert.Equal(t, map[string]interface{}{
28-
"event_name": "System",
29-
"event_levels": []string{"INFORMATION", "WARNING", "ERROR", "SUCCESS"},
33+
"event_name": "System",
34+
"event_levels": []string{"INFORMATION", "WARNING", "ERROR", "SUCCESS"},
35+
"event_ids": []int{1001, 1002, 4624, 4625},
36+
"filters": []map[string]interface{}{
37+
{"type": "include", "expression": "P(UT|OST)"},
38+
{"type": "exclude", "expression": ".*INFORMATION"},
39+
},
3040
"log_group_name": "SystemGroup",
3141
"log_stream_name": "SystemStream",
3242
"log_group_class": util.InfrequentAccessLogGroupClass,

tool/data/config/logs/events.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (config *Events) ToMap(ctx *runtime.Context) (string, map[string]interface{
2121
return "windows_events", resultMap
2222
}
2323

24-
func (config *Events) AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat string, eventLevels []string, retention int, logGroupClass string) {
24+
func (config *Events) AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat string, eventLevels []string, eventIDs []int, filters []*EventFilter, retention int, logGroupClass string) {
2525
if config.EventConfigs == nil {
2626
config.EventConfigs = []*EventConfig{}
2727
}
@@ -32,6 +32,8 @@ func (config *Events) AddWindowsEvent(eventName, logGroupName, logStreamName, ev
3232
LogGroupClass: logGroupClass,
3333
EventFormat: eventFormat,
3434
EventLevels: eventLevels,
35+
EventIDs: eventIDs,
36+
Filters: filters,
3537
Retention: retention,
3638
}
3739
config.EventConfigs = append(config.EventConfigs, singleEvent)

tool/data/config/logs/events_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ import (
1414

1515
func TestEvents_ToMap(t *testing.T) {
1616
conf := new(Events)
17-
conf.AddWindowsEvent("EN1", "LG1", "LS1", "", []string{"ERROR", "SUCCESS"}, 1, util.InfrequentAccessLogGroupClass)
18-
conf.AddWindowsEvent("EN2", "LG2", "LS2", "xml", []string{"ERROR"}, 1, util.InfrequentAccessLogGroupClass)
17+
conf.AddWindowsEvent("EN1", "LG1", "LS1", "", []string{"ERROR", "SUCCESS"}, []int{7632, 9653}, []*EventFilter{{Type: "include", Expression: "P(UT|OST)"}}, 1, util.InfrequentAccessLogGroupClass)
18+
conf.AddWindowsEvent("EN2", "LG2", "LS2", "xml", []string{"ERROR"}, []int{5000, 4496}, []*EventFilter{{Type: "include", Expression: ".*Aunthentication|Database.*"}}, 1, util.InfrequentAccessLogGroupClass)
1919

2020
ctx := &runtime.Context{}
2121
actualkey, actualValue := conf.ToMap(ctx)
2222

2323
expectedKey := "windows_events"
2424
expectedVal := map[string]interface{}{
2525
"collect_list": []map[string]interface{}{
26-
{"event_name": "EN1", "event_levels": []string{"ERROR", "SUCCESS"}, "log_group_name": "LG1", "log_stream_name": "LS1", "retention_in_days": 1, "log_group_class": util.InfrequentAccessLogGroupClass},
27-
{"event_name": "EN2", "event_levels": []string{"ERROR"}, "log_group_name": "LG2", "log_stream_name": "LS2", "event_format": "xml", "retention_in_days": 1, "log_group_class": util.InfrequentAccessLogGroupClass},
26+
{"event_name": "EN1", "event_levels": []string{"ERROR", "SUCCESS"}, "event_ids": []int{7632, 9653}, "filters": []map[string]interface{}{{"type": "include", "expression": "P(UT|OST)"}}, "log_group_name": "LG1", "log_stream_name": "LS1", "retention_in_days": 1, "log_group_class": util.InfrequentAccessLogGroupClass},
27+
{"event_name": "EN2", "event_levels": []string{"ERROR"}, "event_ids": []int{5000, 4496}, "filters": []map[string]interface{}{{"type": "include", "expression": ".*Aunthentication|Database.*"}}, "log_group_name": "LG2", "log_stream_name": "LS2", "event_format": "xml", "retention_in_days": 1, "log_group_class": util.InfrequentAccessLogGroupClass},
2828
},
2929
}
3030
assert.Equal(t, expectedKey, actualkey)

tool/processors/question/events/events.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ package events
55

66
import (
77
"fmt"
8+
"regexp"
89
"strconv"
10+
"strings"
911

1012
"github.com/aws/amazon-cloudwatch-agent/tool/data"
13+
"github.com/aws/amazon-cloudwatch-agent/tool/data/config/logs"
1114
"github.com/aws/amazon-cloudwatch-agent/tool/processors"
1215
"github.com/aws/amazon-cloudwatch-agent/tool/processors/tracesconfig"
1316
"github.com/aws/amazon-cloudwatch-agent/tool/runtime"
@@ -29,6 +32,9 @@ const (
2932

3033
EventFormatXML = "xml"
3134
EventFormatPlainText = "text"
35+
36+
FilterTypeInclude = "include"
37+
FilterTypeExclude = "exclude"
3238
)
3339

3440
var Processor processors.Processor = &processor{}
@@ -67,6 +73,49 @@ func monitorEvents(ctx *runtime.Context, config *data.Config) {
6773
}
6874
}
6975

76+
var eventIDs []int
77+
if util.Yes("Do you want to filter by specific Event IDs?") {
78+
eventIDsInput := util.Ask("Enter Event IDs (comma-separated, e.g., 1001,1002,1003):")
79+
if eventIDsInput != "" {
80+
eventIDStrings := strings.Split(eventIDsInput, ",")
81+
for _, idStr := range eventIDStrings {
82+
idStr = strings.TrimSpace(idStr)
83+
if id, err := strconv.Atoi(idStr); err == nil && id >= 0 && id <= 65535 {
84+
eventIDs = append(eventIDs, id)
85+
} else {
86+
fmt.Printf("Warning: Invalid Event ID '%s' ignored\n", idStr)
87+
}
88+
}
89+
}
90+
}
91+
var filters []*logs.EventFilter
92+
if util.Yes("Do you want to add regex filters to include/exclude specific events?") {
93+
for {
94+
filterType := util.Choice("Filter type:", 1, []string{"Include (events matching regex)", "Exclude (events matching regex)"})
95+
var filterTypeStr string
96+
if filterType == "Include (events matching regex)" {
97+
filterTypeStr = FilterTypeInclude
98+
} else {
99+
filterTypeStr = FilterTypeExclude
100+
}
101+
regexPattern := util.Ask("Enter regex pattern:")
102+
if regexPattern != "" {
103+
if _, err := regexp.Compile(regexPattern); err != nil {
104+
fmt.Printf("Error: Invalid regex pattern '%s': %v\n", regexPattern, err)
105+
continue
106+
}
107+
filter := &logs.EventFilter{
108+
Type: filterTypeStr,
109+
Expression: regexPattern,
110+
}
111+
filters = append(filters, filter)
112+
}
113+
if !util.Yes("Do you want to add another regex filter?") {
114+
break
115+
}
116+
}
117+
}
118+
70119
logGroupName := util.AskWithDefault("Log group name:", eventName)
71120

72121
logStreamNameHint := "{instance_id}"
@@ -102,7 +151,7 @@ func monitorEvents(ctx *runtime.Context, config *data.Config) {
102151
if err == nil {
103152
retention = i
104153
}
105-
logsConf.AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat, eventLevels, retention, logGroupClass)
154+
logsConf.AddWindowsEvent(eventName, logGroupName, logStreamName, eventFormat, eventLevels, eventIDs, filters, retention, logGroupClass)
106155

107156
yes = util.Yes(fmt.Sprintf("Do you want to specify any additional %s to monitor?", WindowsEventLog))
108157
if !yes {

tool/processors/question/events/events_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ func TestProcessor_Process(t *testing.T) {
2222
ctx.OsParameter = util.OsTypeWindows
2323
conf := new(data.Config)
2424

25-
testutil.Type(inputChan, "", "", "", "", "", "", "", "", "", "", "", "", "2")
25+
testutil.Type(inputChan, "", "", "", "", "", "", "", "", "1001,1002", "", "", ".*error.*", "2", "", "", "", "", "", "2")
2626
Processor.Process(ctx, conf)
2727
_, confMap := conf.ToMap(ctx)
2828
assert.Equal(t, map[string]interface{}{
2929
"logs": map[string]interface{}{
3030
"logs_collected": map[string]interface{}{
3131
"windows_events": map[string]interface{}{
3232
"collect_list": []map[string]interface{}{
33-
{"event_name": "System", "event_format": "xml", "event_levels": []string{VERBOSE, INFORMATION, WARNING, ERROR, CRITICAL}, "log_group_name": "System", "log_group_class": util.StandardLogGroupClass, "log_stream_name": "{instance_id}", "retention_in_days": -1}}}}}},
33+
{"event_name": "System", "event_format": "xml", "event_levels": []string{VERBOSE, INFORMATION, WARNING, ERROR, CRITICAL}, "event_ids": []int{1001, 1002}, "filters": []map[string]interface{}{{"type": "include", "expression": ".*error.*"}}, "log_group_name": "System", "log_group_class": util.StandardLogGroupClass, "log_stream_name": "{instance_id}", "retention_in_days": -1}}}}}},
3434
confMap)
3535
}
3636

0 commit comments

Comments
 (0)