Journalisation et diagnostic avec go-mssqldb

Le pilote go-mssqldb propose une journalisation configurable pour le diagnostic des problèmes de connexion, des problèmes de requête et pour l’analyse des performances. Cet article décrit les indicateurs de journalisation disponibles et comment utiliser des enregistreurs personnalisés.

Indicateurs de journalisation

Utilisez le log paramètre de connexion pour activer la sortie de diagnostic. Les indices logaritaires sont des valeurs de bitmask, ce qui signifie que vous pouvez les combiner en additionnant leurs valeurs entières :

Valeur de repère Category Description
1 Errors Journal des messages d’erreur.
2 Messages Enregistrez les messages d’information depuis le serveur.
4 Rows Consigner les données de la ligne.
8 SQL Enregistrez les instructions SQL envoyées au serveur.
16 Paramètres Noms et valeurs de paramètres de log.
32 Transactions Enregistrez les événements de démarrage, de validation et de retour en arrière des transactions.
64 Debug Enregistrez les détails des protocoles bas niveau et du TDS.
128 Nouvelle tentatives Log des tentatives de réouverture de connexion.

Les drapeaux 4 (lignes) et 16 (paramètres) peuvent exposer des données d’application, des secrets ou des informations personnelles identifiables. Traitez-les comme des signaux diagnostiques éphémères, pas comme des paramètres de production routiniers.

Exemples

Consigner uniquement les erreurs :

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=1

Erreurs de journal, instructions SQL et paramètres :

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=25

Note

La valeur 25 est calculée comme 1 + 8 + 16 (erreurs + SQL + paramètres).

Enregistrez tout :

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=255

Avertissement

Les valeurs élevées de drapeaux logaritarithiques (64, 128, 255) produisent une sortie verbeuse et peuvent affecter la performance. Utilisez-les uniquement pour le débogage.

Enregistreur par défaut

Par défaut, le pilote se connecte au package standard log de Go, qui écrit sur os.Stderr. La sortie inclut les horodatages et la catégorie de journal :

2026/03/28 10:15:30 mssql: login successful
2026/03/28 10:15:30 mssql: SQL: SELECT 1

Journaliseur personnalisé avec SetLogger

Utilisez mssql.SetLogger pour rediriger la sortie de journalisation du pilote vers un journaliseur personnalisé. Le logger doit implémenter l’interface mssql.Logger suivante :

import "github.com/microsoft/go-mssqldb"

type myLogger struct{}

func (l *myLogger) Printf(format string, v ...interface{}) {
    // Write to your preferred logging system
    fmt.Printf("[MSSQL] "+format+"\n", v...)
}

func (l *myLogger) Println(v ...interface{}) {
    fmt.Println(append([]interface{}{"[MSSQL]"}, v...)...)
}

func main() {
    mssql.SetLogger(&myLogger{})
    // ... open connection
}

Journaliseur sensible au contexte avec SetContextLogger

Utilisez mssql.SetContextLogger pour fournir un journaliseur tenant compte du contexte. Le logger doit implémenter l’interface mssql.ContextLogger , qui possède une seule Log méthode recevant le contexte, une catégorie de journal et une chaîne de messages. Cette approche permet de corréler les journaux des pilotes avec les données de traçabilité à portée de requête (par exemple, les identifiants de trace) :

import (
    "context"
    "log/slog"
    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
)

type contextLogger struct{}

func (l *contextLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    slog.InfoContext(ctx, msg, "category", category)
}

func main() {
    mssql.SetContextLogger(&contextLogger{})
    // ... open connection
}

Liste de contrôle de diagnostic

Lors du dépannage d’un problème de connexion ou de requête :

  1. Commencez par log=1 pour les erreurs, ou log=3 si vous avez aussi besoin de messages serveur.
  2. Reproduisez le problème et relisez le texte d’erreur avant d’activer d’autres catégories.
  3. Ajoutez 8 si vous devez confirmer quelle instruction SQL ou appel de procédure a été envoyé.
  4. Ajouter 16 ou 4 uniquement dans un environnement sûr où les valeurs des paramètres et les lignes retournées peuvent être enregistrées sans exposer de données sensibles.
  5. Si le problème semble être de niveau protocolaire ou lié aux nouvelles tentatives, augmentez log=64 ou ajoutez 128 pour obtenir des diagnostics sur les nouvelles tentatives.
  6. Supprimez ou réduisez la journalisation une fois le problème résolu.

Journalisation structurée avec log/slog

Go 1.21 introduit log/slog pour la journalisation structurée. Utilisez SetContextLogger pour faire passer la sortie du pilote par slog :

import (
    "context"
    "log/slog"
    "os"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
)

type slogLogger struct {
    logger *slog.Logger
}

func (l *slogLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    level := slog.LevelInfo
    if category == msdsn.LogErrors {
        level = slog.LevelError
    }
    if category == msdsn.LogDebug {
        level = slog.LevelDebug
    }

    l.logger.LogAttrs(ctx, level, msg,
        slog.Int("category", int(category)),
    )
}

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))

    mssql.SetContextLogger(&slogLogger{logger: logger})

    // Connection logging now produces structured JSON.
}

