This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
NXLog
This info on NXLog is circa 2014 - use with caution.
NXLog is best used when Windows Event Forwarding can’t be and filebeats isn’t sufficient.
Background
There are several solutions for capturing logs in Windows, but NXLog has some advantages;
- Cross-platform and Open Source
- Captures windows events pre-parsed
- Native windows installer and service
You could just run logstash everywhere. But in practice, Logstash’s memory requirements are several times NXLog and not everyone likes to install java everywhere.
Deploy on Windows
Download from http://nxlog.org/download. This will take you to the sourceforge site and the MSI you can install from. This installation is clean and the service installs automatically.
NXLog uses a config file with blocks in the basic pattern of:
- Input Block
- Output Block
- Routing Block
The latter being what ties together your inputs and outputs. You start out with one variable, called the $raw_event with everything in it. As you call modules, that variable gets parsed out to more useful individual variables.
Event Viewer Example
Here’s an example of invoking the module that pulls in data from the windows event log entries associated.
- Navigate to C:\Program Files (x86)\nxlog\conf
- Edit the security settings on the file nxlog.conf. Change the ‘Users’ to have modify rights. This allows you to actually edit the config file.
- Open that file in notepad and simply change it to look like so
# Set the ROOT to the folder your nxlog was installed into
define ROOT C:\Program Files (x86)\nxlog
## Default required locations based on the above
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log
# Increase to DEBUG if needed for diagnosis
LogLevel INFO
# Input the windows event logs
<Input in>
Module im_msvistalog
</Input>
# Output the logs to a file for testing
<Output out>
Module om_file
File "C:/Program Files (x86)/nxlog/data/log-test-output.txt"
</Output>
# Define the route by mapping the input to an output
<Route 1>
Path in => out
</Route>
With any luck, you’ve now got some lines in your output file.
# Set the ROOT to the folder your nxlog was installed into
define ROOT C:\Program Files (x86)\nxlog
## Default required locations based on the above
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log
# Increase to DEBUG if needed for diagnosis
LogLevel INFO
# Input a test file
<Input in>
Module im_file
File ""C:/Program Files (x86)/nxlog/data/test-in.txt"
SavePos FALSE
ReadFromLast FALSE
</Input>
# Output the logs to a file for testing
<Output out>
Module om_file
File "C:/Program Files (x86)/nxlog/data/log-test-output.txt"
</Output>
# Define the route by mapping the input to an output
<Route 1>
Path in => out
</Route>
Sending Events to a Remote Logstash Receiver
To be useful, you need to send your logs somewhere. Here’s an example of sending them to a Logstash receiver.
# Set the ROOT to the folder your nxlog was installed into
define ROOT C:\Program Files (x86)\nxlog
## Default required locations based on the above
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log
# Increase to DEBUG if needed for diagnosis
LogLevel INFO
# Load the JSON module needed by the output module
<Extension json>
Module xm_json
</Extension>
# Input the windows event logs
<Input in>
Module im_msvistalog
</Input>
# Output the logs out using the TCP module, convert to JSON format (important)
<Output out>
Module om_tcp
Host some.server
Port 6379
Exec to_json();
</Output>
# Define the route by mapping the input to an output
<Route 1>
Path in => out
</Route>
Restart the service in the windows services, and you are in business.
Note about JSON
You’re probably shipping logs to a logstash broker (or similar json based tcp receiver). In that case, make sure to specify JSON on the way out, as in the example above or you’ll spend hours trying to figure out why you’re getting a glob of plain txt and loose all the pre-parsed windows event messages which are nearly impossible to parse back from plain text.
Using that to_json() will replace the contents. The variable we mentioned earlier, $raw_event, with all of the already parsed fields. If you hand’t invoked a module to parse that data out, you’d just get a bunch of empty events as the data was replaced with a bunch of nothing.
1 - Drop Events
Exec
You can use the ‘Exec’ statement in any block and some pattern matching to drop events you don’t care about.
<Input in>
Module im_file
File "E:/Imports/get_accessplans/log-test.txt"
Exec if $raw_event =~ /someThing/ drop();
</Input>
Or the inverse, with the operator !~
Dropping Events with pm_pattern
The alternative is the patternDB approach as it has some parallelization advantages you’ll read about in the docs should you dig into it further. This matters when you have lots of patterns to check against.
# Set the ROOT to the folder your nxlog was installed into
define ROOT C:\Program Files (x86)\nxlog
## Default required locations based on the above
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log
# Increase to DEBUG if needed for diagnosis
LogLevel INFO
# Load the JSON module needed by the output module
<Extension json>
Module xm_json
</Extension>
# Input the windows event logs
<Input in>
Module im_msvistalog
</Input>
# Process log events
<Processor pattern>
Module pm_pattern
PatternFile %ROOT%/conf/patterndb.xml
</Processor>
# Output the logs out using the TCP module, convert to JSON format (important)
<Output out>
Module om_tcp
Host some.server
Port 6379
Exec to_json();
</Output>
# Define the route by mapping the input to an output
<Route 1>
Path in => pattern => out
</Route>
And create an XML file like so:
<?xml version="1.0" encoding="UTF-8"?>
<patterndb>
<group>
<name>eventlog</name>
<id>1</id>
<pattern>
<id>2</id>
<name>500s not needed</name>
<matchfield>
<name>EventID</name>
<type>exact</type>
<value>500</value>
</matchfield>
<exec>drop();</exec>
</pattern>
</group>
</patterndb>
2 - Event Log
Limiting Log Messages
You may not want ALL the event logs. You can add a query to that module however, and limit logs to the security logs, like so
<Input in>
Module im_msvistalog
Query <QueryList><Query Id="0" Path="Security"><Select Path="Security">*</Select></Query></QueryList>
</Input>
You can break that into multiple lines for easier reading by escaping the returns. Here’s an example that ships the ADFS Admin logs.
<Input in>
Module im_msvistalog
Query <QueryList>\
<Query Id="0">\
<Select Path='AD FS 2.0/Admin'>*</Select>\
</Query>\
</QueryList>
</Input>
Pulling out Custom Logs
If you’re interested in very specific logs, you can create a custom view in the windows event viewer, and after selecting the criteria in with the graphical tool, click on the XML tab to see what the query is. For example, to ship all the ADFS 2 logs (assuming you’ve turned on auditing) Take the output of the XML tab (shown below) and modify to be compliant with the nxlog format.
<QueryList>
<Query Id="0" Path="AD FS 2.0 Tracing/Debug">
<Select Path="AD FS 2.0 Tracing/Debug">*[System[Provider[@Name='AD FS 2.0' or @Name='AD FS 2.0 Auditing' or @Name='AD FS 2.0 Tracing']]]</Select>
<Select Path="AD FS 2.0/Admin">*[System[Provider[@Name='AD FS 2.0' or @Name='AD FS 2.0 Auditing' or @Name='AD FS 2.0 Tracing']]]</Select>
<Select Path="Security">*[System[Provider[@Name='AD FS 2.0' or @Name='AD FS 2.0 Auditing' or @Name='AD FS 2.0 Tracing']]]</Select>
</Query>
</QueryList
Here’s the query from a MS NPS
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider[@Name='NPS']]]</Select>
<Select Path="System">*[System[Provider[@Name='HRA']]]</Select>
<Select Path="System">*[System[Provider[@Name='Microsoft-Windows-HCAP']]]</Select>
<Select Path="System">*[System[Provider[@Name='RemoteAccess']]]</Select>
<Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 12552]]</Select>
</Query>
</QueryList>
3 - Input File Rotation
NXLog has decent ability to rotate it’s own output files, but it’s doesn’t come with a lot of methods to rotate input files - i.e. your reading in Accounting logs from a windows RADIUS and it would be nice to archive those with NXLog, because Windows won’t do it. You could bust out some perl (if you’re on unix) and use the xm_perl module, but there’s a simpler way.
On windows, the solution is to use an exec block with a scheduled command. The forfiles
executable is already present in windows and does the trick. The only gotcha is that ALL the parameters must be delimited like below.
So the command
forfiles /P "E:\IAS_Logs" /D -1 /C "cmd /c move @file \\server\share"
Becomes
<Extension exec>
Module xm_exec
<Schedule>
When @daily
Exec exec('C:\Windows\System32\forfiles.exe','/P','"E:\IAS_Logs"','/D','-1','/C','"cmd','/c','move','@file','\\server\share"');
</Schedule>
</Extension>
A slightly more complex example with added compression and removal of old files (there isn’t a great command line zip utility for windows in advance of powershell 5)
# Add log rotation for the windows input files
<Extension exec>
Module xm_exec
<Schedule>
When @daily
# Make a compressed copy of .log files older than 1 day
Exec exec('C:\Windows\System32\forfiles.exe','/P','"E:\IAS_Logs"','/M','*.log','/D','-1','/C','"cmd','/c','makecab','@file"')
# Delete original files after 2 days, leaving the compressed copies
Exec exec('C:\Windows\System32\forfiles.exe','/P','"E:\IAS_Logs"','/M','*.log','/D','-2','/C','"cmd','/c','del','@file"')
# Move compressed files to the depot after 2 days
Exec exec('C:\Windows\System32\forfiles.exe','/P','"E:\IAS_Logs"','/M','*.lo_','/D','-2','/C','"cmd','/c','move','@file','\\shared.ohio.edu\appshare\radius\logs\radius1.oit.ohio.edu"');
</Schedule>
</Extension>
The @daily runs right at 0 0 0 0 0 (midnight every night). Check the manual for more precise cron controls
4 - Inverse Matching
You can use the ‘Exec’ statement to match inverse like so
<Input in>
Module im_file
File "E:/Imports/get_accessplans/log-test.txt"
Exec if $raw_event !~ /someThing/ drop();
</Input>
However, when you’re using a pattern db this is harder as the REGEXP doesn’t seem to honor inverses like you’d expect. Instead, you must look for matches in your pattern db like normal;
<?xml version="1.0" encoding="UTF-8"?>
<patterndb>
<group>
<name>eventlog</name>
<id>1</id>
<pattern>
<id>2</id>
<name>Identify user login success usernames</name>
<matchfield>
<name>EventID</name>
<type>exact</type>
<value>501</value>
</matchfield>
<matchfield>
<name>Message</name>
<type>REGEXP</type>
<value>windowsaccountname \r\n(\S+)</value>
<capturedfield>
<name>ADFSLoginSuccessID</name>
<type>STRING</type>
</capturedfield>
</matchfield
</pattern>
</group>
</patterndb>
Then, add a section to your nxlog.conf to take action when the above capture field doesn’t existing (meaning there wasn’t a regexp match).
...
# Process log events
<Processor pattern>
Module pm_pattern
PatternFile %ROOT%/conf/patterndb.xml
</Processor>
# Using a null processor just to have a place to put the exec statement
<Processor filter>
Module pm_null
Exec if (($EventID == 501) and ($ADFSLoginSucccessID == undef)) drop();
</Processor>
# Output the logs out using the TCP module, convert to JSON format (important)
<Output out>
Module om_tcp
Host some.server
Port 6379
Exec to_json();
</Output>
# Define the route by mapping the input to an output
<Route 1>
Path in => pattern => filter => out
</Route>
5 - Logstash Broker
When using logstash as a Broker/Parser to receive events from nxlog, you’ll need to explicitly tell it that the message is in json format with a filter, like so:
input {
tcp {
port => 6379
type => "WindowsEventLog"
}
}
filter {
json {
source => message
}
}
output {
stdout { codec => rubydebug }
}
6 - Manipulating Data
Core Fields
NXLog makes and handful of attributes about the event available to you. Some of these are from the ‘core’ module
$raw_event
$EventReceivedTime
$SourceModuleName
$SourceModuleType
Additional Fields
These are always present and added to by the input module or processing module you use. For example, the mseventlog module adds all the attributes from the windows event logs as attributes to the nxlog event. So your event contains:
$raw_event
$EventReceivedTime
$SourceModuleName
$SourceModuleType
$Message
$EventTime
$Hostname
$SourceName
$EventID
...
You can also create new attributes by using a processing module, such as parsing an input file’s XML. This will translate all the tags (within limites) into attributes.
<Extension xml>
Module xm_xml
</Extension>
<Input IAS_Accounting_Logs>
Module im_file
File "E:\IAS_Logs\IN*.log"
Exec parse_xml();
</Input>
And you can also add an Exec at any point to create or replace new attribute as desired
<Input IAS_Accounting_Logs>
Module im_file
File "E:\IAS_Logs\IN*.log"
Exec $type = "RADIUSAccounting";
</Input>
Rewriting Data
Rather than manipulate everything in the input and output modules, use the pm_null module to group a block together.
<Processor rewrite>
Module pm_null
Exec parse_syslog_bsd();\
if $Message =~ /error/ \
{\
$SeverityValue = syslog_severity_value("error");\
to_syslog_bsd(); \
}
</Processor>
<Route 1>
Path in => rewrite => fileout
</Route>
7 - NPS Example
define ROOT C:\Program Files (x86)\nxlog
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log
# Load the modules needed by the outputs
<Extension json>
Module xm_json
</Extension>
<Extension xml>
Module xm_xml
</Extension>
# Inputs. Add the field '$type' so the receiver can easily tell what type they are.
<Input IAS_Event_Logs>
Module im_msvistalog
Query \
<QueryList>\
<Query Id="0" Path="System">\
<Select Path="System">*[System[Provider[@Name='NPS']]]</Select>\
<Select Path="System">*[System[Provider[@Name='HRA']]]</Select>\
<Select Path="System">*[System[Provider[@Name='Microsoft-Windows-HCAP']]]</Select>\
<Select Path="System">*[System[Provider[@Name='RemoteAccess']]]</Select>\
<Select Path="Security">*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task = 12552]]</Select>\
</Query>\
</QueryList>
Exec $type = "RADIUS";
</Input>
<Input IAS_Accounting_Logs>
Module im_file
File "E:\IAS_Logs\IN*.log"
Exec parse_xml();
Exec $type = "RADIUSAccounting";
</Input>
# Output the logs out using the TCP module, convert to JSON format (important)
<Output broker>
Module om_tcp
Host 192.168.1.1
Port 8899
Exec to_json();
</Output>
# Routes
<Route 1>
Path IAS_Event_Logs,IAS_Accounting_Logs => broker
</Route>
# Rotate the input logs while we're at it, so we don't need a separate tool
<Extension exec>
Module xm_exec
<Schedule>
When @daily
#Note - the Exec statement is one line but may appear wrapped
Exec exec('C:\Windows\System32\forfiles.exe','/P','"E:\IAS_Logs"','/D','-1','/C','"cmd','/c','move','@file','\\some.windows.server\share\logs\radius1"');
</Schedule>
</Extension>
8 - Parsing
You can also extract and set values with a pattern_db, like this;
(Note, nxlog uses perl pattern matching syntax if you need to look things up)
<?xml version="1.0" encoding="UTF-8"?>
<patterndb>
<group>
<name>ADFS Logs</name>
<id>1</id>
<pattern>
<id>2</id>
<name>Identify user login fails</name>
<matchfield>
<name>EventID</name>
<type>exact</type>
<value>111</value>
</matchfield>
<matchfield>
<name>Message</name>
<type>REGEXP</type>
<value>LogonUser failed for the '(\S+)'</value>
<capturedfield>
<name>ADFSLoginFailUsername</name>
<type>STRING</type>
</capturedfield>
</matchfield>
<set>
<field>
<name>ADFSLoginFail</name>
<value>failure</value>
<type>string</type>
</field>
</set>
</pattern>
And a more complex example, where we’re matching against a sting like:
2015-03-03T19:45:03 get_records 58 DailyAddAcct completed (Success) with: 15 Records Processed 0 adds 0 removes 0 modified 15 unchanged
<?xml version="1.0" encoding="UTF-8"?>
<patterndb>
<group>
<name>Bbts Logs</name>
<id>1</id>
<pattern>
<id>2</id>
<name>Get TS Records</name>
<matchfield>
<name>raw_event</name>
<type>REGEXP</type>
<value>^(\S+) get_record (\S+)\s+(\S+) completed \((\S+)\) with: (\S+) Records Processed (\S+) adds (\S+) removes (\S+) modified (\S+) unchanged</value>
<capturedfield>
<name>timestamp</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Transaction_ID</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Job_Subtype</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Job_Status</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Record_Total</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Record_Add</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Record_Remove</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Record_Mod</name>
<type>STRING</type>
</capturedfield>
<capturedfield>
<name>Record_NoChange</name>
<type>STRING</type>
</capturedfield>
</matchfield>
<set>
<field>
<name>Job_Type</name>
<value>Get_Records</value>
<type>string</type>
</field>
</set>
</pattern>
</group>
</patterndb>
9 - Reprocessing
Sometimes you have a parse error when you’re testing and you need to feed all your source files back in. Problem is you’re usually saving position and reading only new entries by default.
Defeat this by adding a line to the nxlog config so it starts reading files at the beginning and deleting the ConfigCache file (so there’s no last position to start from).
<Input IAS_Accounting_Logs>
Module im_file
ReadFromLast FALSE
File "E:\IAS_Logs\IN*.log"
Exec parse_xml();
Exec $type = "RADIUSAccounting";
</Input>
del C:\Program Files (x86)\nxlog\data\configcache.dat
Restart and it will begin reprocessing all the data. When you’re done, remove the ReadFromLast line and restart.
Note: If you had just deleted the cache file, nxlog would have resumed at the tail of the file. You could have told it not to save position, but you actually do want that for when you’re ready to resume normal operation.
https://www.mail-archive.com/[email protected]/msg00158.html
10 - Syslog
There are two components; adding the syslog module and adding the export path.
<Extension syslog>
Module xm_syslog
</Extension>
<Input IAS_Accounting_Logs>
Module im_file
File "E:\IAS_Logs\IN*.log"
Exec $type = "RADIUSAccounting";
</Input>
<Output siem>
Module om_udp
Host 192.168.1.1
Port 514
Exec to_syslog_ietf();
</Output>
<Route 1>
Path IAS_Accounting_Logs => siem
</Route>
11 - Troubleshooting
NXLOG Couldn’t read next event
If you see this error message from nxlog:
ERROR Couldn't read next event, corrupted eventlog?; The data is invalid.
Congrats - you’ve hit a bug.
https://nxlog.org/support-tickets/immsvistalog-maximum-event-log-count-support
The work-around is to limit your log event subscriptions on the input side by using a query. Example:
<Input in>
Module im_msvistalog
Query <QueryList><Query Id="0" Path="Microsoft-Windows-PrintService/Operational"><Select Path="Microsoft-Windows-PrintService/Operational">*</Select></Query></QueryList>
Exec if $EventID != 307 drop();
Exec $type = "IDWorks";
</Input>
Parse failure on windows to logstash
We found that nxlog made for the best windows log-shipper. But it didn’t seem to parse the events in the event log. Output to logstash seemed not to be in json format, and we confirmed this by writing directly to disk. This happens even though the event log input module explicitly emits the log attributes atomically.
Turns out you have to explicitly tell the output module to use json. This isn’t well documented.
12 - UNC Paths
When using Windows UNC paths, don’t forget that the backslash is also used for escaping characters, so the path
\\server\radius
looks like
\\server;adius
in your error log message. You’ll want to escape your back slashes like this;
\\\\server\\radius\\file.log
13 - Unicode Normalization
Files you’re reading may be any character set and this can cause strange things when you modify or pass the data on, as an example at stack exchange shows. This isn’t a problem with windows event logs, but windows applications use several different types of charsets.
Best practice is to convert everything to UTF-8. This is especially true when invoking modules such as json, that don’t handle other codes well.
NXLog has the ability to convert and can even to this automatically. However, there is some room for error. If you can, identity what the encoding is by looking at it in a hex editor and comparing to MS’s identification chart.
Here’s an snippet of a manual conversion of a powershell generated log. Having looked at the first part and identified it as UTF-16LE
...
<Extension charconv>
Module xm_charconv
AutodetectCharsets utf-8, utf-16, utf-32, iso8859-2, ucs-2le
</Extension>
<Input in1>
Module im_file
File "E:/Imports/log.txt"
Exec $raw_event = convert($raw_event,"UTF-16LE","UTF-8");
</Input>
...
Notice however that the charconv
module has an automatic directive. You can use that as long as what you have is included as marked in bold here.
<Extension charconv>
Module xm_charconv
AutodetectCharsets utf-8, utf-16, utf-16le, utf-32, iso8859-2
</Extension>
<Input sql-ERlogs>
Module im_file
File 'C:\Program Files\Microsoft SQL Server\MSSQL11.SQL\MSSQL\Log\ER*'
ReadFromLast TRUE
Exec convert_fields("AUTO", "utf-8");
</Input>
If you’re curious what charsets are supported, you can type this command in any unix system to see the names.
iconv -i
14 - Windows Files
Windows uses UTF-16 by default. Other services may use derivations thereof. In any event, it’s recommended to normalize things to UTF-8. Here’s a good example of what will happen if you don’t;
<http://stackoverflow.com/questions/27596676/nxlog-logs-are-in-unicode-charecters>
The answer to that question is to use the specific code field, as “AUTO” doesn’t seem to detect properly.
<Input in>
Module im_file
File "E:/Imports/get_accessplans/log-test.txt"
Exec if $raw_event == '' drop();
Exec $Event = convert($raw_event,"UCS-2LE","UTF-8"); to_json();
SavePos FALSE
ReadFromLast FALSE
</Input>
From the manual on SQL Server
Microsoft SQL Server
Microsoft SQL Server stores its logs in UTF-16 encoding using a line-based format.
It is recommended to normalize the encoding to UTF-8. The following config snipped
will do that.
<Extension _charconv>
Module xm_charconv
</Extension>
<Input in>
Module im_file
File "C:\\MSSQL\\ERRORLOG"
Exec convert_fields('UCS-2LE','UTF-8'); if $raw_event == '' drop();
</Input>
As of this writing, the LineBased parser, the default InputType for im_file
is not able to properly read the double-byte UTF-16 encoded files and will read
an additional empty line (because of the double-byte CRLF). The above drop() call is intended to fix this.
convert_fields('UTF-16','UTF-8');
might also work instead of UCS-2LE.