Retour à l'accueil

Observabilité en Go : QueryTracer, metrics et OTel

L'article compare les outils d'observabilité en Go : QueryTracer, metrics et OpenTelemetry. Explique les différences d'application et les erreurs typiques. Fournit des exemples de code et des recommandations de sélection de stack pour différents scénarios.

QueryTracer vs OpenTelemetry : comment choisir des outils d'observabilité en Go
Advertisement 728x90

# Observabilité en Go : Choisir entre QueryTracer, métriques et OpenTelemetry

Lors de l'intégration de PostgreSQL dans des applications Go, les développeurs se retrouvent souvent face à un choix d'outils d'observabilité. Les métriques, QueryTracer et OpenTelemetry résolvent des problèmes différents, mais il est facile de les confondre. Décomposons quand utiliser chacun et comment éviter les pièges courants, en nous basant sur un cas réel impliquant un décorateur et des transactions.

Trois niveaux d'observabilité : Métriques, traçage et contexte

Les métriques représentent le niveau de base de l'observabilité. Elles répondent à la question « que se passe-t-il ? » : par exemple, une pointe d'erreurs après un déploiement ou le temps de réponse p99 d'un point de terminaison qui passe de 50 ms à 800 ms. La collecte de métriques est peu coûteuse en ressources et permet une détection rapide des anomalies. Cependant, les métriques manquent de contexte : elles indiquent qu'une requête a ralenti mais n'expliquent pas pourquoi. Le traçage est nécessaire pour y répondre.

QueryTracer est une interface fournie par le pilote pgx pour intercepter les opérations au niveau du pilote. Elle génère des événements distincts pour chaque étape de la transaction : BEGIN, la requête elle-même et COMMIT. Ce n'est pas un outil de collecte de métriques mais un crochet où vous pouvez insérer de la logique pour créer des spans, journaliser le SQL ou collecter des métriques. Important : QueryTracer lui-même n'envoie pas de données — il fournit simplement un mécanisme pour les générer.

Google AdInline article slot

OpenTelemetry (OTel) gère le traçage distribué. Un span est créé au niveau du gestionnaire de requêtes HTTP et transmis via le contexte (context.Context) dans la pile : service, dépôt, pilote. Chaque couche ajoute un span enfant, formant un arbre d'exécution. Dans un système de visualisation (par ex., Jaeger), vous voyez l'image complète : quelle requête DB spécifique était lente et dans quel contexte. OTel s'exécute en continu mais utilise l'échantillonnage pour réduire la surcharge : par exemple, seulement 10 % des requêtes sont tracées. Si une requête est échantillonnée, l'arbre complet des spans est enregistré.

Exemple de décorateur : Là où se cache le problème

Dans les projets typiques, un décorateur sur le dépôt est utilisé pour la collecte de métriques. Voici un exemple d'implémentation :

func WithDBMetrics(operation string, fn func() (any, error)) (any, error) {
    start := time.Now()
    result, err := fn()
    seconds := time.Since(start).Seconds()

    metrics.ObserveDatabaseQueryDuration(operation, seconds)
    if err != nil {
        metrics.IncDatabaseErrors(operation)
        return result, CheckDBError(operation, err)
    }

    return result, err
}

Pour les méthodes qui exécutent une seule requête SQL, le décorateur fonctionne avec précision : la métrique correspond au temps d'exécution de la requête. Cependant, dans les méthodes avec transactions, les choses changent. Considérons cet exemple :

Google AdInline article slot
func (r *BookingRepo) CreateBooking(ctx context.Context, b *models.Booking) (int64, error) {
    const operation = "create_booking"

    return WithDBMetrics(operation, func() (int64, error) {
        if err := r.validateBooking(b); err != nil {
            return 0, err
        }

        tx, err := r.db.BeginTxx(ctx, nil)
        if err != nil {
            return 0, err
        }
        defer func() { _ = tx.Rollback() }()

        var id int64
        err = tx.QueryRowContext(ctx, createBookingAtomicQuery, ...).Scan(&id)

        if err != nil {
            if errors.Is(err, sql.ErrNoRows) {
                return 0, models.ErrSlotOccupied
            }
            return 0, err
        }

        if err = tx.Commit(); err != nil {
            return 0, err
        }

        return id, nil
    })
}

