Dimarts, febrer 10, 2026
LINUX

Cron amb logging personalitzat per a tasques crítiques

Molts administradors de sistemes fem servir sempre cron per a automatitzar tasques a Linux. Funciona, és estable, està a tot arreu i no dona problemes… fins que els dona. L’assumpte es complica quan parlem de tasques crítiques: processos nocturns que mouen dades, generen reports, sincronitzen sistemes externs, netegen cues, disparen alertes. I tot i que cron no va ser dissenyat pensant en logging estructurat, a la pràctica necessites alguna cosa més que redireccionar la sortida a un arxiu.

1. cron és mut per defecte: comença per assumir que no veuràs res

El primer error comú: confiar en el correu. Cron envia la sortida del job per correu a l’usuari definit (o al root). Però això requereix que tinguis el correu correctament configurat, que ningú hagi deshabilitat el daemon, que no hi hagi regles de spam internes, i que algú estigui llegint aquesta bústies. En moltes empreses, això ja no passa de fa anys.

La pràctica: desconfia dels correus de cron. Si no pots garantir que els mails es lliuren, es revisen i s’entenen, no són logging.

Per això, el primer és redireccionar sempre stdout i stderr a un log explícit. El patró mínim és:

* * * * * /path/to/script.sh >> /var/log/elsmeusscripts/script.log 2>&1

Però amb això no n’hi ha prou. Ara tens un arxiu que creix indefinidament, on tots els errors i missatges van sense context, sense timestamps, sense separació per execució.

2. Logging amb context per execució

Si l’script s’executa cada 5 minuts, i alguna cosa falla a les 03:10, no vols perdre temps deduint quina va ser l’execució fallida entre 200 línies barrejades. El que resulta més clar és forçar una separació per execució, i anteposar timestamps a cada línia.

Una opció simple és fer wrapper de l’script al cron:

* * * * * /usr/local/bin/wraplog.sh /path/to/script.sh >> /var/log/elsmeusscripts/script.log 2>&1

I wraplog.sh pot ser una cosa com:

#!/bin/bash
echo "===== $(date '+%Y-%m-%d %H:%M:%S') - START: $* ====="
"$@"
status=$?
echo "===== $(date '+%Y-%m-%d %H:%M:%S') - END: $* (exit: $status) ====="
exit $status

Si l’script genera molta sortida, i vols timestamp per línia, necessites una mica més:

"$@" 2>&1 | while IFS= read -r line; do
  echo "$(date '+%Y-%m-%d %H:%M:%S') $line"
done

Hi ha gent que prefereix logs rotatius per execució (un per dia, hora, o per PID). Pot tenir sentit si l’script genera molta sortida o si necessites enviar logs individuals a sistemes de monitoratge o SIEM, però en la majoria d’entorns amb control de disc i logrotate, prefereixo un log per tasca amb estructura clara i neteja automàtica.

3. Logs rotatius i gestió del creixement

No deixis que el log creixi sense control. Hi ha errors comuns:

  1. No posar copytruncate en scripts que no es reinicien entre execucions.
  2. Oblidar compress i delaycompress per a discos compartits.
  3. No tenir dateext en entorns on necessites rastrejar què va passar tal dia.

Un exemple típic en /etc/logrotate.d/elsmeusscripts:

/var/log/elsmeusscripts/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
    dateext
}

I una cosa que sovint s’oblida: valida que cron tingui permisos per a escriure el log i que l’usuari del job tingui accés. M’he trobat casos en què un sysadmin canvia l’owner del log i espatlla el job sense adonar-se’n.

4. Logging estructurat

En tasques crítiques que interactuen amb tercers o amb processos comptables, m’ha servit adoptar un mínim d’estructura: logs que puguis parsejar fàcilment amb grep, awk, o enviar a journald, logstash, cloudwatch, etc.

Sense muntar un sistema de logging central, pots aplicar patrons simples:

echo "$(date +'%Y-%m-%d %H:%M:%S') | INFO | Sincronització de comandes..."
...
echo "$(date +'%Y-%m-%d %H:%M:%S') | ERROR | Fallada en la connexió amb API: $err"

O fer servir funcions de log a bash:

log_info() {
    echo "$(date +'%Y-%m-%d %H:%M:%S') | INFO | $*"
}
 
log_error() {
    echo "$(date +'%Y-%m-%d %H:%M:%S') | ERROR | $*"
}
 
log_info "Inici de procés"

Aquest patró ajuda quan hi ha múltiples scripts i vols comparar errors o filtrar fallades sense obrir cada log complet.

5. Scripts amb estat: log no és el mateix que control

Un altre error comú: confiar només en el log per saber si alguna cosa ha funcionat. Si l’script no torna un exit code correcte, el log pot semblar “correcte” però el job falla silenciosament. I al revés: si un script falla però en torna 0, cron ni se n’assabenta.

