Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

lunedì 24 novembre 2025

Il Logger
come scrivere un logger in C - pt.2

Michael Corleone: Io non penso affatto di dover eliminare tutti Tom, solo i miei nemici, tutto qui.

(...una premessa: questo post potrebbe sembrare un remake di un mio vecchio post. In realtà tratta, incidentalmente, lo stesso argomento, ma amplia notevolmente il discorso è affronta ben altri temi. Leggete e mi direte...)

Visto che nell'ultimo articolo avevo introdotto l'argomento logger usando Il Padrino del Maestro Francis Ford Coppola, per questa seconda parte mi è sembrato il caso di usare un'altro suo capolavoro, Il Padrino - Parte II. E, come già detto nell'altro articolo,"...il logger lavora dietro le quinte e registra tutto, non gli sfugge niente...", proprio come il protagonista del film... Come avrete già capito, in questo articolo parleremo ancora di logger e di syslog!

...ti spiego: senza il logger non vai da nessuna parte...

E veniamo al dunque: ci eravamo lasciati con una pseudo-specifica di un logger che usa il syslog (system logger) , con tanto di anticipazione dei log ottenuti. Adesso è venuto il momento della implementazione vera e propria, e spero che qualcuno abbia raccolto il mio invito a scriverne una versione propria aspettando questo nuovo post (non potete certo dire che non vi ho lasciato il tempo...); tra l'altro, come vedremo tra poco, l'implementazione è veramente semplicissima. Nello scorso post avevamo visto l’header file (mylog.h) e un esempio di uso (main.c), e quindi, con grande originalità, il file di implementazione lo chiameremo mylog.c. E vediamolo, vai col codice!

#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include <unistd.h>
#include "mylog.h"

#define LOGGER_MAXSIZE 2048 // max size di un messaggio di log (in bytes)

// openLog - apre il log sul syslog
void openLog(int level)
{
// set del log level del syslog
setlogmask(LOG_UPTO(level));

// aprp il log sul syslog
openlog(NULL, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_USER);
}

// closeLog - chiude il log sul syslog
void closeLog()
{
// chiudo il syslog e mostro il risultato su stderr
closelog();
fprintf(stderr, "%s: syslog chiuso\n", __func__);
fflush(stderr);
}

// traceLog - scrive un messaggio di log sul syslog
void traceLog(int level, const char *format, ...)
{
// inizializzo la lista variabile di argomenti va_list usando la stringa format
va_list arglist;
va_start(arglist, format);

// costruisco una nuova stringa di format aggiungendo il TID a format
char my_format[LOGGER_MAXSIZE];
snprintf(my_format, sizeof(my_format), "[%d]: %s", gettid(), format);

// chiamo vsyslog(3) (syslog(3) con va_list) con la stringa di format costruita
vsyslog(level, my_format, arglist);

// chiudo la va_list
va_end(arglist);
}

Non starò a descrivere gli include-files, che sono, né più né meno, solo quelli che servono. Sembra lapalissiano, ma è normale trovare sorgenti con molti più include del necessario: non fanno danni ma complicano l'interpretazione visiva del codice. Ricordatevi di mettere solo quelli che servono.

Dopodiché passiamo alla vera e propria implementazione, e incontriamo le prime due delle tre funzioni che compongono il nostro logger, e cioè: openLog() e closeLog(). Sono molto semplici: la prima setta il livello di log con il valore passato come argomento (level) e poi esegue la vera e propria apertura usando l'apposita funzione della famiglia syslog(3), e cioè openlog(3); la seconda si limita, semplicemente, a chiudere il logger usando l'apposita funzione closelog(3).

Notare che openlog(3) accetta una notevole serie di opzioni (col suo secondo argomento, option), che si possono mettere in OR per definire il funzionamento richiesto. Vi rimando al manuale di syslog(3) per la lista delle opzioni disponibili, comunque vi faccio notare che nell'esempio ho usato quelle più comuni, e cioè:

  • LOG_PID: include il PID (Process ID) del chiamante in ogni messaggio di log.
  • LOG_NDELAY: apre immediatamente la connessione (altrimenti la connessione viene aperta quando viene registrato il primo messaggio di log). Si parla di connessione perché per scrivere sul syslog si usa un socket, come già visto qui.
  • LOG_PERROR: scrive il messaggio di log anche su stderr.

E adesso veniamo al cuore del nostro logger, la funzione traceLog(), che è quella che si occupa di scrivere le tracce di log nel file del syslog: per farlo usa funzioni della famiglia stdarg(3) (che gestisce le "variable argument lists"), visto che la nostra traceLog() deve usare una sintassi printf-like, quindi con stringa di formattazione e argomenti variabili. L'operazione è divisa in tre parti come indicato nel codice stra-commentato):

  1. Inizializzo la lista variabile di argomenti va_list  usando la stringa format.
  2. Costruisco una nuova stringa di format aggiungendo il TID (Thread ID).
  3. Chiamo vsyslog(3) (che sarebbe una syslog(3) con va_list) con la stringa di format costruita precedentemente.
  4. Chiudo la va_list.

Faccio un appunto sul punto 2 qui sopra: è la fase di personalizzazione del messaggio di log, dove si possono aggiungere (a piacere) dettagli che si sommano a quelli standard presenti di default. In questo esempio ho aggiunto il TID (Thread ID), che in una applicazione multithread può essere utile a tracciare adeguatamente quello che è successo durante l'uso.

Cosa ve ne sembra dell'implementazione? Non è male, no? Ed è semplicissima! Provate a copiare e a compilare il tutto (con il main() del precedente articolo), vi assicuro che funziona bene (come tutti gli esempi che propongo nel blog, eh!).

E con questo credo che possiamo considerare concluso l'argomento logger su syslog. Il codice presentato in questi due articoli è molto simile a quello che uso spesso in produzione, quindi si tratta di un esempio pratico di uso reale, eh! Fatene buon uso!

Ciao e al prossimo post!