You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GoLang使用SQLC及fmt调用时如何记录带参数的SQL查询日志

SQLC 记录带参数查询日志的实现方案

你不需要手动获取SQL常量再手动拼接格式化,SQLC生成代码的抽象层本身就预留了无侵入的日志注入能力,比你目前的思路实现成本低很多,也没有维护负担。

最优方案:包装DBTX层实现通用日志拦截

SQLC生成的所有查询方法,都不会直接硬编码调用底层数据库驱动,而是依赖一个DBTX接口(定义了ExecContext/QueryContext/QueryRowContext三个核心方法)执行SQL。你只需要自己实现一个带日志逻辑的DBTX包装类,初始化SQLC生成的Queries实例时传入这个包装类,就能自动拿到所有SQL语句和对应的参数,不需要改动任何SQLC生成的代码。
示例代码:

import (
    "context"
    "database/sql"
    "log"
    "time"
)

// LoggingDB 实现SQLC要求的DBTX接口,包装原生数据库连接
type LoggingDB struct {
    db *sql.DB
}

func (l *LoggingDB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) {
    start := time.Now()
    res, err := l.db.ExecContext(ctx, query, args...)
    log.Printf("[SQLC EXEC] cost=%v err=%v sql=%s args=%v", time.Since(start), err, query, args)
    return res, err
}

func (l *LoggingDB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
    start := time.Now()
    rows, err := l.db.QueryContext(ctx, query, args...)
    log.Printf("[SQLC QUERY] cost=%v err=%v sql=%s args=%v", time.Since(start), err, query, args)
    return rows, err
}

func (l *LoggingDB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
    start := time.Now()
    row := l.db.QueryRowContext(ctx, query, args...)
    log.Printf("[SQLC QUERY_ROW] cost=%v sql=%s args=%v", time.Since(start), query, args)
    return row
}

// 初始化SQLC生成的Queries时直接传入包装后的实例即可
func InitQueries(realDB *sql.DB) *db.Queries {
    return db.New(&LoggingDB{db: realDB})
}

这个方案的优势:

  • 零侵入SQLC生成的代码,升级SQLC版本不会出现兼容问题
  • 自动覆盖所有SQLC生成的查询方法,不需要逐个方法加日志
  • 不需要手动维护SQL常量和参数映射关系,不会出现漏记、错配的问题

特定场景简化方案

如果你用pgx驱动连接PostgreSQL,连上面的包装类都不用写,直接利用pgx自带的Tracer能力即可:

import "github.com/jackc/pgx/v5/pgxpool"

poolCfg, err := pgxpool.ParseConfig(dsn)
// 实现pgx.QueryTracer接口,在TraceQueryStart方法里就能拿到SQL和参数
poolCfg.ConnConfig.Tracer = &yourCustomQueryTracer{}
pool, err := pgxpool.NewWithConfig(context.Background(), poolCfg)
// 直接把连接池传给SQLC生成的Queries即可,所有查询会自动被Tracer捕获
queries := db.New(pool)

不推荐手动获取SQL常量的实现思路

这种方案维护成本极高:SQLC生成的SQL常量会随你的SQL文件修改、SQLC版本升级发生变化,手动维护常量和查询的映射关系很容易出现遗漏、错配,手动拼接参数和SQL占位符也容易出现转义错误,长期维护成本远高于拦截DBTX层的方案。

如果需要生成参数替换后的完整可执行SQL,不要自己手动拼接,直接用对应驱动提供的安全转义方法,比如pgx的Sanitize方法,避免SQL注入风险和转义错误。

SQLC本身没有内置日志打印功能,它的定位是SQL到Go代码的生成工具,日志、链路追踪这类横切逻辑,官方推荐的实现方式就是通过包装DBTX层注入。

内容的提问来源于stack exchange,提问作者M.K.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:27:28