J’abordes ici une pratique très professionnelle pour intégrer dbt à un système de monitoring ou observabilité externe, tout en assurant fiabilité, scalabilité, et non-blocage.
🎯 Objectif
Tu veux suivre l’activité dbt (succès, échecs, durées, modèles exécutés…) dans un système externe :
- Datadog
- Prometheus / Grafana
- Sentry
- ELK stack
- ou même ton propre backend ou pipeline Kafka
Mais tu veux le faire fiablement, même si ce système externe tombe, sans bloquer dbt run
.
✅ Solution :
Combiner un hook
on-run-end
avec une file de messages (message queue)
→ Pour capturer les événements dbt asynchrone et sans perte.
🧱 Architecture visuelle
dbt run
|
┌───────▼────────┐
│ on-run-end hook│
└───────┬────────┘
▼
┌────────────────────┐
│ Message Queue (MQ) │ ⟶ Kafka / RabbitMQ / SQS / PubSub / Redis
└────────────────────┘
▼
┌────────────────────┐
│ Monitoring Consumer│ ⟶ Insertion dans Datadog, logs, alertes...
└────────────────────┘
⚙️ Étapes détaillées
1. Utiliser un hook dbt : on-run-end
Dans dbt_project.yml
:
on-run-end:
- "{{ log_run_summary() }}"
Mais ici, on veut faire plus que logger : on veut envoyer un message structuré à une file (queue).
2. Créer une macro log_run_summary
Exemple simple :
-- macros/log_run_summary.sql
{% macro log_run_summary() %}
{% set summary = {
"event": "dbt_run_end",
"project": target.name,
"env": target.schema,
"user": target.user,
"run_started_at": run_started_at,
"run_completed_at": modules.datetime.now().isoformat(),
"status": invocation_status,
"elapsed": run_elapsed_time
} %}
-- Simulation : log json localement
{% do log(tojson(summary), info=True) %}
-- Envoi vers la file de messages (si activé)
{% if var('enable_event_queue', false) %}
{% set json = tojson(summary) %}
{% do run_query("call push_to_queue('" ~ json ~ "')") %}
{% endif %}
{% endmacro %}
3. Tu relies cela à une UDF SQL ou fonction externe push_to_queue
- Ça peut être un
HTTP UDF
dans BigQuery - Ou un wrapper sur un
pubsub
/sqs
/kafka
dans Databricks/Snowflake - Ou un service REST qui accepte des POST depuis un
curl
déclenché par dbt
4. Pourquoi une file de messages ?
Sans MQ | Avec MQ |
---|---|
En cas d’erreur réseau → événement perdu | Bufferisé, il sera traité quand dispo |
Bloque potentiellement dbt run | Ne bloque pas → asynchrone |
Pas de rétention / redelivery | MQ gère la durabilité et la reprise |
Dépendance forte à l’externe | MQ isole dbt des outils downstream |
🧪 Exemple d’event JSON structuré
{
"event": "dbt_run_end",
"project": "mon_projet",
"status": "success",
"elapsed": 132.54,
"models_run": 42,
"env": "prod",
"timestamp": "2025-06-16T09:00:12Z"
}
🧼 Avantages de cette approche
Bénéfice | Détail |
---|---|
🔄 Résilient | Les événements ne sont pas perdus même si le système externe plante |
📤 Asynchrone | Le dbt run n’attend pas l’upload — il pousse dans une file |
🧘 Non-bloquant | Pas d’impact sur la durée ou stabilité du dbt run |
🔎 Observabilité claire | Tu peux tracer par modèle, par projet, par environnement |
📁 Standard industriel | MQ est une brique clé dans toute architecture data fiable |