← retour aux snippets

bash: xtrace horodaté vers fichier

Activer set -x avec PS4 enrichi (timestamp, script, ligne, fonction) et rediriger vers un log dédié.


objectif

Déboguer un script bash sans polluer stderr: activer set -x avec un préfixe riche (timestamp ISO, fichier, ligne, fonction) et écrire le trace dans un fichier via BASH_XTRACEFD.

code minimal

# activer xtrace horodaté vers un fichier (sans polluer stderr)
log="./xtrace.$(date -u +%Y-%m-%d).log"
exec 3>>"$log"
export BASH_XTRACEFD=3
export PS4='+ $(date -u +%Y-%m-%dT%H:%M:%SZ) ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
set -x

utilisation

#!/usr/bin/env bash
set -Eeuo pipefail

# xtrace dédié (logs dans ./logs/ avec rotation simple)
mkdir -p ./logs
exec 3>>"./logs/xtrace-$(date -u +%Y-%m-%d).log"
export BASH_XTRACEFD=3
export PS4='+ $(date -u +%Y-%m-%dT%H:%M:%SZ) ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
set -x

deploy_www() {
  local dest="/srv/data.pm/current"
  rsync -a --delete ./build/ "$dest/"
  touch "$dest/.deployed"
}

# masquer une section sensible (tokens, mots de passe)
set +x
API_TOKEN="$(< ./secrets/api.token)"
set -x

curl -fsS -H "Authorization: Bearer $API_TOKEN" https://api.data.pm/health -o /dev/null
deploy_www

variante(s) utile(s)

# fonction toggle: enable/disable xtrace avec fichier passé en argument
xtrace_on() {
  local log="${1:-./xtrace.log}"
  exec 3>>"$log"
  export BASH_XTRACEFD=3
  export PS4='+ $(date -u +%Y-%m-%dT%H:%M:%SZ) ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
  set -x
}
xtrace_off() {
  set +x
  exec 3>&- || true
}

# envoyer aussi au syslog (copie asynchrone) sans casser le retour de code
tail -F "./logs/xtrace-$(date -u +%Y-%m-%d).log" | logger -t data.pm -p user.debug &

# capturer xtrace uniquement pour une commande (subshell)
( exec 3>>./logs/one-shot.log; BASH_XTRACEFD=3 PS4='+ $(date -u +%H:%M:%S) ${BASH_SOURCE##*/}:${LINENO}: ' bash -x -c 'make -s build' )

# réduire le coût CPU du timestamp: préfixe figé par run (moins précis mais rapide)
export PS4='+ '"$(date -u +%Y-%m-%dT%H:%M:%SZ)"' ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}: '
set -x

# isoler le trace d'un module sourcé
module_log="./logs/module-$(date -u +%Y-%m-%d).log"
( exec 3>>"$module_log"; BASH_XTRACEFD=3 PS4='+ $(date -u +%H:%M:%S) ${BASH_SOURCE##*/}:${LINENO}: ' bash -x module.sh )

notes

  • BASH_XTRACEFD redirige le set -x vers un descripteur indépendant (ici 3), évitant de mélanger trace et sorties de la commande.
  • PS4 définit le préfixe par ligne de trace. Les expansions utiles: ${BASH_SOURCE##*/}, ${LINENO}, ${FUNCNAME[0]}.
  • set +x autour des secrets empêche de les divulguer dans les logs. Vérifiez aussi vos variables d’environnement.
  • ouvrez le FD avec >> (append) et des permissions strictes sur le dossier de logs (0700/fichiers 0600) si sensibles.
  • la commande date s’exécute à chaque ligne tracée (coût modéré). Pour diminuer l’overhead, figez une partie du préfixe (variante ci-dessus).