CheckLogFile¶
File for checking log files and various other forms of updating text files
Enable module¶
To enable this module and and allow using the commands you need to ass CheckLogFile = enabled to the [/modules] section in nsclient.ini:
[/modules]
CheckLogFile = enabled
Queries¶
A quick reference for all available queries (check commands) in the CheckLogFile module.
List of commands:
A list of all available queries (check commands)
| Command | Description |
|---|---|
| check_logfile | Check for errors in log file or generic pattern matching in text files. |
List of command aliases:
A list of all short hand aliases for queries (check commands)
| Command | Description |
|---|---|
| checklogfile | Alias for: :query:check_logfile |
check_logfile¶
Check for errors in log file or generic pattern matching in text files.
Only checking new lines (bookmark)¶
By default check_logfile reads the entire file on every run, so a single
ERROR line keeps failing the check for as long as it stays in the file. Add
bookmark to make the check incremental instead: NSClient++ remembers how far
it read last time and each run only looks at what was appended since.
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=app-errors
The name (app-errors above) identifies the stored position. Two checks that
use the same name over the same file share one position — the first one to run
consumes the lines. Use distinct names when two checks look for different
things in the same file, or let the name be derived automatically by passing
bookmark with no value (bookmark, bookmark= and bookmark=auto all mean
the same thing), which builds it from the file name plus a hash of the
filter, warning and critical expressions:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark
Positions are kept per file, so file= may be repeated as usual; each file is
tracked on its own. They are written to ${data-path}/nsclient.db when
NSClient++ shuts down and restored on start, so a restart does not re-report
everything.
Because an automatic name covers the expressions, editing the filter starts a new position (and the file is read in full once more) and leaves the old one behind in the store. Prefer an explicit name for checks whose filter changes often, or which are generated with varying arguments.
Behaviour worth knowing:
- The first check of a file reads it in full. Only from the second check on is the check incremental. This is deliberate — entries that were already in the file when monitoring started are reported rather than silently dropped.
- An unterminated last line is held back. If the file ends mid-line (the
writer has not written the
line-splitterminator yet) that fragment is not reported, and the position stays in front of it. The line is reported once, in full, by the check that sees its terminator. Without a bookmark the fragment is reported as-is, and again once it is complete. - Rotation and truncation are detected. If the file is shorter than the
stored position, or the first bytes of the file no longer match what was
there before (a rotated or re-created file,
copytruncate, a daily file under a fixed name), the file is read from the beginning again. Rotation to a different name is not followed: point the check at the name that keeps receiving new lines. - A failing check moves nothing. If any of the files cannot be read the check returns an error and every position stays where it was, so the lines which were read on the way are reported by the next successful check instead of vanishing with the error.
- A quiet check is OK, not UNKNOWN. When nothing new arrived the result is
the empty state (
%(status): Nothing found), which is OK by default; useempty-state=to change it. - Checks without
bookmarkare unaffected. They neither read nor advance any stored position, so an ad-hoc full scan can be run at any time without disturbing a bookmarked check.
Before you switch a check over to bookmark, know what you are trading a
re-reported line for:
- A line is consumed when the check runs, not when its result arrives. With a bookmark the position moves as soon as the lines have been read. If the result is submitted passively (NSCA, NRDP, …) and that submission fails, the lines it described are not reported again by the next check. An actively polled check is not exposed to this: the position moves as the poller receives the answer.
- Positions are saved when NSClient++ shuts down. A crash, a killed service or a power loss therefore rewinds every bookmark to where it was when the service last stopped cleanly, and the lines written since are reported again. They are never lost, only repeated.
- A last line without its terminator is never reported on its own. This is
the flip side of holding back half-written lines: a file which is written in
one go and does not end with
line-splitkeeps its final line unreported until something appends to the file. Without a bookmark that line is reported on every check as before. ${total}counts what the check looked at. With a bookmark that is the lines added since the previous run — not the number of lines in the file.${count}is, as always, how many of them matched the filter.- The number of remembered positions is capped (at 1000 file/bookmark
pairs, which no ordinary configuration comes close to). If a host generates
bookmark names — a script which puts a timestamp in the name, or automatic
names for a filter which keeps changing — the least recently used positions
are dropped, and the files they tracked are read in full the next time that
name shows up. A dropped position is also cleared from
nsclient.dbon the next shutdown, so the store does not grow forever.
Real-time monitoring (/settings/logfile/real-time/checks) is the other way to
get each line reported once; it pushes results as lines are written instead of
being polled. bookmark is the polled equivalent and needs no configuration on
the agent.
Only checking the newest lines (max-lines, newest)¶
max-lines=N limits the check to the newest N lines of each file. Everything
else follows from that: ${total} counts what was examined, not what the file
holds, and only the selected lines can match the filter.
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" max-lines=100
The limit is per file, so a check with several file= arguments takes the
newest N lines of each of them.
newest says which end of the file the newest line is at:
| Value | Meaning |
|---|---|
last |
Lines are appended (the default, and what almost every machine-written log does). max-lines takes them from the end of the file. |
first |
The file is rewritten with the newest line at the top, as hand-maintained changelogs often are. max-lines takes them from the start of the file. |
Whichever end they come from, the selected lines are reported in the order they
appear in the file, so %(list) reads the way the file does.
Behaviour worth knowing:
- Only the wanted part of the file is read. The check seeks to the newest
Nlines instead of loading the whole file, somax-linesis a cheap way to look at the tail of a very large log. (Aline-splitvalue which can overlap itself, such asaaaor--, cannot be located from the end; those files are read in full and the surplus lines are dropped afterwards. The result is the same either way.) max-linescombines withbookmark. The bookmark still decides which lines are new, and the limit then caps how many of them are reported — useful when an application can dump thousands of lines at once. The lines dropped by the limit are consumed: they are not reported by a later check either, since the stored position moves past everything that was read.newest=firstcannot be combined withbookmark. A file which is rewritten from the top has no stable position to resume from — its first bytes change on every write, so the bookmark would detect a “new” file and re-read it in full every single time. The check reports this as an error rather than doing it quietly.- An unterminated last line still counts as a line. Without a bookmark the
trailing fragment is one of the
N; with a bookmark it is held back as usual.
Jump to section:
Sample Commands¶
Find errors in a log file
Given /var/log/app.log:
app started
ERROR failed to connect to db
INFO retrying
ERROR failed to connect to db
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" "critical=count > 3"
2/4 (ERROR failed to connect to db, ERROR failed to connect to db)|'count'=2;0;3
${count} is the number of matching lines, ${total} the number of lines read.
Only report lines added since the last check (bookmark)
The first check reads the whole file:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=app-errors
2/4 (ERROR failed to connect to db, ERROR failed to connect to db)|'count'=2;0;0
Running it again with nothing appended reports nothing — the two errors are not raised over and over:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=app-errors
OK: Nothing found|'count'=0;0;0
After ERROR disk full is appended, only that line is considered:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=app-errors
1/1 (ERROR disk full)|'count'=1;0;0
Let the bookmark name itself be derived
bookmark without a value derives the name from the file and the filter /
warning / critical expressions, which keeps this check from consuming the lines
of the app-errors check above (it starts from the beginning, since this name
has not seen the file before):
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark
3/5 (ERROR failed to connect to db, ERROR failed to connect to db, ERROR disk full)|'count'=3;0;0
bookmark= (an empty value) and bookmark=auto are spelled differently by
different transports but mean exactly this.
Watch several files with one check
Each file keeps its own position; the counts are aggregated. A fresh bookmark name starts by reading both files in full:
check_logfile file=/var/log/app.log file=/var/log/worker.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=all-errors
4/7 (ERROR failed to connect to db, ERROR failed to connect to db, ERROR disk full, ERROR queue stalled)|'count'=4;0;0
Afterwards only the file that actually grew contributes:
check_logfile file=/var/log/app.log file=/var/log/worker.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=all-errors
1/1 (ERROR queue stalled again)|'count'=1;0;0
Only look at the newest lines (max-lines)
Given /var/log/app.log:
app started
ERROR failed to connect to db
INFO retrying
ERROR failed to connect to db
INFO recovered
ERROR disk full
Without a limit every line is read:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0"
3/6 (ERROR failed to connect to db, ERROR failed to connect to db, ERROR disk full)|'count'=3;0;0
With max-lines=3 only the last three lines are examined — ${total} drops to
3 and the older error is out of scope:
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" max-lines=3
2/3 (ERROR failed to connect to db, ERROR disk full)|'count'=2;0;0
Files whose newest line is at the top (newest=first)
Given a hand-maintained /var/log/deploy-changelog.txt where each new entry is
added at the top:
2018-07-20 deploy 42 FAILED rollback started
2018-07-19 deploy 41 ok
2018-07-18 deploy 40 ok
2018-07-17 deploy 39 FAILED disk full
newest=first makes max-lines count from the top of the file, so only the two
most recent deploys are checked:
check_logfile file=/var/log/deploy-changelog.txt "filter=column1 like 'FAILED'" "warning=count > 0" max-lines=2 newest=first
1/2 (2018-07-20 deploy 42 FAILED rollback started)|'count'=1;0;0
Without the limit newest=first changes nothing — the whole file is read either
way:
check_logfile file=/var/log/deploy-changelog.txt "filter=column1 like 'FAILED'" "warning=count > 0" newest=first
2/4 (2018-07-20 deploy 42 FAILED rollback started, 2018-07-17 deploy 39 FAILED disk full)|'count'=2;0;0
Report a quiet check as something other than OK
check_logfile file=/var/log/app.log "filter=column1 like 'ERROR'" "warning=count > 0" bookmark=app-errors empty-state=unknown
UNKNOWN: Nothing found|'count'=0;0;0
Command-line Arguments¶
| Option | Default Value | Description |
|---|---|---|
| line-split | \n | Character string used to split a file into several lines (default \n). |
| column-split | \t | Character string to split a line into several columns (default \t) |
| split | Alias for split-column | |
| file | File to read (can be specified multiple times to check multiple files. | |
| files | A comma separated list of files to scan (same as file except a list) | |
| bookmark | auto | Only scan lines added since the last check with the same bookmark name. |
| max-lines | 0 | Only examine the newest |
| newest | last | Which end of the file holds the newest line: last (the default: lines are appended, as with most machine-written logs) or first (the file is rewritten with the newest line at the top, which is common in hand-maintained files such as changelogs). |
line-split:
Character string used to split a file into several lines (default \n).
The escape sequences \n and \t are translated to LF and TAB respectively; all other characters are taken literally. Multi-character delimiters are supported (for example \r\n to split strictly on CRLF, or ||| for a custom separator). Setting line-split to an empty value (line-split=) makes the entire file content available as a single record, which is useful together with a multi-line regular-expression filter.\nWhen the chosen delimiter ends with , a trailing carriage return is stripped from each record so that files with CRLF line endings produce clean lines.
Default Value: \n
column-split:
Character string to split a line into several columns (default \t)
Default Value: \t
file:
File to read (can be specified multiple times to check multiple files. Notice that specifying multiple files will create an aggregate set it will not check each file individually. In other words if one file contains an error the entire check will result in error or if you check the count it is the global count which is used.
bookmark:
Only scan lines added since the last check with the same bookmark name. NSClient++ remembers, per file and per bookmark, how far it read last time and resumes from there, so a line is reported once instead of on every check. The first check of a file reads it in full; a file which is truncated, rotated or replaced is detected (via its size and a fingerprint of its first bytes) and read from the beginning again. A trailing line which is not yet terminated by line-split is held back until it is complete, so half-written lines are never reported twice. If you set this to auto (or leave the value empty) the bookmark name is derived from the file name together with a hash of your filter, warning and critical expressions, which keeps unrelated checks of the same file from consuming each other’s lines. Use an explicit name to share (or separate) positions deliberately. Positions are persisted when NSClient++ shuts down and restored on start; the newest ones are kept if more than a thousand accumulate.
Default Value: auto
max-lines:
Only examine the newest newest.
Default Value: 0
newest:
Which end of the file holds the newest line: last (the default: lines are appended, as with most machine-written logs) or first (the file is rewritten with the newest line at the top, which is common in hand-maintained files such as changelogs).
This only decides which end max-lines counts from; lines are always reported in the order they appear in the file. newest=first cannot be combined with bookmark, since a file which is rewritten from the top has no stable position to resume from.
Default Value: last
Common options:
These options are shared by all filter based commands and are described on the common options page; the default values below are specific to this command.
| Option | Default Value |
|---|---|
| filter | |
| warning | |
| warn | |
| critical | |
| crit | |
| ok | |
| debug | false |
| show-all | false |
| empty-state | ignored |
| perf-config | |
| escape-html | false |
| list-separator | , |
| top-syntax | ${count}/${total} (${problem_list}) |
| ok-syntax | |
| empty-syntax | %(status): Nothing found |
| detail-syntax | ${column1} |
| perf-syntax | ${column1} |
| byte-unit | |
| decimal-separator | |
| decimals | -1 |
| thousands-separator |
This command also accepts the standard help options: help, help-pb, show-default, help-short.
Filter keywords¶
| Option | Description |
|---|---|
| column() | Fetch the value from the given column number. |
| column1 | The value in the first column |
| column2 | The value in the second column |
| column3 | The value in the third column |
| column4 | The value in the 4:th column |
| column5 | The value in the 5:th column |
| column6 | The value in the 6:th column |
| column7 | The value in the 7:th column |
| column8 | The value in the 8:th column |
| column9 | The value in the 9:th column |
| file | The name of the file |
| filename | The name of the file |
| line | Match the content of an entire line |
This command also supports the common filter keywords: count, total, ok_count, warn_count, crit_count, problem_count, list, ok_list, warn_list, crit_list, problem_list, detail_list, sep, status.
Configuration¶
| Path / Section | Description |
|---|---|
| /settings/logfile/real-time | Real-time filtering |
| /settings/logfile/real-time/checks | Real-time filters |
Real-time filtering ¶
A set of options to configure the real time checks
| Key | Default Value | Description |
|---|---|---|
| enabled | false | Real time |
# A set of options to configure the real time checks
[/settings/logfile/real-time]
enabled=false
Real time ¶
Spawns a background thread which waits for file changes.
| Key | Description |
|---|---|
| Path: | /settings/logfile/real-time |
| Key: | enabled |
| Default value: | false |
Sample:
[/settings/logfile/real-time]
# Real time
enabled=false
Real-time filters ¶
A set of filters to use in real-time mode
This is a section of objects. This means that you will create objects below this point by adding sections which all look the same.
Keys:
| Key | Default Value | Description |
|---|---|---|
| byte unit | BYTE UNIT | |
| column split | COLUMN SPLIT | |
| column-split | COLUMN SPLIT | |
| command | COMMAND NAME | |
| critical | CRITICAL FILTER | |
| debug | DEBUG | |
| decimal separator | DECIMAL SEPARATOR | |
| decimals | -1 | DECIMALS |
| destination | DESTINATION | |
| detail syntax | SYNTAX | |
| empty message | eventlog found no records | EMPTY MESSAGE |
| escape html | ESCAPE HTML | |
| file | FILE | |
| files | FILES | |
| filter | FILTER | |
| list separator | LIST SEPARATOR | |
| maximum age | 5m | MAXIMUM AGE |
| ok | OK FILTER | |
| ok syntax | SYNTAX | |
| perf config | PERF CONFIG | |
| read entire file | read entire file | |
| severity | SEVERITY | |
| silent period | false | Silent period |
| source id | SOURCE ID | |
| target | DESTINATION | |
| target id | TARGET ID | |
| thousands separator | THOUSANDS SEPARATOR | |
| top syntax | SYNTAX | |
| warning | WARNING FILTER |
Sample:
# An example of a Real-time filters section
[/settings/logfile/real-time/checks/sample]
#byte unit=...
#column split=...
#column-split=...
#command=...
#critical=...
#debug=...
#decimal separator=...
decimals=-1
#destination=...
#detail syntax=...
empty message=eventlog found no records
#escape html=...
#file=...
#files=...
#filter=...
#list separator=...
maximum age=5m
#ok=...
#ok syntax=...
#perf config=...
#read entire file=...
#severity=...
silent period=false
#source id=...
#target=...
#target id=...
#thousands separator=...
#top syntax=...
#warning=...