#!/bin/bash # -*- bash -*- : <<=cut =head1 NAME nginx error - Munin plugin to monitor nginx error rates (http status codes per minute). =head1 APPLICABLE SYSTEMS Any host running nginx, with bash version > 4.0 =head1 CONFIGURATION This shows the default configuration of this plugin. You can override the log file path, the logpattern and the awk_script. Additionally you may want to adjust 'group' (or 'user') based on the permissions required for reading the log file. [nginx_error] group adm env.logpath /var/log/nginx env.logpattern a.*.log env.awk_script {print $9} Nginx must also be configured to log accesses in "combined" log format (default) or the awk_script adapted for your log format =head1 USAGE Link this plugin to /etc/munin/plugins/ and restart the munin-node. This plugin also can be used like wildcard-plugin for monitoring particular virtual host log. E.g. ln -s /usr/share/munin/plugins/nginx_error /etc/munin/plugins/nginx_error_mydomaincom will parse the log file /var/log/nginx/a.mydomaincom.log You can change 'env.logpattern' using asterisk ('*') to match your logs filenames. 'env.logpattern' is ignored for a non-symlink configuration. =head1 INTERPRETATION The plugin shows nginx http "error" status rates by parsing access log. =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf =head1 BUGS None known. =head1 VERSION 1.2 - 2024/10/29 * add env variable awk_script to support other log formats 1.1 - 2018/01/20 * add 'dirty config' capability support * fix shell style issues reported by shellcheck * improve readability of symlink configuration code 1.0 - 2017/02/21 =head1 AUTHOR vovansystems@gmail.com, 2013 =head1 LICENSE GPLv3 =cut set -eu # default environment variable values logpath=${logpath:-/var/log/nginx} # derive the name of the log file from a potential symlink-configured virtual host script_name=$(basename "$0") plugin_suffix=${script_name#nginx_error} if [ -n "${plugin_suffix#_}" ]; then # a domain was given via symlink configuration: adjust the logpattern domain=${plugin_suffix#_} # default logpattern for symlink configuration mode logpattern=${logpattern:-a.*.log} log_filename=${logpattern/\*/$domain} else log_filename='access.log' fi log="$logpath/$log_filename" # shellcheck disable=SC2016 awk_script=${awk_script:-'{print $9}'} # declaring an array with http status codes, we are interested in declare -A http_codes http_codes[400]='Bad Request' http_codes[401]='Unauthorized' http_codes[403]='Forbidden' http_codes[404]='Not Found' http_codes[405]='Method Not Allowed' http_codes[406]='Not Acceptable' http_codes[408]='Request Timeout' http_codes[429]='Too Many Requests' http_codes[499]='Client Connection Terminated' http_codes[500]='Internal Server Error' http_codes[502]='Bad Gateway' http_codes[503]='Service Unavailable' # parse error counts from log file do_fetch () { local count status_code declare -A line_counts values="$(awk "$awk_script" "$log" | sort | uniq -c)" # Log files may be empty due to logrotation if [ -n "$values" ]; then while read -r count status_code; do line_counts[$status_code]=$count done <<< "$values" fi for status_code in "${!http_codes[@]}"; do echo "error${status_code}.value ${line_counts[$status_code]:-0}" done } do_config () { local status_code echo "graph_title $(basename "$log") - Nginx errors per minute" echo "graph_vlabel pages with http error codes / \${graph_period}" echo "graph_category webserver" echo "graph_period minute" echo "graph_info This graph shows nginx error rate per minute" for status_code in "${!http_codes[@]}"; do echo "error${status_code}.type DERIVE" echo "error${status_code}.min 0" echo "error${status_code}.label $status_code ${http_codes[$status_code]}" done } do_autoconf () { echo yes } case ${1:-} in config) do_config # support "dirty config" capability if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi ;; autoconf) do_autoconf ;; '') do_fetch ;; esac exit $?