Protokollierung und Diagnostik mit go-mssqldb

Der Treiber go-mssqldb bietet konfigurierbares Logging zur Fehlerbehebung von Verbindungsproblemen, Abfrageproblemen und Leistungsanalysen. Dieser Artikel beschreibt die verfügbaren Log-Flags und wie man benutzerdefinierte Logger verwendet.

Log-Kennzeichnungen

Verwenden Sie den Verbindungsparameter log , um die Diagnoseausgabe zu aktivieren. Log-Flags sind Bitmaskenwerte, das heißt, man kann sie kombinieren, indem man ihre ganzzahligen Werte addiert:

Flagwert Kategorie Description
1 Errors Fehlermeldungen protokollieren.
2 Messages Informationsmeldungen des Servers protokollieren.
4 Rows Daten einer Zeile protokollieren.
8 SQL Protokolliere SQL-Anweisungen, die an den Server gesendet werden.
16 Parameter Protokolliere Parameternamen und -werte.
32 Transaktionen Protokolliere Transaktionsstart-, Commit- und Rollback-Ereignisse.
64 Debug Protokolliere Low-Level-Protokoll- und TDS-Details.
128 Erneute Versuche Protokolliere Verbindungsversuche.

Flags 4 (Zeilen) und 16 (Parameter) können Anwendungsdaten, Geheimnisse oder personenbezogene Daten offenlegen. Behandle sie als kurzlebige Diagnosesignale, nicht als routinemäßige Produktionsumgebungen.

Beispiele

Nur Protokollfehler:

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

Logfehler, SQL-Anweisungen und Parameter:

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

Note

Der Wert 25 wird berechnet wie folgt 1 + 8 + 16 (Fehler + SQL + Parameter).

Protokolliere alles:

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

Warning

Hohe logaritmische Flag-Werte (64, 128, ) 255erzeugen umfangreiche Ausgaben und können die Leistung beeinträchtigen. Verwende sie nur zum Debuggen.

Standardprotokollierer

Standardmäßig loggt sich der Treiber in das Standardpaket log von Go ein, das dann in schreibt os.Stderr. Die Ausgabe enthält Zeitstempel und die Log-Kategorie:

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

Benutzerdefinierter Logger mit SetLogger

Nutze es mssql.SetLogger , um die Loggausgabe des Treibers an einen benutzerdefinierten Logger umzuleiten. Der Logger muss die mssql.Logger-Schnittstelle implementieren:

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
}

Kontextbewusster Logger mit SetContextLogger

Verwende mssql.SetContextLogger, um einen kontextbezogenen Logger bereitzustellen. Der Logger muss die Schnittstelle mssql.ContextLogger implementieren, die eine einzige Log Methode besitzt, die den Kontext empfängt, eine Log-Kategorie und eine Nachrichtenzeichenkette. Dieser Ansatz ermöglicht es Ihnen, Treiberprotokolle mit anfragebezogenen Tracingdaten (zum Beispiel Trace-IDs) zu korrelieren:

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
}

Diagnoseprüfliste

Beim Troubleshooting eines Verbindungs- oder Abfrageproblems:

  1. Beginne mit log=1 bei Fehlern oder mit log=3, wenn du auch Servermeldungen benötigst.
  2. Reproduzieren Sie das Problem und überprüfen Sie den Fehlertext, bevor Sie weitere Kategorien aktivieren.
  3. Fügen Sie hinzu, 8 wenn Sie bestätigen müssen, welche SQL-Anweisung oder Prozeduraufruf gesendet wurde.
  4. Fügen Sie nur 16 oder 4 in einer sicheren Umgebung hinzu, in der Parameterwerte und zurückgegebene Zeilen protokolliert werden können, ohne sensible Daten offenzulegen.
  5. Wenn das Problem auf Protokollebene zu liegen scheint oder mit Wiederholungsversuchen zusammenhängt, setzen Sie log=64 höher oder fügen Sie 128 für die Diagnose von Wiederholungsversuchen hinzu.
  6. Entferne oder reduziere das Loggen, nachdem das Problem behoben ist.

Strukturiertes Logging mit log/slog

Go 1.21 führte log/slog für strukturiertes Logging ein. Verwenden Sie SetContextLogger, um die Treiberausgabe durch slog zu leiten:

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.
}

Integration mit zerolog

Zerolog ist ein strukturierter Logger mit Null-Allokation. Protokolle des Routentreibers über 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})
}

Integration mit Zap

ZAP ist ein hochleistungsfähiger strukturierter Logger. Fahrerprotokolle der Route über 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})
}

Weitergabe der Korrelations-ID

In verteilten Systemen wird eine Korrelations-ID durch den Kontext verbreitet, sodass Treiberprotokolle mit der Anfrage korreliert werden können, die sie ausgelöst hat:

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)),
    )
}

Dann übergeben Sie die Korrelations-ID im Anforderungskontext:

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")
    // ...
}

Wie man Korrelations-IDs in der Praxis verwendet

Verwenden Sie Korrelations-IDs als Nachverfolgungsschlüssel über Logs, Retries und Datenbankaufrufe:

  1. Erstelle oder akzeptiere eine Korrelations-ID an der Anfragegrenze.
  2. Speichere es im Anforderungskontext und füge es in die Anwendungs- und Treiberlogs ein.
  3. Für SQL-seitige Fehlersuche setze es in den Session-Kontext mit sp_set_session_context und lies es mit SESSION_CONTEXT.

Korrelations-IDs werden nicht automatisch in SQL Server-Tabellen gespeichert. SESSION_CONTEXT ist session-scoped, sodass Werte nur auf die aktuelle Verbindung angewendet werden und nach Ablauf der Sitzung nicht bestehen bleiben.

Wenn Sie eine dauerhafte Historie benötigen, schreiben Sie die Korrelations-ID explizit in Ihre Audit- oder Geschäftstabellen (zum Beispiel eine AuditLog Tabelle mit correlation_id, Zeitstempel, Betrieb und Status).

Konfiguration der Produktionsprotokollierung

In der Produktion sollten Sie minimale Protokollierung aktivieren, um Leistungsüberkopf zu vermeiden und zu verhindern, dass sensible Daten in Logs erscheinen:

Umgebung Empfohlener Wert log Was er erfasst
Entwicklung 63 (Fehler + Nachrichten + Zeilen + SQL + Params + Transaktionen) Volle Übersicht zum Debuggen.
Staging 3 (Fehler + Nachrichten) Fehler und Servernachrichten ohne Abfragedetails.
Produktion 1 (Fehler) oder 0 (aus) Nur Fehler oder die Treiberprotokollierung vollständig deaktivieren.

Achtung

Das Flag Parameters (16) protokolliert tatsächliche Parameterwerte, die personenbezogene Daten (PII), Passwörter oder andere sensible Daten umfassen können. Aktiviere diese Flagge niemals in der Produktion. Für eine vollständige Risikobewertung jeder Flagge siehe Security Best Practices.

Protokollierung in der Produktion unterdrücken

Verwenden Sie Umweltvariablen, um die Log-Ebene pro Bereitstellungsstufe zu steuern:

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