Intégration avec zerolog

Zerolog est un logger structuré à allocation zéro. Acheminer les journaux du pilote avec zerolog :

import (
    "context"
    "os"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
    "github.com/rs/zerolog"
)

type zerologAdapter struct {
    logger zerolog.Logger
}

func (l *zerologAdapter) Log(ctx context.Context, category msdsn.Log, msg string) {
    event := l.logger.Info()
    if category == msdsn.LogErrors {
        event = l.logger.Error()
    }
    event.Int("category", int(category)).Msg(msg)
}

func main() {
    logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
    mssql.SetContextLogger(&zerologAdapter{logger: logger})
}

Intégration avec zap

ZAP est un enregistreur structuré haute performance. Le conducteur de la route enregistre les registres via zap :

import (
    "context"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
    "go.uber.org/zap"
)

type zapAdapter struct {
    logger *zap.Logger
}

func (l *zapAdapter) Log(ctx context.Context, category msdsn.Log, msg string) {
    if category == msdsn.LogErrors {
        l.logger.Error(msg, zap.Int("category", int(category)))
    } else {
        l.logger.Info(msg, zap.Int("category", int(category)))
    }
}

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()

    mssql.SetContextLogger(&zapAdapter{logger: logger})
}

Propagation de l’identifiant de corrélation

Dans les systèmes distribués, propagez un identifiant de corrélation dans le contexte afin que les journaux de pilotes puissent être corrélés à la requête qui les a déclenchés :

type correlationKey struct{}

func WithCorrelationID(ctx context.Context, id string) context.Context {
    return context.WithValue(ctx, correlationKey{}, id)
}

func CorrelationID(ctx context.Context) string {
    if id, ok := ctx.Value(correlationKey{}).(string); ok {
        return id
    }
    return "unknown"
}

type correlatedLogger struct {
    logger *slog.Logger
}

func (l *correlatedLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    l.logger.LogAttrs(ctx, slog.LevelInfo, msg,
        slog.String("correlation_id", CorrelationID(ctx)),
        slog.Int("category", int(category)),
    )
}

Ensuite, faites passer l’ID de corrélation dans le contexte de votre requête :

func handleRequest(w http.ResponseWriter, r *http.Request) {
    correlationID := r.Header.Get("X-Correlation-ID")
    if correlationID == "" {
        correlationID = uuid.NewString()
    }

    ctx := WithCorrelationID(r.Context(), correlationID)

    // All database operations using this context will include the correlation ID.
    rows, err := db.QueryContext(ctx, "SELECT TOP (10) ProductID, Name FROM Production.Product")
    // ...
}

Comment utiliser les IDs de corrélation en pratique

Utilisez les identifiants de corrélation comme clé de traçage à travers les journaux, les tentatives et les appels à la base de données :

  1. Générer ou accepter un identifiant de corrélation à la frontière de la requête.
  2. Stockez-le dans le contexte de la requête et incluez-le dans les journaux d’application et de pilotes.
  3. Pour le dépannage côté SQL, mettez-le dans le contexte de session avec sp_set_session_context et lisez-le avec SESSION_CONTEXT.

Les identifiants de corrélation ne sont pas automatiquement stockés dans les tables SQL Server. SESSION_CONTEXT est à portée de session, donc les valeurs ne s’appliquent qu’à la connexion actuelle et ne persistent pas après la fin de la session.

Si vous avez besoin d’un historique durable, écrivez explicitement l’ID de corrélation dans vos tables d’audit ou d’entreprise (par exemple, une AuditLog table avec correlation_id, horodatage, opération et statut).

Configuration de la journalisation de production

En production, activez un minimum de journalisation pour éviter la surcharge de performance et empêcher l’apparition de données sensibles dans les journaux :

Environnement Valeur recommandée log Ce qu’il capture
Development 63 (erreurs + messages + lignes + SQL + paramètres + transactions) Visibilité totale pour le débogage.
Staging 3 (erreurs + messages) Erreurs et messages serveur sans détails de requête.
production 1 (erreurs) ou 0 (désactivé) Erreurs uniquement, ou désactiver complètement la journalisation des pilotes.

Attention

Le drapeau Parameters (16) enregistre les valeurs réelles des paramètres, qui peuvent inclure des informations personnelles identifiables (PII), des mots de passe ou d’autres données sensibles. N’activez jamais cette option en production. Pour une évaluation complète des risques de chaque drapeau, consultez les meilleures pratiques en matière de sécurité.

Supprimer la journalisation en production

Utilisez des variables d’environnement pour contrôler le niveau de journalisation par environnement de déploiement :

if os.Getenv("APP_ENV") == "production" {
    // Use only error-level logging in production.
    connString += "&log=1"
} else {
    // Full logging in development.
    connString += "&log=63"
}