Schlagwort-Archive: access_log

Zwei Bash-Skripte zur Analyse der NGINX Access Logs

Beim Durchwühlen des Internets bin ich auf eine Perle gestoßen, die ich hier festhalten möchte. In „Bash Script to Parse and Analyze Nginx Access Logs“ stellt Ruan Bekker ein kurzes Bash-Skript vor, welches die NGINX Access Logs analysiert, um einen Bericht mit folgenden Sektionen auszugeben:

  • Top 10 Request IPs (aus dem aktuellen Access Log)
  • Top Request Methods (aus dem aktuellen Access Log)
  • Top 10 Request Pages (aus dem aktuellen Access Log)
  • Top 10 Request Pages (aus dem aktuellen und Gzipten Logs)
  • Top 10 HTTP 404 Page Responses (aus dem aktuellen und Gzipten Logs)

Ich selbst nutze aktuell den Fork von Marc Brunet, welchen ich meinen My-IT-Scripts hinzugefügt habe:

#!/bin/bash

# URL: https://github.com/Tronde/My-IT-Scripts/blob/master/bash/analyze_nginx_access_logs.sh

# variables
LOGFILE="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log"
LOGFILE_GZ="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log.*"
RESPONSE_CODE="200"

# functions
filters(){
grep -w $RESPONSE_CODE \
| grep -v "\/rss\/" \
| grep -v robots.txt \
| grep -v "\.css" \
| grep -v "\.jss*" \
| grep -v "\.png" \
| grep -v "\.ico"
}

filters_404(){
grep -w "404"
}

request_ips(){
awk '{print $1}'
}

request_method(){
awk '{print $6}' \
| cut -d'"' -f2
}

request_pages(){
awk '{print $7}'
}

wordcount(){
sort \
| uniq -c
}

sort_desc(){
sort -rn
}

return_kv(){
awk '{print $1, $2}'
}

request_pages(){
awk '{print $7}'
}

return_top_ten(){
head -10
}

