Registo e diagnóstico com go-mssqldb

O go-mssqldb driver fornece registos configuráveis para resolver problemas de ligação, problemas de consulta e análise de desempenho. Este artigo descreve os indicadores de registo disponíveis e como utilizar registadores personalizados.

Sinalizadores de registo

Use o log parâmetro de ligação para ativar a saída de diagnóstico. As flags de registo são valores de máscara de bits, o que significa que pode combiná-las somando os respetivos valores inteiros:

Valor da bandeira Categoria Description
1 Errors Registar mensagens de erro.
2 Messages Registar mensagens de informação do servidor.
4 Rows Registar os dados da linha.
8 SQL Registar as instruções SQL enviadas ao servidor.
16 Parâmetros Registe os nomes e os valores dos parâmetros.
32 Transactions Regista os eventos de início, confirmação e rollback das transações.
64 Debug Registar detalhes do protocolo de baixo nível e do TDS.
128 Reintentos Registar tentativas de repetição da ligação.

Sinalizadores 4 (linhas) e 16 (parâmetros) podem expor dados da aplicação, segredos ou informações de identificação pessoal. Trata-os como sinais de diagnóstico de curta duração, não como configurações de produção rotineiras.

Examples

Apenas erros de log:

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

Erros de log, instruções SQL e parâmetros:

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

Note

O valor 25 é calculado como 1 + 8 + 16 (erros + SQL + parâmetros).

Registe tudo:

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

Warning

Valores elevados de sinalização logarítmica (64, 128, 255) produzem uma saída verbosa e podem afetar o desempenho. Usa-os apenas para depuração.

Registador predefinido

Por defeito, o driver regista-se no pacote padrão log do Go, que escreve em os.Stderr. A saída inclui marcas temporais e a categoria de registo:

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

Logger personalizado com SetLogger

Use mssql.SetLogger para redirecionar a saída de registo do controlador para um registador personalizado. O registador deve implementar a interface 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
}

Registador sensível ao contexto com SetContextLogger

Utilize mssql.SetContextLogger para fornecer um logger sensível ao contexto. O logger deve implementar a mssql.ContextLogger interface, que tem um único Log método que recebe o contexto, uma categoria de logs e uma cadeia de mensagens. Esta abordagem permite correlacionar registos do controlador com dados de rastreio associados ao pedido (por exemplo, IDs de rastreio):

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 de verificação diagnóstica

Ao resolver um problema de ligação ou consulta:

  1. Comece por log=1 para erros, ou log=3 se também precisar de mensagens do servidor.
  2. Reproduza o problema e reveja o texto do erro antes de ativar mais categorias.
  3. Adiciona 8 se precisares de confirmar qual instrução SQL ou chamada de procedimento foi enviada.
  4. Adicionar 16 ou 4 apenas num ambiente seguro onde os valores dos parâmetros e as linhas retornadas possam ser registados sem expor dados sensíveis.
  5. Se o problema parecer estar relacionado com o protocolo ou com novas tentativas, aumente o valor para log=64 ou adicione 128 para diagnóstico de novas tentativas.
  6. Remova ou reduza o registo depois de o problema estar resolvido.

Registo estruturado com log/slog

O Go 1.21 introduziu log/slog para registo estruturado. Utilize SetContextLogger para encaminhar a saída do controlador através de 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.
}

Integração com zerolog

Zerolog é um logger estruturado de alocação zero. Encaminhar os registos do controlador através do 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})
}

Integração com ZAP

O ZAP é um logger estruturado de alto desempenho. O motorista de rota inicia sessão através do 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})
}

Propagação do ID de correlação

Em sistemas distribuídos, propaga um ID de correlação através do contexto para que os registos de drivers possam ser correlacionados com o pedido que os desencadeou:

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

Depois passa o ID de correlação pelo contexto do teu pedido:

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

Como usar IDs de correlação na prática

Utilize IDs de correlação como chave de rastreio em logs, novas tentativas e chamadas à base de dados:

  1. Gerar ou aceitar um ID de correlação na fronteira do pedido.
  2. Guarde-a no contexto do pedido e inclua-a nos registos da aplicação e do controlador.
  3. Para resolução de problemas do lado SQL, defina-o no contexto da sessão com sp_set_session_context e leia-o com SESSION_CONTEXT.

Os IDs de correlação não são armazenados automaticamente em tabelas do SQL Server. SESSION_CONTEXT é com âmbito de sessão, por isso os valores aplicam-se apenas à ligação atual e não persistem após o fim da sessão.

Se precisar de histórico duradouro, escreva explicitamente o ID de correlação nas suas tabelas de auditoria ou de negócio (por exemplo, uma AuditLog tabela com correlation_id, carimbo temporal, operação e estado).

Configuração do registo de produção

Em produção, permita o mínimo registo para evitar sobrecarga de desempenho e evitar que dados sensíveis apareçam nos registos:

Meio Ambiente Valor recomendado log O que capta
Development 63 (erros + mensagens + linhas + SQL + parâmetros + transações) Visibilidade total para depuração.
Staging 3 (erros + mensagens) Erros e mensagens do servidor sem detalhes de consulta.
Produção 1 (erros) ou 0 (desativado) Apenas erros, ou desativar completamente o registo de controladores.

Caution

A Parameters flag (16) regista os valores reais dos parâmetros, que podem incluir informações pessoais identificáveis (PII), palavras-passe ou outros dados sensíveis. Nunca ative esta opção em produção. Para uma avaliação completa de risco de cada bandeira, consulte as melhores práticas de segurança.

Suprimir registos em produção

Utilize variáveis de ambiente para controlar o nível de registo para cada fase de implementação:

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