munin-contrib/plugins/logs/loggrepx_

167 lines
5.4 KiB
Bash
Executable File

#!/bin/bash
set -e
: << =cut
=head1 NAME
loggrepx - Counts the number of matching log lines by log file
=head1 DESCRIPTION
This plugin is somewhat of a bash port of the original loggrep plugin,
except that it displays a breakdown of matches per file, rather than
aggregating matches across all files. It is intended to answer the
question, "Which of my logs are reporting concerning events right now?"
=head1 CONFIGURATION
The plugin can be included multiple times to create graphs for various
differing kinds of information in your log files. For example, you may
want to monitor the occurrence of 5xx errors in your webserver logs, but
also the occurrence of alert|critical|emergency entries in your syslog.
You can accomplish this by linking the plugin twice and providing
different criteria for each instance.
Note that most instances will probably work best when run as root, since
log files are usually (or at least should be) controlled with strict
permissions.
Available config options include the following:
env.logfiles - Files to grep (shellglob) (required)
env.regex - Regex to look for (required)
env.title - Graph title
env.warning - Default warning level
env.critical - Default critical level
env.[field]_label - Label to use for a specific logfile
env.[field]_warning - Warning level for specific logfile
env.[field]_critical - Critical level for specific logfile
NOTE: for any variable with [field] in it, [field] is derived from the
full logfile path by simply removing the preceding slash and replacing
all non-alphanumerics with underscores. For example, the "warning" field
for the logfile F</var/log/nginx/errors.log> would be
F<var_log_nginx_errors_log_warning>.
One good way to get these names is to run C<munin-run [plugin-name]>
after you've configured the required variables and then just copy/paste
the names from the output.
=head1 AUTHOR
Kael Shipman <kael.shipman@gmail.com>
=head1 LICENSE
Copyright 2018 Kael Shipman<kael.shipman@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
=head1 MAGIC MARKERS
#%# family=manual
=cut
. "$MUNIN_LIBDIR/plugins/plugin.sh"
regex=${regex:-}
logfiles=${logfiles:-}
LOGFILES="$(IFS= ; for f in $logfiles; do echo "$f"; done)"
title="${title:-Log Matches}"
function config() {
echo "graph_title ${title}"
echo "graph_args --base 1000 -l 0"
echo "graph_vlabel ${title}"
echo "graph_category other"
echo "graph_info Lists number of times the given regex is matched in the given log files per period"
local var_prefix var logfile lbl
while read -u 3 -r logfile; do
var_prefix="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')"
var="${var_prefix}_label"
lbl="${!var:-$logfile}"
echo "$var_prefix.label $lbl"
print_warning "$var_prefix"
print_critical "$var_prefix"
echo "$var_prefix.info Lines that match '${regex}' in log file '$logfile'"
done 3< <(echo "$LOGFILES")
}
function fetch() {
# Load state
touch "$MUNIN_STATEFILE"
local nextstate=()
local curstate
curstate="$(cat "$MUNIN_STATEFILE")"
local var_prefix logfile prvlines curlines matches
while read -u 3 -r logfile; do
# Convert current logfile path to variable prefix
var_prefix="$(echo "$logfile" | sed -r 's/^[^a-zA-Z]+//g' | sed -r 's/[^a-zA-Z0-9]+/_/g')"
# Get running number of lines to determine whether or not the file may have been rotated
prvlines="$(echo "$curstate" | grep "^${var_prefix}_lines=" | cut -f 2 -d "=")"
if [ -z "$prvlines" ]; then
prvlines=0
fi
# Get the current number of lines in the file
curlines="$(wc -l < "$logfile")"
if [ -z "$curlines" ]; then
curlines=0
fi
# If the current line count is less than the previous line count, we've probably rotated.
# Reset to 0.
if [ "$curlines" -lt "$prvlines" ]; then
prvlines=0
else
prvlines=$((prvlines + 1))
fi
# Get current number of incidents
matches="$(tail -n +"$prvlines" "$logfile" | grep -Ec "${regex}" || true)"
# Echo the value
echo "$var_prefix.value $matches"
# Push onto next state
nextstate+=("${var_prefix}_lines=$curlines")
done 3< <(echo "$LOGFILES")
# Write state to munin statefile
(IFS=$'\n'; echo "${nextstate[*]}" > "$MUNIN_STATEFILE")
return 0
}
case "$1" in
config) config ;;
*) fetch ;;
esac