Il monitoraggio costante dell'attività di un server è fondamentale per prevenire disservizi e garantirne il corretto funzionamento. In caso di problemi, un'analisi della situazione pregressa può rivelare informazioni importanti riguardo le cause del disservizio. Pertanto, la capacità di raccogliere e memorizzare tutte le informazioni relative allo stato del servizio è di fondamentale importanza.
Nginx espleta questa funzione mediante l'emissione di messaggi di log. Questi messaggi contengono informazioni relative agli eventi che si verificano durante la normale attività del server, quali ad esempio richieste HTTP, errori (ad esempio, una richiesta HTTP ad una risorsa non disponibile), ecc. Ogni messaggio è corredato da un'indicazione temporale (in genere, la data e l'ora in cui si è verificato l'evento) e da altri metadati che variano in base al tipo di evento.
Questi messaggi possono essere memorizzati direttamente su file di testo (i cosiddetti file log appunto), emessi sui flussi di output dei processi worker (ad esempio, sullo stderr), oppure inviati al syslog per essere gestiti in modo centralizzato.
Nginx utilizza due categorie distinte per i messaggi di log: in caso di errore, il messaggio relativo viene inviato all'Error Log; nel caso di normale attività del server, il messaggio viene inviato all'Access Log.
Configurazione dell'Error Log
La direttiva di configurazione error_log permette di specificare la destinazione dei messaggi di errore (un file, lo stderr o il syslog) ed il livello minimo di gravità degli stessi. Classificare gli eventi di errore in base al livello di gravità permette di controllare effettivamente la quantità di informazioni che verranno riportate nel log o di specificare più log, ciascuno contenente i messaggi di un livello specifico. Nginx classifica i messaggi in cinque categorie, in ordine di gravità: warn, error, crit, alert, emerg.
Per default i messaggi relativi ad errori con gravità pari o superiore ad "error" vegnono scritti nel file logs/error.log. Nel caso in cui queste impostazioni siano adeguate per il proprio server non è necessario specificare alcuna configurazione per la direttiva error_log. In caso contrario, essa può essere inserita sia nel contesto globale che nei blocchi http, stream, server e location. L'effetto della direttiva si applica al solo contesto nel quale è inserita.
Ad esempio, per memorizzare tutti i messaggi di errore emessi dal server (indipendentemente dal livello) nel file logs/miologerrori.log, la direttiva va specificata nel contesto globale, come segue:
# ...
error_log logs/miologerrori.log warn;
# ...
Dalla versione 1.5.2 di Nginx è possibile specificare più direttive error_log nello stesso contesto. Ciò permette di generare più file di log, ciascuno con diversi livelli di errore. Ad esempio:
# ...
error_log logs/warnings.log warn;
error_log logs/error_log.log error;
error_log logs/critical_erorrs.log crit;
# ...
Configurazione dell'Access Log
Le informazioni relative alle richieste effettuate dai client vengono invece emesse nell'Access Log. La configurazione di default prevede che i messaggi dell'Access Log vengano memorizzati nel file logs/access.log nel formato predefinito combined.
Il formato combined è un'estensione del Common Log Format utilizzata anche dal web server Apache. Maggior dettagli sono riportati nel documento W3C relativo, disponibile qui. Il formato è compatibile con il Common Log Format, al quale aggiunge i campi referrer, user-agent e cookie.
Una riga nel formato combined contiene quindi le seguenti informazioni:
- L'indirizzo IP del client che ha effettuato la richiesta;
- Un eventuale identificativo del client (di solito, "-");
- Il nome dell'utente che ha effettuato la richiesta (se inviato negli header della richiesta, ad esempio nel caso di autenticazione HTTP basic);
- La data, l'ora ed il fuso orario relativi al momento in cui è stata effettuata la richiesta;
- Il dettaglio della richiesta HTTP effettuata, che include il metodo HTTP (GET, POST, ecc.), l'URI richiesto e la versione del protocollo HTTP utilizzata;
- Il codice HTTP di risposta (ad esempio, 200);
- La dimensione in byte della risorsa inviata al client;
- L'eventuale referrer, così come indicato negli header HTTP inviati dal client;
- Lo User-Agent HTTP, così come indicato dal client;
- Eventuali cookie ritrasmessi dal client al server;
Ad esempio, la riga:
216.67.10.15 - user1 [01/Jul/2019:12:11:52 +0000] "GET /index.html HTTP/1.1" 200 431 "http://www.google.com/" "Google Chrome Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" "USERID=UserC;"
riporta le seguenti informazioni:
Indirizzo IP: | 216.67.10.15 |
---|---|
Identificativo del client: | |
Nome utente: | user1 |
Data e ora: | 12:11:52 01/06/2019 - GMT |
Richiesta HTTP: | GET |
URI: | /index.html |
Protocollo: | HTTP/1.1 |
Codice risposta: | HTTP 200 (OK) |
Dimensione in byte: | 431 |
Protocollo: | HTTP/1.1 |
Referral: | http://www.google.com/ |
User agent: | Google Chrome 58 su Windows 10 |
Cookie: | USERID=UserC; |
Nginx permette di arricchire ulteriormente i messaggi aggiungendo informazioni non presenti nel formato combined mediante la direttiva log_format. La direttiva accetta una stringa composta dai nomi delle varibili che possono essere riportate nei messaggi. Una lista completa delle variabili è disponibile nella documentazione ufficiale, consultabile qui.
Ad esempio, si supponga di aver configurato Nginx come reverse proxy (vedi qui) e di voler riportare i dettagli relativi ai tempi di elaborazione delle richieste al fine di indagare eventuali rallentamenti del servizio. Nginx offre diverse variabili atte allo scopo:
- $upstream_connect_time: Tempo necessario per stabilire la connessione con il server upstream;
- $upstream_header_time: Tempo intercorso tra l'inizio della connessione e la ricezione del primo byte;
- $upstream_response_time: Tempo intercorso tra l'inizio della connessione e la ricezione dell'ultimo byte;
- $request_time: Tempo totale speso per elaborare la richiesta;
Per aggiungere queste variabili al log si può definire un nuovo formato con la direttiva log_format, assegnare ad esso un nome (nell'esempio, "upstream_time") e poi inserire un riferimento a questo formato nella direttiva access_log. Ad esempio:
# ...
http {
log_format upstream_time '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"'
'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';
server {
access_log /spool/logs/nginx-access.log upstream_time;
# ...
}
}
# ...
Logging condizionale
Nginx offre la possibilità di controllare la dimensione dei file di log escludendo i messaggi che rispondano ad un criterio personalizzato. Ad esempio, si supponga di voler escludere dall'access log tutti i messaggi relativi alle richieste andate a buon fine. Per farlo è sufficiente indicare ad Nginx di voler escludere tutti i messaggi per i quali la variabile $status, che contiene il valore del codice HTTP di risposta, è compresa tra 200 (Success) e 300 (Redirect):
# ...
map $status $loggable {
~^[23] 0;
default 1;
}
access_log logs/access.log combined if=$loggable;
# ...
la variabile $loggable esprime la condizione descritta sopra, mediante una semplice espressione regolare ("~^[23]"): se il primo carattere della variaible è "2" o "3", la variabile $loggable assumerà valore 0, altrimenti avrà valore pari ad 1.
Utilizzando la variabile $loggable così definita nella clausola "if" della direttiva access_log si ottiene l'effetto desiderato: quando $loggable è pari a 0, i messaggi non vengono scritti nel file log.
Scrittura dei messaggi nel Syslog
Sia i messaggi dell'Access log che quelli dell'Error log possono essere inviati ad un server syslog, sia esso locale (ovvero in esecuzione sulla stessa macchina) o remoto.
Nel caso si voglia usare un server syslog locale è sufficiente indicare il percorso della socket Unix corrispondente. Ad esempio:
# ...
error_log server=unix:/var/log/nginx.sock debug;
# ...
Nel caso in cui si voglia utilizzare un server syslog remoto, bisognerà specificare il corrispondente indirizzo IP o nome host. Ad esempio:
# ...
access_log syslog:server=10.10.0.3:1234,facility=local7,tag=nginx,severity=info;
# ...
Come si evince dall'esempio precedente, è anche possibile specificare i parametri tipici di un messaggio syslog: facility e level. La facility indica la tipologia di programma che ha generato il messaggio. Poichè nella tassonomia standard non è presente un valore specifico per i server web (la specifica Syslog risale agli anni ottanta ed è pertanto precedente alla nascita del web), si utilizza il valore di default "local7". Maggiori informazioni sulle facilities e sui livelli definiti nel protocollo Syslog sono riportati nella RFC 5424, disponibile qui.