github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/promtail/targets/windows/win_eventlog/win_eventlog.go (about)

     1  // The MIT License (MIT)
     2  
     3  // Copyright (c) 2015-2020 InfluxData Inc.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  //go:build windows
    23  // +build windows
    24  
    25  //revive:disable-next-line:var-naming
    26  // Package win_eventlog Input plugin to collect Windows Event Log messages
    27  package win_eventlog
    28  
    29  import (
    30  	"bytes"
    31  	"encoding/xml"
    32  	"path/filepath"
    33  	"strings"
    34  	"syscall"
    35  
    36  	"github.com/go-kit/log/level"
    37  	"github.com/influxdata/telegraf"
    38  	"github.com/influxdata/telegraf/plugins/inputs"
    39  	"golang.org/x/sys/windows"
    40  
    41  	util_log "github.com/grafana/loki/pkg/util/log"
    42  )
    43  
    44  var sampleConfig = `
    45    ## Telegraf should have Administrator permissions to subscribe for some Windows Events channels
    46    ## (System log, for example)
    47  
    48    ## LCID (Locale ID) for event rendering
    49    ## 1033 to force English language
    50    ## 0 to use default Windows locale
    51    # locale = 0
    52  
    53    ## Name of eventlog, used only if xpath_query is empty
    54    ## Example: "Application"
    55    # eventlog_name = ""
    56  
    57    ## xpath_query can be in defined short form like "Event/System[EventID=999]"
    58    ## or you can form a XML Query. Refer to the Consuming Events article:
    59    ## https://docs.microsoft.com/en-us/windows/win32/wes/consuming-events
    60    ## XML query is the recommended form, because it is most flexible
    61    ## You can create or debug XML Query by creating Custom View in Windows Event Viewer
    62    ## and then copying resulting XML here
    63    xpath_query = '''
    64    <QueryList>
    65      <Query Id="0" Path="Security">
    66        <Select Path="Security">*</Select>
    67        <Suppress Path="Security">*[System[( (EventID &gt;= 5152 and EventID &lt;= 5158) or EventID=5379 or EventID=4672)]]</Suppress>
    68      </Query>
    69      <Query Id="1" Path="Application">
    70        <Select Path="Application">*[System[(Level &lt; 4)]]</Select>
    71      </Query>
    72      <Query Id="2" Path="Windows PowerShell">
    73        <Select Path="Windows PowerShell">*[System[(Level &lt; 4)]]</Select>
    74      </Query>
    75      <Query Id="3" Path="System">
    76        <Select Path="System">*</Select>
    77      </Query>
    78      <Query Id="4" Path="Setup">
    79        <Select Path="Setup">*</Select>
    80      </Query>
    81    </QueryList>
    82    '''
    83  
    84    ## System field names:
    85    ##   "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated",
    86    ##   "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName",
    87    ##   "Channel", "Computer", "UserID", "UserName", "Message", "LevelText", "TaskText", "OpcodeText"
    88  
    89    ## In addition to System, Data fields can be unrolled from additional XML nodes in event.
    90    ## Human-readable representation of those nodes is formatted into event Message field,
    91    ## but XML is more machine-parsable
    92  
    93    # Process UserData XML to fields, if this node exists in Event XML
    94    process_userdata = true
    95  
    96    # Process EventData XML to fields, if this node exists in Event XML
    97    process_eventdata = true
    98  
    99    ## Separator character to use for unrolled XML Data field names
   100    separator = "_"
   101  
   102    ## Get only first line of Message field. For most events first line is usually more than enough
   103    only_first_line_of_message = true
   104  
   105    ## Parse timestamp from TimeCreated.SystemTime event field.
   106    ## Will default to current time of telegraf processing on parsing error or if set to false
   107    timestamp_from_event = true
   108  
   109    ## Fields to include as tags. Globbing supported ("Level*" for both "Level" and "LevelText")
   110    event_tags = ["Source", "EventID", "Level", "LevelText", "Task", "TaskText", "Opcode", "OpcodeText", "Keywords", "Channel", "Computer"]
   111  
   112    ## Default list of fields to send. All fields are sent by default. Globbing supported
   113    event_fields = ["*"]
   114  
   115    ## Fields to exclude. Also applied to data fields. Globbing supported
   116    exclude_fields = ["TimeCreated", "Binary", "Data_Address*"]
   117  
   118    ## Skip those tags or fields if their value is empty or equals to zero. Globbing supported
   119    exclude_empty = ["*ActivityID", "UserID"]
   120  `
   121  
   122  // WinEventLog config
   123  type WinEventLog struct {
   124  	Locale                 uint32   `yaml:"locale"`
   125  	EventlogName           string   `yaml:"eventlog_name"`
   126  	Query                  string   `yaml:"xpath_query"`
   127  	ProcessUserData        bool     `yaml:"process_userdata"`
   128  	ProcessEventData       bool     `yaml:"process_eventdata"`
   129  	Separator              string   `yaml:"separator"`
   130  	OnlyFirstLineOfMessage bool     `yaml:"only_first_line_of_message"`
   131  	TimeStampFromEvent     bool     `yaml:"timestamp_from_event"`
   132  	EventTags              []string `yaml:"event_tags"`
   133  	EventFields            []string `yaml:"event_fields"`
   134  	ExcludeFields          []string `yaml:"exclude_fields"`
   135  	ExcludeEmpty           []string `yaml:"exclude_empty"`
   136  
   137  	subscription EvtHandle
   138  	buf          []byte
   139  }
   140  
   141  var bufferSize = 1 << 14
   142  
   143  var description = "Input plugin to collect Windows Event Log messages"
   144  
   145  // Description for win_eventlog
   146  func (w *WinEventLog) Description() string {
   147  	return description
   148  }
   149  
   150  // SampleConfig for win_eventlog
   151  func (w *WinEventLog) SampleConfig() string {
   152  	return sampleConfig
   153  }
   154  
   155  // Gather Windows Event Log entries
   156  func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
   157  
   158  	// var err error
   159  	// if w.subscription == 0 {
   160  	// 	w.subscription, err = w.evtSubscribe(w.EventlogName, w.Query)
   161  	// 	if err != nil {
   162  	// 		return fmt.Errorf("Windows Event Log subscription error: %v", err.Error())
   163  	// 	}
   164  	// }
   165  
   166  	// loop:
   167  	// 	for {
   168  	// 		events, err := w.FetchEvents(w.subscription)
   169  	// 		if err != nil {
   170  	// 			switch {
   171  	// 			case err == ERROR_NO_MORE_ITEMS:
   172  	// 				break loop
   173  	// 			case err != nil:
   174  	// 				// w.Log.Error("Error getting events:", err.Error())
   175  	// 				return err
   176  	// 			}
   177  	// 		}
   178  
   179  	// 		for _, event := range events {
   180  	// 			// Prepare fields names usage counter
   181  	// 			var fieldsUsage = map[string]int{}
   182  
   183  	// 			tags := map[string]string{}
   184  	// 			fields := map[string]interface{}{}
   185  	// 			evt := reflect.ValueOf(&event).Elem()
   186  	// 			timeStamp := time.Now()
   187  	// 			// Walk through all fields of Event struct to process System tags or fields
   188  	// 			for i := 0; i < evt.NumField(); i++ {
   189  	// 				fieldName := evt.Type().Field(i).Name
   190  	// 				fieldType := evt.Field(i).Type().String()
   191  	// 				fieldValue := evt.Field(i).Interface()
   192  	// 				computedValues := map[string]interface{}{}
   193  	// 				switch fieldName {
   194  	// 				case "Source":
   195  	// 					fieldValue = event.Source.Name
   196  	// 					fieldType = reflect.TypeOf(fieldValue).String()
   197  	// 				case "Execution":
   198  	// 					fieldValue := event.Execution.ProcessID
   199  	// 					fieldType = reflect.TypeOf(fieldValue).String()
   200  	// 					fieldName = "ProcessID"
   201  	// 					// Look up Process Name from pid
   202  	// 					if should, _ := w.shouldProcessField("ProcessName"); should {
   203  	// 						_, _, processName, err := GetFromSnapProcess(fieldValue)
   204  	// 						if err == nil {
   205  	// 							computedValues["ProcessName"] = processName
   206  	// 						}
   207  	// 					}
   208  	// 				case "TimeCreated":
   209  	// 					fieldValue = event.TimeCreated.SystemTime
   210  	// 					fieldType = reflect.TypeOf(fieldValue).String()
   211  	// 					if w.TimeStampFromEvent {
   212  	// 						timeStamp, err = time.Parse(time.RFC3339Nano, fmt.Sprintf("%v", fieldValue))
   213  	// 						if err != nil {
   214  	// 							// w.Log.Warnf("Error parsing timestamp %q: %v", fieldValue, err)
   215  	// 						}
   216  	// 					}
   217  	// 				case "Correlation":
   218  	// 					if should, _ := w.shouldProcessField("ActivityID"); should {
   219  	// 						activityID := event.Correlation.ActivityID
   220  	// 						if len(activityID) > 0 {
   221  	// 							computedValues["ActivityID"] = activityID
   222  	// 						}
   223  	// 					}
   224  	// 					if should, _ := w.shouldProcessField("RelatedActivityID"); should {
   225  	// 						relatedActivityID := event.Correlation.RelatedActivityID
   226  	// 						if len(relatedActivityID) > 0 {
   227  	// 							computedValues["RelatedActivityID"] = relatedActivityID
   228  	// 						}
   229  	// 					}
   230  	// 				case "Security":
   231  	// 					computedValues["UserID"] = event.Security.UserID
   232  	// 					// Look up UserName and Domain from SID
   233  	// 					if should, _ := w.shouldProcessField("UserName"); should {
   234  	// 						sid := event.Security.UserID
   235  	// 						usid, err := syscall.StringToSid(sid)
   236  	// 						if err == nil {
   237  	// 							username, domain, _, err := usid.LookupAccount("")
   238  	// 							if err == nil {
   239  	// 								computedValues["UserName"] = fmt.Sprint(domain, "\\", username)
   240  	// 							}
   241  	// 						}
   242  	// 					}
   243  	// 				default:
   244  	// 				}
   245  	// 				if should, where := w.shouldProcessField(fieldName); should {
   246  	// 					if where == "tags" {
   247  	// 						strValue := fmt.Sprintf("%v", fieldValue)
   248  	// 						if !w.shouldExcludeEmptyField(fieldName, "string", strValue) {
   249  	// 							tags[fieldName] = strValue
   250  	// 							fieldsUsage[fieldName]++
   251  	// 						}
   252  	// 					} else if where == "fields" {
   253  	// 						if !w.shouldExcludeEmptyField(fieldName, fieldType, fieldValue) {
   254  	// 							fields[fieldName] = fieldValue
   255  	// 							fieldsUsage[fieldName]++
   256  	// 						}
   257  	// 					}
   258  	// 				}
   259  
   260  	// 				// Insert computed fields
   261  	// 				for computedKey, computedValue := range computedValues {
   262  	// 					if should, where := w.shouldProcessField(computedKey); should {
   263  	// 						if where == "tags" {
   264  	// 							tags[computedKey] = fmt.Sprintf("%v", computedValue)
   265  	// 							fieldsUsage[computedKey]++
   266  	// 						} else if where == "fields" {
   267  	// 							fields[computedKey] = computedValue
   268  	// 							fieldsUsage[computedKey]++
   269  	// 						}
   270  	// 					}
   271  	// 				}
   272  	// 			}
   273  
   274  	// 			// Unroll additional XML
   275  	// 			var xmlFields []EventField
   276  	// 			if w.ProcessUserData {
   277  	// 				fieldsUserData, xmlFieldsUsage := UnrollXMLFields(event.UserData.InnerXML, fieldsUsage, w.Separator)
   278  	// 				xmlFields = append(xmlFields, fieldsUserData...)
   279  	// 				fieldsUsage = xmlFieldsUsage
   280  	// 			}
   281  	// 			if w.ProcessEventData {
   282  	// 				fieldsEventData, xmlFieldsUsage := UnrollXMLFields(event.EventData.InnerXML, fieldsUsage, w.Separator)
   283  	// 				xmlFields = append(xmlFields, fieldsEventData...)
   284  	// 				fieldsUsage = xmlFieldsUsage
   285  	// 			}
   286  	// 			uniqueXMLFields := UniqueFieldNames(xmlFields, fieldsUsage, w.Separator)
   287  	// 			for _, xmlField := range uniqueXMLFields {
   288  	// 				if !w.shouldExclude(xmlField.Name) {
   289  	// 					fields[xmlField.Name] = xmlField.Value
   290  	// 				}
   291  	// 			}
   292  
   293  	// 			// Pass collected metrics
   294  	// 			acc.AddFields("win_eventlog", fields, tags, timeStamp)
   295  	// 		}
   296  	// 	}
   297  
   298  	return nil
   299  }
   300  
   301  func (w *WinEventLog) shouldExclude(field string) (should bool) {
   302  	for _, excludePattern := range w.ExcludeFields {
   303  		// Check if field name matches excluded list
   304  		if matched, _ := filepath.Match(excludePattern, field); matched {
   305  			return true
   306  		}
   307  	}
   308  	return false
   309  }
   310  
   311  func (w *WinEventLog) shouldProcessField(field string) (should bool, list string) {
   312  	for _, pattern := range w.EventTags {
   313  		if matched, _ := filepath.Match(pattern, field); matched {
   314  			// Tags are not excluded
   315  			return true, "tags"
   316  		}
   317  	}
   318  
   319  	for _, pattern := range w.EventFields {
   320  		if matched, _ := filepath.Match(pattern, field); matched {
   321  			if w.shouldExclude(field) {
   322  				return false, "excluded"
   323  			}
   324  			return true, "fields"
   325  		}
   326  	}
   327  	return false, "excluded"
   328  }
   329  
   330  func (w *WinEventLog) shouldExcludeEmptyField(field string, fieldType string, fieldValue interface{}) (should bool) {
   331  	for _, pattern := range w.ExcludeEmpty {
   332  		if matched, _ := filepath.Match(pattern, field); matched {
   333  			switch fieldType {
   334  			case "string":
   335  				return len(fieldValue.(string)) < 1
   336  			case "int":
   337  				return fieldValue.(int) == 0
   338  			case "uint32":
   339  				return fieldValue.(uint32) == 0
   340  			}
   341  		}
   342  	}
   343  	return false
   344  }
   345  
   346  func EvtSubscribe(logName, xquery string) (EvtHandle, error) {
   347  	var logNamePtr, xqueryPtr *uint16
   348  
   349  	sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
   350  	if err != nil {
   351  		return 0, err
   352  	}
   353  	defer windows.CloseHandle(sigEvent)
   354  
   355  	logNamePtr, err = syscall.UTF16PtrFromString(logName)
   356  	if err != nil {
   357  		return 0, err
   358  	}
   359  
   360  	xqueryPtr, err = syscall.UTF16PtrFromString(xquery)
   361  	if err != nil {
   362  		return 0, err
   363  	}
   364  
   365  	subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr,
   366  		0, 0, 0, EvtSubscribeToFutureEvents)
   367  	if err != nil {
   368  		return 0, err
   369  	}
   370  	level.Debug(util_log.Logger).Log("msg", "Subcribed with handle id", "id", subsHandle)
   371  
   372  	return subsHandle, nil
   373  }
   374  
   375  func EvtSubscribeWithBookmark(logName, xquery string, bookMark EvtHandle) (EvtHandle, error) {
   376  	var logNamePtr, xqueryPtr *uint16
   377  
   378  	sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
   379  	if err != nil {
   380  		return 0, err
   381  	}
   382  	defer windows.CloseHandle(sigEvent)
   383  
   384  	logNamePtr, err = syscall.UTF16PtrFromString(logName)
   385  	if err != nil {
   386  		return 0, err
   387  	}
   388  
   389  	xqueryPtr, err = syscall.UTF16PtrFromString(xquery)
   390  	if err != nil {
   391  		return 0, err
   392  	}
   393  
   394  	subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr,
   395  		bookMark, 0, 0, EvtSubscribeStartAfterBookmark)
   396  	if err != nil {
   397  		return 0, err
   398  	}
   399  	level.Debug(util_log.Logger).Log("msg", "Subcribed with handle id", "id", subsHandle)
   400  
   401  	return subsHandle, nil
   402  }
   403  
   404  func fetchEventHandles(subsHandle EvtHandle) ([]EvtHandle, error) {
   405  	var eventsNumber uint32
   406  	var evtReturned uint32
   407  
   408  	eventsNumber = 5
   409  
   410  	eventHandles := make([]EvtHandle, eventsNumber)
   411  
   412  	err := _EvtNext(subsHandle, eventsNumber, &eventHandles[0], 0, 0, &evtReturned)
   413  	if err != nil {
   414  		if err == ERROR_INVALID_OPERATION && evtReturned == 0 {
   415  			return nil, ERROR_NO_MORE_ITEMS
   416  		}
   417  		return nil, err
   418  	}
   419  
   420  	return eventHandles[:evtReturned], nil
   421  }
   422  
   423  type EventFetcher struct {
   424  	buf []byte
   425  }
   426  
   427  func NewEventFetcher() *EventFetcher {
   428  	return &EventFetcher{}
   429  }
   430  
   431  func (w *EventFetcher) FetchEvents(subsHandle EvtHandle, lang uint32) ([]Event, []EvtHandle, error) {
   432  	if w.buf == nil {
   433  		w.buf = make([]byte, bufferSize)
   434  	}
   435  	var events []Event
   436  
   437  	eventHandles, err := fetchEventHandles(subsHandle)
   438  	if err != nil {
   439  		return nil, nil, err
   440  	}
   441  
   442  	for _, eventHandle := range eventHandles {
   443  		if eventHandle != 0 {
   444  			event, err := w.renderEvent(eventHandle, lang)
   445  			if err == nil {
   446  				events = append(events, event)
   447  			}
   448  		}
   449  	}
   450  
   451  	return events, eventHandles, nil
   452  }
   453  
   454  func Close(handles []EvtHandle) error {
   455  	for i := 0; i < len(handles); i++ {
   456  		err := _EvtClose(handles[i])
   457  		if err != nil {
   458  			return err
   459  		}
   460  	}
   461  	return nil
   462  }
   463  
   464  func (w *EventFetcher) renderEvent(eventHandle EvtHandle, lang uint32) (Event, error) {
   465  	var bufferUsed, propertyCount uint32
   466  
   467  	event := Event{}
   468  	err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(w.buf)), &w.buf[0], &bufferUsed, &propertyCount)
   469  	if err != nil {
   470  		return event, err
   471  	}
   472  
   473  	eventXML, err := DecodeUTF16(w.buf[:bufferUsed])
   474  	if err != nil {
   475  		return event, err
   476  	}
   477  	err = xml.Unmarshal([]byte(eventXML), &event)
   478  	if err != nil {
   479  		// We can return event without most text values,
   480  		// that way we will not loose information
   481  		// This can happen when processing Forwarded Events
   482  		return event, nil
   483  	}
   484  
   485  	publisherHandle, err := openPublisherMetadata(0, event.Source.Name, lang)
   486  	if err != nil {
   487  		return event, nil
   488  	}
   489  	defer _EvtClose(publisherHandle)
   490  
   491  	// Populating text values
   492  	keywords, err := formatEventString(EvtFormatMessageKeyword, eventHandle, publisherHandle)
   493  	if err == nil {
   494  		event.Keywords = keywords
   495  	}
   496  	message, err := formatEventString(EvtFormatMessageEvent, eventHandle, publisherHandle)
   497  	if err == nil {
   498  		event.Message = message
   499  	}
   500  	level, err := formatEventString(EvtFormatMessageLevel, eventHandle, publisherHandle)
   501  	if err == nil {
   502  		event.LevelText = level
   503  	}
   504  	task, err := formatEventString(EvtFormatMessageTask, eventHandle, publisherHandle)
   505  	if err == nil {
   506  		event.TaskText = task
   507  	}
   508  	opcode, err := formatEventString(EvtFormatMessageOpcode, eventHandle, publisherHandle)
   509  	if err == nil {
   510  		event.OpcodeText = opcode
   511  	}
   512  	return event, nil
   513  }
   514  
   515  func formatEventString(
   516  	messageFlag EvtFormatMessageFlag,
   517  	eventHandle EvtHandle,
   518  	publisherHandle EvtHandle,
   519  ) (string, error) {
   520  	var bufferUsed uint32
   521  	err := _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
   522  		0, nil, &bufferUsed)
   523  	if err != nil && err != ERROR_INSUFFICIENT_BUFFER {
   524  		return "", err
   525  	}
   526  
   527  	bufferUsed *= 2
   528  	buffer := make([]byte, bufferUsed)
   529  	bufferUsed = 0
   530  
   531  	err = _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
   532  		uint32(len(buffer)/2), &buffer[0], &bufferUsed)
   533  	bufferUsed *= 2
   534  	if err != nil {
   535  		return "", err
   536  	}
   537  
   538  	result, err := DecodeUTF16(buffer[:bufferUsed])
   539  	if err != nil {
   540  		return "", err
   541  	}
   542  
   543  	var out string
   544  	if messageFlag == EvtFormatMessageKeyword {
   545  		// Keywords are returned as array of a zero-terminated strings
   546  		splitZero := func(c rune) bool { return c == '\x00' }
   547  		eventKeywords := strings.FieldsFunc(string(result), splitZero)
   548  		// So convert them to comma-separated string
   549  		out = strings.Join(eventKeywords, ",")
   550  	} else {
   551  		result := bytes.Trim(result, "\x00")
   552  		out = string(result)
   553  	}
   554  	return out, nil
   555  }
   556  
   557  // openPublisherMetadata opens a handle to the publisher's metadata. Close must
   558  // be called on returned EvtHandle when finished with the handle.
   559  func openPublisherMetadata(
   560  	session EvtHandle,
   561  	publisherName string,
   562  	lang uint32,
   563  ) (EvtHandle, error) {
   564  	p, err := syscall.UTF16PtrFromString(publisherName)
   565  	if err != nil {
   566  		return 0, err
   567  	}
   568  
   569  	h, err := _EvtOpenPublisherMetadata(session, p, nil, lang, 0)
   570  	if err != nil {
   571  		return 0, err
   572  	}
   573  
   574  	return h, nil
   575  }
   576  
   577  func init() {
   578  	inputs.Add("win_eventlog", func() telegraf.Input {
   579  		return &WinEventLog{
   580  			buf:                    make([]byte, bufferSize),
   581  			ProcessUserData:        true,
   582  			ProcessEventData:       true,
   583  			Separator:              "_",
   584  			OnlyFirstLineOfMessage: true,
   585  			TimeStampFromEvent:     true,
   586  			EventTags:              []string{"Source", "EventID", "Level", "LevelText", "Keywords", "Channel", "Computer"},
   587  			EventFields:            []string{"*"},
   588  			ExcludeEmpty:           []string{"Task", "Opcode", "*ActivityID", "UserID"},
   589  		}
   590  	})
   591  }