Repository
Munin (contrib)
Last change
2019-12-19
Graph Categories
Family
manual
Capabilities
Keywords
Language
Bash
Authors

loggrepx_

Example graph: day Example graph: week

Name

loggrepx - Counts the number of matching log lines by log file

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?”

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 /var/log/nginx/errors.log would be var_log_nginx_errors_log_warning.

One good way to get these names is to run munin-run [plugin-name] after you’ve configured the required variables and then just copy/paste the names from the output.

Author

Kael Shipman kael.shipman@gmail.com

License

Copyright 2018 Kael Shipmankael.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.

Magic Markers

#%# family=manual
#!/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