驅動 go-mssqldb 程式提供可配置的日誌功能,用於排查連線問題、查詢問題及效能分析。 本文說明可用的日誌旗標以及如何使用自訂記錄器。
記錄旗標
使用 log 連線參數來啟用診斷輸出。 記錄旗標是位元遮罩值,這表示你可以將它們各自的整數值相加來加以組合:
| 旗標值 | 類別 | Description |
|---|---|---|
1 |
Errors | 記錄錯誤訊息。 |
2 |
Messages | 記錄伺服器的資訊訊息。 |
4 |
Rows | 記錄列資料。 |
8 |
SQL | 記錄發送給伺服器的 SQL 陳述。 |
16 |
參數 | 記錄參數名稱與數值。 |
32 |
Transactions | 記錄交易開始、提交和回滾事件。 |
64 |
Debug | 記錄低階協定與TDS細節。 |
128 |
重試 | 記錄連線重試次數。 |
旗標 4 (列)和 16 (參數)可以揭露應用程式資料、秘密或個人識別資訊。 將它們視為短暫的診斷標記,而非例行的生產設定。
Examples
僅記錄錯誤:
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=1
日誌錯誤、SQL 語句與參數:
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=25
備註
該值 25 的計算方式為 1 + 8 + 16 (錯誤 + SQL + 參數)。
記錄所有內容:
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=255
Warning
高 log 標誌值(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
}
診斷檢查清單
在排解連線或查詢問題時:
- 如果是錯誤,請從
log=1開始;如果也需要伺服器訊息,則使用log=3。 - 重現問題並查看錯誤訊息,再啟用更多類別。
- 如果你需要確認傳送的是哪個 SQL 語句或程序呼叫,請加上
8。 - 僅可在安全的環境中新增
16或4,且該環境能夠記錄參數值和傳回的資料列,而不會洩露敏感資料。 - 如果問題看起來屬於通訊協定層級或與重試有關,請將其提高至
log=64,或加入128以取得重試診斷資訊。 - 問題解決後移除或減少日誌記錄。
使用 log/slog 進行結構化記錄
Go 1.21 引入了用於結構化日誌記錄的 log/slog。 使用 SetContextLogger 將驅動程式輸出經由 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.
}
與 zerolog 整合
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})
}
關聯識別碼傳遞
在分散式系統中,透過上下文傳播相關 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。
- 將它儲存在請求上下文中,並包含在應用程式和驅動程式日誌中。
- 若要從 SQL 端進行疑難排解,請在工作階段內容中使用
sp_set_session_context進行設定,並使用SESSION_CONTEXT讀取。
相關 ID 不會自動儲存在 SQL Server 資料表中。
SESSION_CONTEXT 是會話範圍,因此值只適用於目前連線,且不會在會話結束後持續存在。
如果你需要持久歷史,請將相關性 ID 明確寫入你的審計或業務資料表(例如 AuditLog ,包含 correlation_id、 、 時間戳、營運和狀態的表格)。
生產日誌配置
在生產環境中,啟用最小記錄以避免效能負擔並防止敏感資料出現在日誌中:
| 環境 | 建議 log 值 |
它捕捉了什麼 |
|---|---|---|
| Development |
63 (錯誤 + 訊息 + 列 + SQL + 參數 + 交易) |
完整掌握除錯資訊。 |
| Staging |
3 (錯誤+訊息) |
錯誤和伺服器訊息,不含查詢詳細資料。 |
| Production |
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"
}