observability-and-instrumentation
Rend le comportement des automatisations VISIBLE et diagnosticable — logs structurés JSON, compteurs de fin de run, heartbeats, alertes sur seuil — pour les timers systemd, tâches planifiées, pipeline
Files2
View on GitHubSKILL.mdcowork-plugins/tk-dev-discipline/skills/observability-and-instrumentation/SKILL.md
GitHubObservabilité & instrumentation
Objectif : répondre à « ça a tourné, et qu'a-t-il fait ? » SANS relancer. Règles citées (R34 : tout
artefact opérationnel est inscrit au vault ; R48 : logging de session ; R71 : exit 0 seulement si
l'effet annoncé a eu lieu) — définitions dans ${CLAUDE_PLUGIN_ROOT}/references/regles.md.
Les 3 questions de toute automatisation
- A-t-elle tourné ? — dernière exécution, succès/échec, durée.
- Qu'a-t-elle produit ? — compteurs : N traités, N ignorés, N erreurs.
- Qu'est-ce qui a cloché ? — erreurs avec contexte (fichier, entrée, exception), pas juste un code.
Pratiques
- Log structuré : une ligne JSON par événement, horodatage ISO 8601 UTC, append-only, flush
immédiat. Squelette prêt à copier :
scripts/structlog_snippet.py(stdlib, ~60 lignes ;python3 structlog_snippet.py --demopour voir la sortie). - Événements minimaux :
run_start(paramètres),run_end(compteurs + durée),error(contexte + exception),skip(raison). Nommer les événements en snake_case stable — c'est ce qu'on grep. - Heartbeat pour les services longs : un événement
heartbeattoutes les N minutes, ou un fichier touché ; l'absence de heartbeat est l'alerte. - Vérifier un timer :
systemctl list-timers <unit>+journalctl -u <unit> --since today. Sous Windows : historique de la tâche planifiée + fichier de log. - Seuils d'alerte (peu, mais fermes) : run sans
run_end, log non mis à jour depuis > 2 périodes, compteurerreurs> 0, exit 0 sans effet (R71). - Enregistrer l'artefact (R34) : quoi, où, cadence, chemin du log, réversibilité — dans l'index des automatisations du vault.
Exemple avant / après — un timer de veille
Avant. veille.service lance veille.py ; le script fait print("ok") et rend 0. Deux jours
sans nouveau digest : impossible de dire si le timer n'a pas tiré, si le script a tourné sans rien
trouver, ou s'il a planté en silence.
Après.
from structlog_snippet import get_logger
log = get_logger("veille", logfile="veille.log")
log.info("run_start", sources=len(SOURCES))
for src in SOURCES:
try:
n = collecter(src)
log.info("source_ok", source=src.name, items=n)
except Exception as e:
log.error("source_err", source=src.name, exc=repr(e))
log.info("run_end", items=total, erreurs=nb_err, duree_s=round(t, 1))
sys.exit(0 if total > 0 or nb_err == 0 else 1) # R71 : 0 = effet réel
Lecture : tail -1 veille.log donne {"ts":"…","event":"run_end","items":0,"erreurs":3,…} → le
timer tire, le script tourne, trois sources échouent : la question 3 a sa réponse sans relancer.
Alerte : erreurs > 0 ou pas de run_end depuis 2 périodes.
Garde-fous
Pas de secret dans les logs (masquer tokens, mots de passe, en-têtes Authorization). Rotation ou
limite de taille. Local d'abord, zéro coût. Un log qui survit au crash (append immédiat, pas de
buffer).
Anti-patterns
« Ça doit marcher » sans preuve. Logs non datés ou non parsables. Un seul log fourre-tout. Alerter sur tout (bruit = silence).
Changelog
- 2026-09-07 — 1.1 : noms de scripts privés retirés ;
scripts/structlog_snippet.pyajouté (logger JSON stdlib,--demo) ; exemple avant/après sur un timer ; frontmatter normalisé. - 2026-07 — 1.0 : version initiale.