Pencatatan dan diagnostik dengan go-mssqldb

Driver go-mssqldb menyediakan pengelogan yang dapat dikonfigurasi untuk memecahkan masalah koneksi, masalah kueri, dan analisis performa. Artikel ini menjelaskan bendera log yang tersedia dan cara menggunakan logger kustom.

Bendera log

Gunakan log parameter koneksi untuk mengaktifkan output diagnostik. Bendera log adalah nilai bitmask, artinya Anda dapat menggabungkannya dengan menambahkan nilai bilangan bulatnya:

Nilai penanda Kategori Description
1 Errors Mencatat pesan kesalahan.
2 Messages Catat pesan informasi dari server.
4 Rows Catat data baris.
8 SQL Log pernyataan SQL yang dikirim ke server.
16 Parameter-parameternya Catat nama dan nilai parameter.
32 Transactions Catat peristiwa mulai, penerapan dan pengembalian transaksi.
64 Debug Catat protokol tingkat rendah dan rincian TDS.
128 Pengulangan Catat upaya percobaan ulang koneksi.

Bendera 4 (baris) dan 16 (parameter) dapat mengekspos data aplikasi, rahasia, atau informasi identitas pribadi. Perlakukan mereka sebagai bendera diagnostik berumur pendek, bukan sebagai pengaturan produksi rutin.

Examples

Catat hanya kesalahan:

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

Kesalahan log, pernyataan SQL, dan parameter:

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

Note

Nilainya 25 dihitung sebagai 1 + 8 + 16 (kesalahan + SQL + parameter).

Catat semuanya:

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

Warning

Nilai flag log yang tinggi (64, 128, 255) menghasilkan output yang verbose dan dapat memengaruhi kinerja. Gunakan hanya untuk debugging.

Pencatat default

Secara bawaan, driver mencatat log ke paket standar log milik Go, yang menulis ke os.Stderr. Keluaran mencakup stempel waktu dan kategori log:

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

Pencatat khusus dengan SetLogger

Gunakan mssql.SetLogger untuk mengalihkan output log driver ke logger kustom. Logger harus mengimplementasikan antarmuka 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
}

Pencatat sadar konteks dengan SetContextLogger

Gunakan mssql.SetContextLogger untuk menyediakan logger yang memahami konteks. Pencatat harus mengimplementasikan mssql.ContextLogger antarmuka, yang memiliki satu Log metode yang menerima konteks, kategori log, dan string pesan. Pendekatan ini memungkinkan Anda untuk menghubungkan log driver dengan data pelacakan cakupan permintaan (misalnya, ID pelacakan):

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
}

Daftar periksa diagnostik

Saat memecahkan masalah koneksi atau kueri:

  1. Mulailah dengan log=1 untuk kesalahan, atau log=3 jika Anda juga memerlukan pesan server.
  2. Reproduksi masalah tersebut dan tinjau pesan kesalahan sebelum mengaktifkan lebih banyak kategori.
  3. Tambahkan 8 jika Anda perlu mengonfirmasi pernyataan SQL atau panggilan prosedur mana yang dikirim.
  4. Tambahkan 16 atau 4 hanya di lingkungan yang aman di mana nilai parameter dan baris yang ditampilkan dapat dicatat tanpa mengekspos data sensitif.
  5. Jika masalah tampak berada di tingkat protokol atau terkait dengan percobaan ulang, tingkatkan ke log=64 atau tambahkan 128 untuk diagnostik percobaan ulang.
  6. Hapus atau kurangi pengelogan setelah masalah diselesaikan.

Pencatatan terstruktur dengan log/slog

Go 1.21 memperkenalkan log/slog untuk logging terstruktur. Gunakan SetContextLogger untuk merutekan output driver melalui 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.
}

Integrasi dengan zerolog

Zerolog adalah pencatat terstruktur tanpa alokasi. Arahkan log driver melalui 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})
}

Integrasi dengan zap

ZAP adalah logger terstruktur berkinerja tinggi. Arahkan log driver melalui 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})
}

Propagasi ID korelasi

Dalam sistem terdistribusi, menyebarkan ID korelasi melalui konteks sehingga log driver dapat dikorelasikan dengan permintaan yang memicunya:

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

Kemudian teruskan ID korelasi melalui konteks permintaan Anda:

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

Cara menggunakan ID korelasi dalam praktik

Gunakan ID korelasi sebagai kunci pelacakan di seluruh log, percobaan ulang, dan panggilan database:

  1. Buat atau terima ID korelasi di batas permintaan.
  2. Simpan dalam konteks permintaan dan sertakan dalam log aplikasi dan driver.
  3. Untuk pemecahan masalah di sisi SQL, tetapkan dalam konteks sesi dengan sp_set_session_context dan baca dengan SESSION_CONTEXT.

ID korelasi tidak secara otomatis disimpan dalam tabel SQL Server. SESSION_CONTEXT memiliki cakupan sesi, sehingga nilainya hanya berlaku untuk koneksi saat ini dan tidak dipertahankan setelah sesi berakhir.

Jika Anda memerlukan riwayat yang tahan lama, tulis ID korelasi secara eksplisit ke tabel audit atau bisnis Anda (misalnya, AuditLog tabel dengan correlation_id, stempel waktu, operasi, dan status).

Konfigurasi pengelogan produksi

Dalam produksi, aktifkan pengelogan minimal untuk menghindari overhead performa dan mencegah data sensitif muncul di log:

Lingkungan Nilai yang direkomendasikan log Apa yang ditangkapnya
Development 63 (kesalahan + pesan + baris + SQL + parameter + transaksi) Visibilitas penuh untuk debugging.
Staging 3 (kesalahan + pesan) Kesalahan dan pesan server tanpa detail kueri.
Production 1 (error) atau 0 (nonaktif) Kesalahan saja, atau nonaktifkan pencatatan log driver sepenuhnya.

Caution

Bendera Parameters (16) mencatat nilai parameter aktual, yang dapat mencakup informasi identitas pribadi (PII), kata sandi, atau data sensitif lainnya. Jangan pernah mengaktifkan flag ini di lingkungan produksi. Untuk penilaian risiko lengkap dari setiap bendera, lihat Praktik terbaik keamanan.

Nonaktifkan pencatatan log di lingkungan produksi

Gunakan variabel lingkungan untuk mengontrol tingkat log per tahap penyebaran:

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