Logowanie i diagnostyka za pomocą go-mssqldb

Sterownik go-mssqldb umożliwia konfigurowalne logowanie do rozwiązywania problemów z połączeniem, zapytań oraz analizy wydajności. Ten artykuł opisuje dostępne flagi logów oraz sposób korzystania z niestandardowych loggerów.

Flagi logów

Użyj parametru połączenia log , aby włączyć wyjście diagnostyczne. Flagi logu to wartości masek bitowych, co oznacza, że można je ze sobą łączyć, dodając wartości liczb całkowitych:

Wartość flagi Kategoria Opis
1 Errors Loguj komunikaty o błędach.
2 Messages Rejestruj informacyjne wiadomości z serwera.
4 Wiersze Dane wiersza logu.
8 SQL Loguj wypowiedzi SQL wysyłane na serwer.
16 Parametry Loguj nazwy i wartości parametrów.
32 Transactions Rejestruj zdarzenia rozpoczęcia, zatwierdzania i cofania transakcji.
64 Debug Rejestruj szczegóły protokołu niskiego poziomu i TDS.
128 Ponowne próby Rejestruj próby ponowienia połączenia.

Flagi 4 (wiersze) i 16 (parametry) mogą ujawniać dane aplikacji, informacje poufne lub dane osobowe umożliwiające identyfikację osoby. Traktuj je jako krótkotrwałe sygnały diagnostyczne, a nie jako rutynowe ustawienia produkcyjne.

Examples

Rejestruj tylko błędy:

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

Błędy logów, instrukcje SQL i parametry:

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

Uwaga / Notatka

Wartość 25 oblicza się jako 1 + 8 + 16 (błędy + SQL + parametry).

Zapisuj wszystko:

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

Warning

Wysokie wartości flagi logarytmu (64, 128, 255) powodują rozwlekły wynik i mogą wpływać na wydajność. Używaj ich tylko do debugowania.

Domyślny rejestrator

Domyślnie sterownik zapisuje logi za pomocą standardowego pakietu log języka Go, który zapisuje do os.Stderr. Wyjście zawiera znaczniki czasu oraz kategorię logu:

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

Niestandardowy rejestrator z użyciem SetLogger

Użyj mssql.SetLogger, aby przekierować dane wyjściowe dziennika sterownika do niestandardowego rejestratora. Logger musi implementować interfejs mssql.Logger:

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
}

Logger świadomy kontekstu z SetContextLogger

Użyj mssql.SetContextLogger do zapewnienia loggera uwzględniającego kontekst. Rejestrator musi implementować interfejs mssql.ContextLogger, który udostępnia jedną metodę Log, przyjmującą kontekst, kategorię dziennika oraz ciąg znaków komunikatu. Takie podejście pozwala skorelować logi sterownika z danymi śledzenia powiązanymi z konkretnym żądaniem (na przykład identyfikatorami śledzenia):

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
}

Lista kontrolna diagnostyki

Podczas rozwiązywania problemów z połączeniem lub zapytaniem:

  1. Zacznij od log=1 dla błędów lub log=3 jeśli potrzebujesz też komunikatów serwerowych.
  2. Odtworzyć problem i przejrzeć tekst błędu przed włączeniem kolejnych kategorii.
  3. Dodaj 8, jeśli musisz potwierdzić, która instrukcja SQL lub które wywołanie procedury zostały wysłane.
  4. Dodaj 16 lub 4 tylko w bezpiecznym środowisku, w którym wartości parametrów i zwracane wiersze mogą być rejestrowane bez ujawniania danych wrażliwych.
  5. Jeśli problem wydaje się dotyczyć poziomu protokołu lub być związany z ponawianiem prób, zwiększ do log=64 lub dodaj 128 w celu diagnostyki ponawiania prób.
  6. Usuń lub ogranicz logowanie po rozwiązaniu problemu.

Logowanie strukturalne za pomocą log/slog

Go 1.21 wprowadziło log/slog do ustrukturyzowanego logowania. Używa się SetContextLogger do kierowania wyjścia sterownika przez 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.
}

Integracja z zerolog

zerolog to strukturalny logger o zerowej alokacji. Trasowanie logów kierowców przez 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})
}

Integracja z zap

zap to wysokowydajny strukturalny logger. Trasowanie dzienników kierowców przez 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})
}

Propagacja identyfikatora korelacji

W systemach rozproszonych propaguj identyfikator korelacji przez kontekst, aby logi sterowników mogły być skorelowane z żądaniem, które je wywołało:

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

Następnie przekażcie ID korelacji przez kontekst żądania:

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

Jak praktycznie używać identyfikatorów korelacji

Używaj identyfikatorów korelacji jako klucza do śledzenia w logach, ponowieniach i odwołaniach do bazy danych:

  1. Wygeneruj lub zaakceptuj identyfikator korelacji na granicy żądania.
  2. Przechowuj go w kontekście żądań i uwzględniaj w logach aplikacji i sterowników.
  3. Do rozwiązywania problemów po stronie SQL ustaw to w kontekście sesji za pomocą sp_set_session_context i odczytaj za pomocą SESSION_CONTEXT.

Identyfikatory korelacji nie są automatycznie przechowywane w tabelach SQL Server. SESSION_CONTEXT ma zakres sesji, więc wartości dotyczą tylko bieżącego połączenia i nie są zachowywane po zakończeniu sesji.

Jeśli potrzebujesz trwałej historii, zapisz identyfikator korelacji bezpośrednio w tabelach audytowych lub biznesowych (na przykład w tabeli AuditLog zawierającej correlation_id, znacznik czasu, operację i status).

Konfiguracja logowania produkcji

W produkcji należy umożliwić minimalne logowanie, aby uniknąć narzutu wydajnościowego i zapobiec pojawianiu się wrażliwych danych w logach:

Środowisko Zalecana log wartość Co przechwytuje
Development 63 (błędy + wiadomości + wiersze + SQL + parametry + transakcje) Pełna widoczność do debugowania.
Staging 3 (błędy + komunikaty) Błędy i komunikaty serwera bez szczegółów zapytań.
Produkcja 1 (błędy) lub 0 (wyłączone) Tylko błędy albo całkowicie wyłącz rejestrowanie sterownika.

Uwaga

Flaga Parameters (16) rejestruje rzeczywiste wartości parametrów, które mogą obejmować dane osobowe (PII), hasła lub inne wrażliwe dane. Nigdy nie włączaj tej flagi w produkcji. Pełną ocenę ryzyka każdej flagi można znaleźć w artykule o najlepszych praktykach bezpieczeństwa.

Wyłącz logowanie w środowisku produkcyjnym

Używaj zmiennych środowiskowych do kontrolowania poziomu logu na etapie wdrożenia:

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