## actions
get_request_ips(){
echo ""
echo "Top 10 Request IP's:"
echo "===================="

cat $LOGFILE \
| filters \
| request_ips \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

get_request_methods(){
echo "Top Request Methods:"
echo "===================="
cat $LOGFILE \
| filters \
| request_method \
| wordcount \
| return_kv
echo ""
}

get_request_pages_404(){
echo "Top 10: 404 Page Responses:"
echo "==========================="
zgrep '-' $LOGFILE $LOGFILE_GZ\
| filters_404 \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}


get_request_pages(){
echo "Top 10 Request Pages:"
echo "====================="
cat $LOGFILE \
| filters \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

get_request_pages_all(){
echo "Top 10 Request Pages from All Logs:"
echo "==================================="
zgrep '-' --no-filename $LOGFILE $LOGFILE_GZ \
| filters \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

# executing
get_request_ips
get_request_methods
get_request_pages
get_request_pages_all
get_request_pages_404

Selbstverständlich erhalte ich damit keine genauen Statistiken, da meine Logs nach einem Monat automatisch gelöscht werden. Für einen kurzen Rückblick und der Erstellung eines monatlichen Berichts scheint das kleine Skript jedoch gut geeignet zu sein. Ich probiere es gerade aus, um zu sehen, wie gut es mir auf Dauer gefällt.

Auf Basis des ersten Skripts habe ich ein zweites geschrieben, mit dessen Hilfe ich die Requests für einen spezifischen Beitrag abfragen kann (Quelle):

#!/bin/bash

# variables
LOGFILE="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log"
LOGFILE_GZ="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log.*"
RESPONSE_CODE="200"
ARG1=$1

# functions
filters(){
grep -w $RESPONSE_CODE \
| grep -v "\/rss\/" \
| grep -v robots.txt \
| grep -v "\.css" \
| grep -v "\.jss*" \
| grep -v "\.png" \
| grep -v "\.ico"
}

request_ips(){
awk '{print $1}'
}

request_page(){
awk '{print $7}' \
| grep -w $ARG1
}

wordcount(){
sort \
| uniq -c
}

return_kv(){
awk '{print $1, $2}'
}

get_request_page(){
echo "Page requests in current log:"
echo "====================="
cat $LOGFILE \
| filters \
| request_page \
| wordcount \
| return_kv
echo ""
}

get_request_page_all(){
echo "Page requests in all logs (last month):"
echo "==================================="
zgrep '-' --no-filename $LOGFILE $LOGFILE_GZ \
| filters \
| request_page \
| wordcount \
| return_kv
echo ""
}

# execute
get_request_page
get_request_page_all

Der folgende Code-Block zeigt ein Beispiel, wie das Skript angewendet wird. Dabei wird der Permalink als Argument übergeben:

:~/bin$ sh get_page_requests_from_nginx_access_logs.sh kommentar-linux-container-spreu-und-weizen
Page requests in current log:
=====================
262 /wordpress/kommentar-linux-container-spreu-und-weizen/
6 /wordpress/kommentar-linux-container-spreu-und-weizen/feed/

Page requests in all logs (last month):
===================================
5124 /wordpress/kommentar-linux-container-spreu-und-weizen/
49 /wordpress/kommentar-linux-container-spreu-und-weizen/feed/
2 /wordpress/wp-json/oembed/1.0/embed?url=https://www.my-it-brain.de/wordpress/kommentar-linux-container-spreu-und-weizen/

Noch nicht schön, aber zweckmäßig.

Was haltet ihr davon? Falls ihr beim Drübergucken zufällig noch einen Fehler in den Skripten entdeckt, freue ich mich, wenn ihr mir einen Kommentar hinterlasst.

Logging und logrotate mit NGINX

In diesem Artikel wird beschrieben, wie ich das Logging und logrotate meines NGINX-Servers konfiguriert habe. Dabei gehe ich kurz auf die beiden verwendeten Direktiven error_log und ngx_http_log_module ein.

Damit dient dieser Artikel meiner Dokumentation und evtl. euch als Anregung, ein eigenes Logging zu konfigurieren.

Logging

Informationen zum Logging findet man in der offiziellen NGINX-Dokumentation.[1. NGINX-Dokumentation] Im folgenden werden die Direktiven error_log[2. NGINX core module error_log] und ngx_http_log_module[3. NGINX – ngx_http_log_module] verwendet.

Mein Server liefert mehrere Webseiten aus. Ich möchte gern für jede Webanwendung ein separates Error-Log und Access-Log schreiben. Dabei wird folgendes Muster verwendet:

  • Log-Verzeichnis: /var/www//logs
  • Name für error_log: _error.log
  • Name für access_log: _access.log

Konfiguration des Error-Log

Die Error_log-Syntax ist denkbar einfach:

error_log log_file [ log_level ]

log_file gibt den Pfad zur Log-Datei an. Mit log_level wird bestimmt, wie viele Informationen protokolliert werden sollen.

Log-Level[4. How To Configure Logging and Log Rotation in Nginx on an Ubuntu VPS]

  • emerg: Notfall, in dem sich das System in einem nicht nutzbaren Zustand befindet
  • alert: Ernste Störung. Sofortiger Eingriff ist erforderlich
  • crit: Kritische Probleme, um die man sich kümmern sollte
  • error: Ein Fehler ist aufgetreten. Hier funktioniert etwas nicht
  • warn: Ein ungewöhnliches Ereignis ist aufgetreten. Dies ist jedoch kein Grund zur Sorge
  • notice: Normale Vorgänge werden ebenfalls protokolliert
  • info: Unnützes Wissen – Nice to know
  • debug: Debugging-Informationen, welche helfen, ein Problem näher zu analysieren

Die Log-Level sind nach Priorität angeordnet. Wird das Level auf „error“ gesetzt, so werden alle Events der Level error, crit, alert und emerg protokolliert.

Möchte man rein gar nichts protokollieren, muss das Log nach /dev/null umgeleitet werden.

error_log /dev/null crit;

Konfiguration des Access-Log

Das Modul ngx_http_log_module besteht aus den Direktiven access_log, log_format und open_log_file_cache, von denen ich hier nur die ersten beiden verwenden werde.

Mit der Direktive log_format kann das Format der Log-Dateien konfiguriert werden. Die einzelnen Formate werden über einen Bezeichner ausgewählt. Dies kann z.B. wie folgt aussehen:

log_format compression '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$gzip_ratio"';

access_log /spool/logs/nginx-access.log compression buffer=32k;

Eine detaillierte Beschreibung aller verfügbaren Parameter kann der offiziellen Dokumentation entnommen werden.[5. NGINX -log_format]

Ich selbst verwende aktuell ausschließlich das Format combined. Dieses ist bereits in der Standardinstallation enthalten. Es sieht wie folgt aus:

log_format combined '$remote_addr - $remote_user [$time_local]  '
		    '"$request" $status $body_bytes_sent '
		    '"$http_referer" "$http_user_agent"';

Für die Protokollierung meiner Webanwendungen wird daher folgendes in die jeweiligen Server-Direktiven eingetragen:

server {
...
access_log /var/www//logs/_access.log combined;
...
}

Falls man das Access-Log deaktivieren möchte, kann man dies durch den folgenden Eintrag erreichen:

access_log off;

Nun werden schon mal alle Log-Dateien nach Webanwendungen getrennt in das Verzeichnis /var/www//logs geschrieben.

Im nächsten Abschnitt gehe ich darauf ein, wie man verhindert, dass die Festplatte mit Log-Dateien vollgeschrieben wird.

Rotation der NGINX Log-Dateien

Zum Rotieren der Logs verwende ich die Anwendung logrotate. Diese ist bei Ubuntu bereits in der Standardinstallation enthalten.

Es wird ein Skript im Verzeichnis /etc/logrotate.d erstellt und folgender Inhalt eingefügt.

/var/www//logs/*.log {
        daily
        missingok
        rotate 31
        compress
        delaycompress
        notifempty
        sharedscripts
        postrotate
                [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        endscript
}

Mit diesem Skript wird logrotate angewiesen, die Log-Dateien im Verzeichnis /var/www//logs/ täglich zu rotieren und die letzten 31 Log-Dateien zu behalten. Die Log-Datei wird nicht rotiert, falls sie leer ist, also keine Einträge enthält. Die älteren Dateien werden dabei komprimiert, um Speicherplatz zu sparen.

Die generelle Beschreibung von logrotate würde den Rahmen dieses Artikels sprengen. Weitere Informationen sind in der Manpage zu finden.[6. logrotate(8) – Linux man page]

Damit ist der verspätete Frühjahrsputz auf diesem Server beendet.