Le décorateur mesure le temps de l'ensemble du bloc, y compris la validation, le démarrage de la transaction, l'exécution de la requête et le commit. La métrique create_booking = 45ms ne révèle pas quelle étape prend 40 ms. Pendant ce temps, QueryTracer décompose la transaction en trois événements distincts :

  • BEGIN = 0.1ms
  • INSERT = 40ms
  • COMMIT = 2ms

Cette granularité est cruciale pour diagnostiquer les goulots d'étranglement de performance.

Implémentation de QueryTracer : Interfaces et pièges

QueryTracer est composé d'interfaces dans pgx, chacune pour un type d'opération spécifique. Par exemple, BatchTracer est utilisé pour les opérations Batch :

Google AdInline article slot
type BatchTracer interface {
    TraceBatchStart(ctx context.Context, conn *Conn, data TraceBatchStartData) context.Context
    TraceBatchQuery(ctx context.Context, conn *Conn, data TraceBatchQueryData)
    TraceBatchEnd(ctx context.Context, conn *Conn, data TraceBatchEndData)
}

L'implémentation nécessite de créer une struct qui satisfait ces interfaces :

type MyTracer struct{}

func (t *MyTracer) TraceQueryStart(ctx context.Context, conn *Conn, data TraceQueryStartData) context.Context {
    // Début de requête : créer un span ou journaliser
    return ctx
}

func (t *MyTracer) TraceQueryEnd(ctx context.Context, conn *Conn, data TraceQueryEndData) {
    // Fin de requête : enregistrer le temps, envoyer la métrique
}

// Attacher à la config
config.ConnConfig.Tracer = &MyTracer{}

Principaux pièges :

  • Si vous n'implémentez pas BatchTracer, les opérations Batch passeront inaperçues. Aucune erreur ne se produit, mais les données sont perdues.
  • Pour l'intégration OTel, créez manuellement des spans dans les méthodes QueryTracer et transmettez correctement le contexte.
  • QueryTracer ne remplace pas les métriques : une logique supplémentaire est nécessaire pour l'agrégation.

Comparaison des piles : sqlx, pgx et solutions personnalisées

Le choix d'outil dépend de la pile actuelle et des exigences de détail. Voici trois options :

  • sqlx + otelsql

- Configuration : Une ligne de code.

- Avantages : Pas besoin de changer de pilote, support de traçage de base.

- Inconvénients : Pas de support pour Batch ou CopyFrom, pas de logique personnalisée.

- Recommandation : Bon pour les projets simples sans opérations DB complexes.

  • pgx + otelpgx

- Configuration : Une ligne de code.

- Avantages : Support complet de Batch et CopyFrom, création automatique de spans.

- Inconvénients : Nécessite de passer de sqlx à pgx.

- Recommandation : Meilleur pour les nouveaux projets ou les migrations.

  • pgx + MyTracer

- Configuration : Implémentation manuelle d'interfaces.

- Avantages : Contrôle total (métriques personnalisées, journalisation avancée).

- Inconvénients : Complexité élevée, maintenance requise.

- Recommandation : Uniquement pour des besoins spécifiques non couverts par otelpgx.

Important : Dans tous les cas, le contexte (context.Context) doit être transmis à travers toutes les couches de l'application — du gestionnaire au pilote. Sinon, les spans deviennent de niveau racine et perdent leurs liens avec la requête utilisateur originale.

Points clés

  • Les métriques montrent le problème mais pas la cause. Utilisez-les pour la surveillance, pas pour des diagnostics approfondis.
  • QueryTracer est un crochet de traçage, pas pour les métriques. Il décompose les transactions en étapes pour une analyse détaillée.
  • OpenTelemetry nécessite une propagation correcte du contexte. Assurez-vous que context.Context circule à travers toutes les couches de l'application.
  • Les décorateurs fonctionnent pour des métriques simples mais pas pour les transactions. Utilisez QueryTracer ou OTel pour les diagnostics intra-transaction.
  • Choix de pile : Restez avec otelsql si vous utilisez sqlx sans opérations complexes. Passez à pgx + otelpgx pour une visibilité complète.

— Editorial Team

Advertisement 728x90

Lire ensuite