go-mssqldb로 로깅 및 진단

이 드라이버는 go-mssqldb 연결 문제 해결, 쿼리 문제 및 성능 분석을 위한 구성 가능한 로깅을 제공합니다. 이 글에서는 사용 가능한 로그 플래그와 맞춤형 로거 사용 방법을 설명합니다.

로그 플래그

연결 매개변수를 log 사용해 진단 출력을 활성화하세요. 로그 플래그는 비트마스크 값으로, 정수 값을 더해 결합할 수 있습니다:

플래그 값 Category Description
1 Errors 오류 메시지를 기록하세요.
2 Messages 서버에서 정보 메시지를 기록하세요.
4 Rows 행 데이터를 기록하세요.
8 SQL 서버에 전송된 SQL 문장을 기록하세요.
16 매개 변수 기록하는 매개변수 이름과 값.
32 Transactions 트랜잭션 시작, 커밋, 롤백 이벤트를 기록하세요.
64 Debug 저수준 프로토콜과 TDS 세부 사항을 기록하세요.
128 재시도 연결 재시도 기록.

플래그 4 (행) 및 16 (매개변수)는 애플리케이션 데이터, 기밀 정보 또는 개인 식별 정보를 노출할 수 있습니다. 이들을 일상적인 생산 설정이 아니라 단기간 진단 신호로 취급하세요.

예제

로그 오류만 발생:

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

로그 오류, SQL 문장 및 매개변수:

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

비고

25은(는) 1 + 8 + 16(errors + SQL + 매개변수)로 계산됩니다.

모든 기록:

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

Warning

로그 플래그 값(64, 128, 255)이 높으면 과장된 출력이 발생하여 성능에 영향을 줄 수 있습니다. 디버깅에만 사용하세요.

기본 로거

기본적으로 드라이버는 Go의 표준 log 패키지에 로그를 기록하고, 이는 os.Stderr에 기록합니다. 출력에는 타임스탬프와 로그 카테고리가 포함됩니다:

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

SetLogger를 이용한 커스텀 로거

드라이버 로그 출력을 커스텀 로거로 리디렉션하는 데 사용 mssql.SetLogger 하세요. 로거는 다음 인터페이스를 구현 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
}

SetContextLogger를 이용한 컨텍스트 인식 로거

mssql.SetContextLogger를 사용하여 컨텍스트 인식 로거를 제공하세요. 로거는 컨텍스트, 로그 카테고리, 메시지 문자열을 받는 단일 mssql.ContextLogger 메서드로 구성된 인터페이스를 구현 Log 해야 합니다. 이 접근법은 운전자 로그를 요청 범위 추적 데이터(예: 추적 ID)와 연관시킬 수 있게 해줍니다:

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
}

진단 검사 목록

연결 또는 쿼리 문제를 해결할 때:

  1. 오류의 경우 log=1로 시작하고, 서버 메시지도 필요하면 log=3로 시작하세요.
  2. 문제를 재현하고 오류 텍스트를 검토한 후 추가 카테고리를 활성화하세요.
  3. 어떤 SQL 문장이나 프로시저 호출이 전송되었는지 확인해야 한다면 추가 8 하세요.
  4. 추가 16 하거나 4 민감한 데이터를 노출하지 않고 매개변수 값과 반환된 행을 기록할 수 있는 안전한 환경에서만 사용하세요.
  5. 문제가 프로토콜 수준이나 재시도 관련으로 보인다면, 재시도 진단을 위해 용량을 log=64 늘리거나 추가 128 하세요.
  6. 문제가 해결되면 로그를 제거하거나 줄이세요.

로그/슬로그를 이용한 구조적 로깅

Go 1.21에서는 구조화된 로깅을 위한 log/slog가 도입되었습니다. 드라이버 출력을 slog을(를) 통해 라우팅하려면 SetContextLogger을(를) 사용하세요:

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

제로로그와의 통합

zerolog 는 제로 할당 구조 로거입니다. 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})
}

zap과의 통합

ZAP 은 고성능 구조화 로거입니다. 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})
}

Correlation ID 전파

분산 시스템에서는 상관관계 ID를 컨텍스트를 통해 전파하여 드라이버 로그를 트리거한 요청과 상관관계를 유지할 수 있습니다:

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

그 다음 요청 컨텍스트를 통해 상관 ID를 전달합니다:

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

실제로 상관 ID 사용법

로그, 재시도, 데이터베이스 호출 간 추적 키로 상관 ID를 사용하세요:

  1. 요청 경계에서 상관 ID를 생성하거나 수락하세요.
  2. 요청 컨텍스트에 저장하고 애플리케이션 및 드라이버 로그에 포함하세요.
  3. SQL 측 문제 해결을 위해서는 sp_set_session_context를 사용해 세션 컨텍스트에 설정하고 SESSION_CONTEXT로 읽으세요.

상관관계 ID는 SQL Server 테이블에 자동으로 저장되지 않습니다. SESSION_CONTEXT 세션 범위가 설정되어 있어, 값은 현재 연결에만 적용되고 세션이 끝난 후에는 유지되지 않습니다.

영구적으로 보존되는 이력이 필요하다면 상관 ID를 감사용 또는 비즈니스 테이블에 명시적으로 기록하세요(예: AuditLog, 타임스탬프, 작업, 상태가 포함된 correlation_id 테이블).

프로덕션 로그 구성

운영 환경에서는 성능 부담을 줄이고 민감한 데이터가 로그에 나타나지 않도록 최소한의 로깅을 허용하세요:

환경 권장 log 캡처한 내용
Development 63 (오류 + 메시지 + 행 + SQL + 매개변수 + 트랜잭션) 디버깅을 위한 완전한 가시성을 제공합니다.
Staging 3 (오류 + 메시지) 쿼리 세부사항이 없는 오류와 서버 메시지.
프로덕션 1 (오류) 또는 0 (꺼짐) 오류만 발생하거나, 드라이버 로깅을 아예 비활성화하세요.

주의

플래그(Parameters)는 16 실제 매개변수 값을 기록하며, 여기에는 개인 식별 정보(PII), 비밀번호 또는 기타 민감한 데이터가 포함될 수 있습니다. 이 플래그를 운영 환경에서 절대 활성화하지 마세요. 각 플래그에 대한 완전한 위험 평가는 보안 모범 사례를 참조하세요.

생산 중 벌목 억제

환경 변수를 사용하여 배포 단계별 로그 레벨을 제어하세요:

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