Registro y diagnóstico con go-mssqldb

El go-mssqldb controlador proporciona registros configurables para solucionar problemas de conexión, consultas y análisis de rendimiento. Este artículo describe las banderas de registro disponibles y cómo utilizar registradores personalizados.

Banderas de registro

Utiliza el log parámetro de conexión para habilitar la salida de diagnóstico. Las banderas de registro son valores de máscara de bits, lo que significa que puedes combinarlas sumando sus valores enteros:

Valor de marca Category Description
1 Errors Registrar mensajes de error.
2 Messages Registra mensajes informativos del servidor.
4 Rows Registrar los datos de la fila.
8 SQL Registra las sentencias SQL enviadas al servidor.
16 Parámetros Nombres y valores de parámetros de log.
32 Transactions Registra los eventos de inicio, confirmación y reversión de la transacción.
64 Debug Registra los detalles de bajo nivel del protocolo y de TDS.
128 Reintentos Registrar los intentos de reconexión.

Las banderas 4 (filas) y 16 (parámetros) pueden exponer datos de la aplicación, secretos o información personal identificable. Trátalos como señales diagnósticas de corta duración, no como configuraciones rutinarias de producción.

Examples

Solo errores de registro:

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

Errores de registro, sentencias SQL y parámetros:

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

Note

El valor 25 se calcula como 1 + 8 + 16 (errores + SQL + parámetros).

Registra todo:

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

Warning

Los valores altos del indicador de registro (64, 128, 255) producen una salida detallada y pueden afectar al rendimiento. Úsalas solo para depuración.

Registrador predeterminado

Por defecto, el controlador envía los registros al paquete estándar log de Go, que escribe en os.Stderr. La salida incluye marcas temporales y la categoría de registro:

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

Registrador personalizado con SetLogger

Úsala mssql.SetLogger para redirigir la salida del log del controlador a un logger personalizado. El registrador debe implementar la mssql.Logger interfaz:

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
}

Registro con reconocimiento del contexto con SetContextLogger

Use mssql.SetContextLogger para proporcionar un registrador con conocimiento del contexto. El registrador debe implementar la mssql.ContextLogger interfaz, que tiene un único Log método que recibe el contexto, una categoría de registro y una cadena de mensajes. Este enfoque permite correlacionar los registros del controlador con datos de trazabilidad con alcance de solicitud (por ejemplo, identificadores de traza):

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 comprobación de diagnóstico

Al solucionar un problema de conexión o consulta:

  1. Empieza con log=1 para los errores, o con log=3 si también necesitas mensajes del servidor.
  2. Reproduce el problema y revisa el texto de error antes de habilitar más categorías.
  3. Añade 8 si necesitas confirmar qué instrucción SQL o llamada a procedimiento se envió.
  4. Añadir 16 o 4 solo en un entorno seguro donde los valores de los parámetros y las filas devueltas puedan registrarse sin exponer datos sensibles.
  5. Si el problema parece ser de nivel de protocolo o estar relacionado con los reintentos, aumenta a log=64 o añade 128 para diagnosticar los reintentos.
  6. Elimina o reduce el registro una vez que se resuelva el problema.

Registro estructurado con log/slog

Go 1.21 introdujo log/slog para el registro estructurado. Úsase SetContextLogger para enrutar la salida del controlador a travé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.
}

Integración con zerolog

Zerolog es un registrador estructurado de asignación cero. Registros de conductores de ruta a través de 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})
}

Integración con zap

ZAP es un registrador estructurado de alto rendimiento. Registros del conductor de ruta mediante 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})
}

Propagación de ID de correlación

En sistemas distribuidos, propaga un ID de correlación a través del contexto para que los registros de controladores puedan correlacionarse con la solicitud que los activó:

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

Luego pasa el ID de correlación por el contexto de tu solicitud:

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

Cómo usar los IDs de correlación en la práctica

Utiliza los IDs de correlación como clave de trazado a través de registros, reintentos y llamadas a bases de datos:

  1. Generar o aceptar un ID de correlación en el límite de la solicitud.
  2. Guárdalo en el contexto de la solicitud e inclúyelo en los registros de aplicaciones y controladores.
  3. Para la resolución de problemas en SQL, ponlo en contexto de sesión con sp_set_session_context y léelo con SESSION_CONTEXT.

Los IDs de correlación no se almacenan automáticamente en tablas de SQL Server. SESSION_CONTEXT tiene alcance de sesión, por lo que los valores solo se aplican a la conexión actual y no persisten después de que la sesión termina.

Si necesitas un historial persistente, guarda explícitamente el ID de correlación en tus tablas de auditoría o de negocio (por ejemplo, una tabla AuditLog con correlation_id, marca de tiempo, operación y estado).

Configuración de registro de producción

En producción, permite un registro mínimo para evitar sobrecarga de rendimiento y evitar que datos sensibles aparezcan en los registros:

Medio ambiente Valor recomendado log Lo que captura
Development 63 (errores + mensajes + filas + SQL + parámetros + transacciones) Visibilidad total para depuración.
Staging 3 (errores + mensajes) Errores y mensajes del servidor sin detalles de consulta.
Producción 1 (errores) o 0 (apagado) Solo errores, o desactivar por completo el registro del controlador.

Precaución

La Parameters bandera (16) registra los valores reales de los parámetros, que pueden incluir información personal identificable (PII), contraseñas u otros datos sensibles. Nunca habilites este indicador en producción. Para una evaluación completa de riesgos de cada bandera, consulte las mejores prácticas de seguridad.

Desactivar el registro en producción

Utiliza variables de entorno para controlar el nivel de registro para cada etapa de despliegue:

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