La regla: tot script de cron ha de tenir exit codes clars i controlats. Qualsevol excepció o fallada esperada s’ ha de gestionar explícitament.

També val la pena mantenir un last_status o last_run en una ruta coneguda. Un arxiu d’ estat, JSON o simple text, que indiqui:

{
  "last_run": "2025-06-28T03:00:00",
  "exit_code": 0,
  "duration_sec": 37,
  "errors": 0
}

Aquest arxiu pot fer-se servir per a un monitoratge bàsic sense necessitat de parsejar logs: check_script_status.sh pot revisar els .status i alertar si algun porta més de 12 hores sense actualitzar-se, o si el exit_code no és 0.

6. Evita l’antipatró de “redirigir i oblidar”

Un dels pitjors hàbits és el següent patró:

* * * * * /path/to/script.sh > /dev/null 2>&1

Això és llençar els errors a les escombraries. Fins i tot si és un script “de prova” o “no crític”, aquest patró és l’equivalent a tapar-se les oïdes. Si alguna cosa falla i ningú se n’assabenta, el problema no és l’script: és la decisió d’ignorar la seva sortida.

Hi ha moments on redirigir /dev/null pot tenir sentit, però que sigui una decisió conscient, documentada, i limitada en temps.

7. Logging i concurrència

Quan un script triga més del que dura l’interval entre execucions, pots acabar amb processos encavalcats. De vegades el descobreixes perquè el log està desordenat, de vegades perquè el sistema comença a col·lapsar. Si l’script no té locking, és només qüestió de temps.

El més simple: flock.

* * * * * /usr/bin/flock -n /var/lock/miscript.lock /path/to/script.sh >> /var/log/miscripts/script.log 2>&1

Això evita que múltiples instàncies s’executin alhora. I si combines això amb un log per execució (tipus timestamp en el filename), evita que un segon job trepitgi el log del primer.

8. Quan el job és crític, tracta el logging com a part del lliurament

En diversos projectes, l’èxit d’un job no és només que acabi, sinó que ho hagi fet bé, a temps i amb un assoliment. I això significa que el mateix script ha de validar el seu entorn i ser explícit en el que ha fet.

En tasques crítiques (processos de facturació, conciliació bancària, backup de bases de dades), no hem de deixar que l’script simplement faci “lo seu”. Ha de registrar:

  1. Quines versions d’eines ha fet servir (per si canvien).
  2. Quins paràmetres ha rebut.
  3. Quant ha trigatr cada etapa.
  4. Quants elements ha processat.
  5. Quins errors ha trobat i si els ha tractat o ha avortat.

Això fa que, quan alguna cosa va malament, no estiguis cec. Pots revisar un sol log i entendre si el problema ha estat de xarxa, de permisos, de dades o de codi.

9. Exemple final: patró complet en un entorn real

Aquest és un exemple real, lleugerament anonimitzat, de com executar una tasca crítica de sincronització cada hora:

0 * * * * /usr/bin/flock -n /var/lock/sync_clients.lock /usr/local/bin/wraplog.sh /opt/sync/sync_clients.sh >> /var/log/sync/sync_clients.log 2>&1

El sync_clients.sh té funcions com:

log_info "Connectant a base de dades"
if ! psql ...; then
    log_error "Error de connexió a base de dades"
    exit 1
fi
 
log_info "Descarregant dades remotes"
resp=$(curl -sSf https://api/clients)
if [ $? -ne 0 ]; then
    log_error "Fallada en descarregar dades remotes"
    exit 2
fi
 
log_info "Processant dades"
# processament...
 
log_info "Sincronització completada amb èxit"

El log resultant:

2025-06-28 03:00:00 | INFO | ======= START: /opt/sync/sync_clients.sh =======
2025-06-28 03:00:00 | INFO | Connectant a base de dades
2025-06-28 03:00:01 | INFO | Descarregant dades remotes
2025-06-28 03:00:03 | INFO | Processant dades
2025-06-28 03:00:10 | INFO | Sincronització completada amb èxit
2025-06-28 03:00:10 | INFO | ======= END: /opt/sync/sync_clients.sh (exit: 0) =======

I això és tot el que necessita veure qui estigui de guàrdia.

Conclusió

El logging en cron no és una eina separada. És part del sistema que estàs automatitzant. Si el job és important, el seu log també ho és. Invertir una mica de temps a estructurar-lo, cuidar-lo i poder llegir-lo ràpid és una d’aquestes decisions que no brillen en el full de ruta, però que et salven el cul a les 3 del matí.

Si algú hereta el teu sistema i pot entendre què va passar només llegint el log, has fet bé la teva feina.

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *