Script per backup de logs diaris amb compressió automàtica (tar + gzip)
Tenir un mecanisme de backup diari de logs no hauria de requerir una solució de tercers, un agent pesant, ni una estructura més complexa que el propi sistema de logs. Moltes vegades n’hi ha prou amb un script de shell ben fet, pensat per a córrer a diari des de cron, que arxivi el que toca, ho comprimeixi, ho etiqueti correctament, i ho deixi on ha d’estar. Lo difícil no és escriure l’script en si, sinó que funcioni de forma fiable a tots els escenaris reals: canvis de nom dels arxius, errors silenciosos, logs trencats a destemps, discos gairebé plens, i cron jobs que no avisen quan fallen.
Què ha de fer un script de backup de logs ben fet
Un bon script diari de backup de logs ha de cobrir aquests punts:
- Arxivar tots els logs generats el dia anterior.
- Incloure únicament els logs d’aquell dia, sense arrossegar dies anteriors.
- Comprimir el resultat.
- Etiquetar correctament el nom de l’arxiu (data, host, tipus de logs).
- Guardar-lo en una destinació configurada.
- Registrar el que ha fet (i si ha fallat, registrar perquè).
- Ser tolerant a errors (per exemple, si un arxiu falta, no avortar-ho tot).
- No fer servir eines o dependències que no vinguin de sèrie al sistema.
Tot això sense suposar que l’entorn és perfecte, perquè mai ho és.
L’ estructura base de l’ script
Aquí va un exemple real, per a fer servir en producció, que arxiva logs de diverses aplicacions en /var/log/appname, separades per tipus (nginx, app, workers), cadascuna amb la seva subcarpeta.
#!/bin/bash
# backup-logs.sh
set -euo pipefail
IFS=$'\n\t'
# Configuració
LOG_BASE_DIR="/var/log/appname"
BACKUP_DIR="/mnt/backups/logs"
TODAY=$(date +%Y-%m-%d)
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
HOSTNAME=$(hostname -s)
# Excloure extensions no desitjades (evita comprimir arxius ja comprimits)
EXCLUDE_PATTERNS=("*.gz" "*.zip" "*.tar")
# Crear carpeta de destí si no existeix
mkdir -p "$BACKUP_DIR/$YESTERDAY"
# Funció per a arxivar i comprimir
backup_logs_for_dir() {
local log_type="$1"
local source_dir="$LOG_BASE_DIR/$log_type"
local dest_file="$BACKUP_DIR/$YESTERDAY/${HOSTNAME}-${log_type}-${YESTERDAY}.tar.gz"
# Verifica si el directori existeix
if [[ ! -d "$source_dir" ]]; then
echo "[$(date)] Directori no trobat: $source_dir, s' omet."
return
fi
# Busca arxius modificats el dia anterior
files=$(find "$source_dir" -type f -newermt "$YESTERDAY" ! -newermt "$TODAY")
if [[ -z "$files" ]]; then
echo "[$(date)] No hi ha arxius per $log_type el $YESTERDAY"
return
fi
# Construcció d' exclusió
exclude_args=()
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
exclude_args+=("--exclude=$pattern")
done
# Arxivat i compressió
tar -czf "$dest_file" -C "$source_dir" . --mtime="$YESTERDAY" "${exclude_args[@]}"
echo "[$(date)] Backup completat: $dest_file"
}
# Llista de tipus de logs que es donarà suport
log_types=("nginx" "app" "worker")
for log_type in "${log_types[@]}"; do
backup_logs_for_dir "$log_type"
done
Decisions que eviten errors
Separar per tipus de logs
Encara que tots els logs estiguin sota una mateixa arrel, dividir per tipus (nginx, app, worker) evita problemes quan es vol restaurar només una part, o quan una app genera molts més logs que una altra. També fa que els backups no es barregin si alguna cosa falla a meitat d’execució.
Nomenar l’ arxiu amb hostname i data
Això sembla trivial, però quan tens diversos servidors fent backups en un destí compartit (un NFS o una carpeta muntada per rsync), que els arxius es nomenin igual (logs-2024-07-04.tar.gz) es torna un problema. Fer servir $(hostname -s) o una variable passada per entorn elimina aquest inconvenient.
Fer servir find amb -newermt en lloc de confiar en noms d’arxiu
Molts scripts basats en *.log o app-2024-07-04.log donen per suposat que tots els logs tenen noms consistents. Això no sempre és cert. Algunes aplicacions escriuen en un sol arxiu app.log i el roten per mida, no per data. D’altres, com ara logrotate, renomenen i mouen arxius en qualsevol moment del dia.
Filtrar per data de modificació (mtime) és més fiable quan t’interessa només l’escrit ahir, no el nom de l’arxiu.
Fer servir:
find /ruta -type f -newermt "$YESTERDAY" ! -newermt "$TODAY"
és més robust que qualsevol grep sobre noms d’arxiu.
Arxivar amb tar en lloc de moure o copiar arxius
Moure els logs originals pot ser un error a producció, perquè algunes aplicacions no toleren bé que els seus arxius de log desapareguin o canviïn d’inode. Arxivar directament des del directori, sense tocar res, és més segur.
El que es busca aquí no és neteja, sinó una còpia comprimida diària. La neteja es pot fer amb logrotate o una altra política posterior, no amb l’script de backup.
Què passa quan alguna cosa falla
Aquest tipus de scripts es llença per cron, i si no es gestionen bé els errors, ningú s’adona quan falla. Algunes recomanacions:
Redirigir la sortida d’ errors
Una sortida mínima és redirigir tot el que stderr a un log:
exec 2>> /var/log/backup-logs-error.log
Però això només serveix si algú revisa aquest log. En entorns on hi ha integració amb sistemes de monitoratge (com Nagios, Prometheus, etc.), convé afegir una alerta si l’script falla o si no genera l’arxiu esperat.
Validació post-backup
Una pràctica útil és verificar la mida de l’ arxiu generat. Si tar.gz pesa menys de cert llindar (per exemple, 5 KB), probablement alguna cosa ha sortit malament o no hi havia res a recolzar.
if [[ -f "$dest_file" ]]; then
size=$(stat -c%s "$dest_file")
if [[ "$size" -lt 5120 ]]; then
echo "[$(date)] Advertència: backup $dest_file massa petit ($size bytes)"
fi
fi
Polítiques de retenció i neteja
L’script anterior no esborra backups vells. Si tens una política de retenció de 30 dies, pots afegir un bloc així al final:
find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +30 -delete
Això elimina qualsevol arxiu .tar.gz amb més de 30 dies. Si els arxius estan organitzats per carpeta (una per data), pot eliminar directoris sencers:
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
Depèn de com vulguis organitzar l’arbre de backups.
Consideracions de rendiment
Quan els logs són grans (més de 500 MB al dia), la compressió pot trigar i consumir força CPU. En aquests casos:
- Es pot canviar tar -czf per tar -cf – | pigz > archivo.tar.gz si tens pigz instal·lat, que fa servir múltiples nuclis.
- Si els backups es fan en paral·lel en diversos servidors, és preferible llançar-los en moments desfasats per a evitar pics de càrrega en un servidor NFS central.
També es pot canviar el tipus de compressió si l’ emmagatzematge és barat i la velocitat importa més:
tar -cf archivo.tar -I 'lzop' # molt més ràpid, però menys compressió
Què no fer
- Comprimir arxiu per arxiu abans d’ arxivar-los. Això alenteix el procés i no es guanya res: tar comprimeix millor quan agrupa.
- Copiar arxius abans d’arxivar-los, creient que es necessita una “còpia de treball”. Augmenta l’ús de disc innecessàriament.
- Assumir que *.log inclou tot lo rellevant. Hi ha molts logs rotats amb noms diferents.
- No fer servir set -e ni pipefail. Un error silenciós a tar pot deixar-te sense backup i sense avisos.
Extensió per a múltiples servidors
En entorns distribuïts, cada servidor executa el seu script localment i guarda el .tar.gz en una destinació compartida (/mnt/backups/logs muntat per NFS, SSHFS, o amb rsync). El nom de l’arxiu ja porta el hostname, així que no es trepitgen.
Per a transferències més segures, es pot guardar localment primer i després fer un rsync al servidor central:
rsync -avz "$BACKUP_DIR/$YESTERDAY" backupuser@central:/data/backups/logs/
Això separa el backup del transport, cosa que simplifica la depuració si alguna cosa falla.
Variants segons l’ entorn
Tot i que l’script anterior cobreix la majoria d’escenaris estàndard, hi ha ajustos que val la pena aplicar segons el tipus d’entorn i les seves restriccions.
Logs amb timestamps en nom d’ arxiu
Algunes aplicacions generen un arxiu per dia amb nom com app-2025-07-04.log. En aquest cas, és més directe fer servir find per nom en lloc de mtime, sempre que el naming sigui consistent:
find "$source_dir" -type f -name "*$YESTERDAY*.log"
Això és més ràpid que avaluar timestamps, però deixa de funcionar si l’app canvia el seu patró de noms o rota per mida. En general, quan hi ha dubtes sobre consistència, és preferible mtime.
Directoris amb molts petits arxius
Si l’arbre de logs té milers d’arxius petits per dia (el típic amb workers que escriuen logs per tasca o per client), pot trigar més a recórrer el directori que a comprimir. En aquests casos, limitar la profunditat del find o aplicar un filtre directe sobre noms pot millorar el rendiment.
Exemple:
find "$source_dir" -maxdepth 2 -type f -newermt "$YESTERDAY" ! -newermt "$TODAY"
També ajuda desactivar la compressió a tar i deixar-la per després, si es fa en un pas separat o si es fa servir xz offline per a emmagatzematge a llarg termini.
Emmagatzematge limitat
Si estàs treballant amb espai ajustat (servidors amb discos petits, VPS, etc.), pots muntar temporalment un volum extern (NFS o similar) abans de generar el .tar.gz i escriure directament aquí. Alguna cosa com:
mount -t nfs backupserver:/exports/logs /mnt/backup
tar -czf /mnt/backup/${HOSTNAME}-${log_type}-${YESTERDAY}.tar.gz ...
umount /mnt/backup
Això redueix la pressió sobre l’emmagatzematge local i evita sorpreses si un disc ple interromp el backup sense avisar.
Gestió de logs rotats fora d’ horari
Una font d’errors comú és quan el procés que genera logs els rota en horaris arbitraris. Per exemple, si una app arxiva el seu log a les 03:00 de l’endemà (a través del seu propi mecanisme), els arxius amb contingut del dia anterior tenen data de modificació de l’endemà.
Això afecvta els filtres basats en mtime.
Solució parcial: fer servir stat amb ctime o revisar el contingut amb grep i extreure dates, però això és costós i no sempre viable.
Alternativa pràctica: si coneixes el patró de noms, pots considerar un desfasament de dates. Exemple:
DAY_TO_BACKUP=$(date -d "2 days ago" +%Y-%m-%d)
i així t’assegures de capturar arxius que es van moure tard. No és ideal, però moltes vegades és el més estable.
Backup de logs de sistema
L’script pot adaptar-se fàcilment a recolzar logs del sistema (/var/log) o logs de serveis com journald si exportes prèviament els binaris. Per a journalctl:
journalctl --since "yesterday 00:00" --until "today 00:00" > /var/log/journal-${YESTERDAY}.log
i després inclous aquest arxiu al teu tar.gz.
Això és especialment útil en servidors sense syslog tradicional o on només fas servir journald.
Backup incremental vs diario completo
Una altra decisió rellevant: arxivar tot el log del dia anterior, o fer un backup incremental per canvis?
Per a logs, el backup incremental poques vegades compensa. Tret que tinguis volums gegants i molt bona segmentació per timestamp, és més simple i segur comprimir una còpia diària. L’espai que ocupa gzip amb nivells raonables és manejable, i els errors en incrementals són més difícils de detectar o revertir.
Registre del procés de backup
Sempre convé guardar un log del propi procés de backup. Pots afegir a l’script una variable LOGFILE com:
LOGFILE="/var/log/backup-logs-${TODAY}.log"
exec >> "$LOGFILE" 2>&1
Així tens un historial simple que pots consultar en cas de dubte o fallada. No cal que el log sigui detallat, però sí que registri què es va copiar, quant va ocupar, i si hi va haver errors.
Unitats cron i supervisió
Tot i que l’script es pot executar via cron, també és comú muntar-lo com un servei de systemd en entorns moderns. Això dona més control sobre la seva execució i et permet fer servir journalctl per verificar errors o temps.
Exemple d’unitat systemd:
[Unit]
Description=Còpia de seguretat diari de logs
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-logs.sh
[Install]
WantedBy=multi-user.target
I ho programes amb un timer en lloc de cron, que té avantatges en quant a tolerància a reinicis o sistemes suspesos.
Notificació d’ errors
Si no hi ha sistema d’alertes actiu (com Prometheus o Zabbix), almenys convé que l’script enviï un correu en cas d’error:
if ! backup_logs_for_dir "app"; then
echo "Backup fallit per a logs de app el $YESTERDAY" | mail -s "Error de backup" sysadmin@empresa.com
fi
Això no és infal·lible, però més val tenir una mínima notificació que confiar que algú miri els logs manualment.
Conclusió
El backup diari de logs amb tar i gzip no és glamurós, però és part del teixit que sosté els sistemes reals. Fer-ho bé no requereix eines noves ni dependències extra, només entendre com es comporten els logs a la pràctica, i construir un script que aguanti condicions no ideals: rotacions impredictibles, formats inconsistents, discos plens, i errors silenciosos.
Les decisions petites —com filtrar els arxius, com nomenar el .tar.gz, on escriure els logs del mateix script— són les que determinen si, quan alguna cosa falla, tens una còpia útil o només un tarball buit amb la data correcta.
Un backup diari fiable és poc espectacular, però molt valuós.

