如何使用sqlx与PostgreSQL记录参数而非占位符?
在sqlx中输出带实际参数的SQL日志
要解决sqlx日志里仅显示$1、$2占位符而非实际参数的问题,可通过以下两种方式实现:
一、手动编写SQL参数替换函数
自己实现辅助函数,将占位符替换为实际参数值,同时处理不同数据类型的格式和转义问题:
import ( "fmt" "strings" "time" "log" ) // 将带占位符的SQL和参数拼接成可直接执行的SQL语句 func formatSQLWithParams(query string, args ...interface{}) string { formattedQuery := query for idx, arg := range args { placeholder := fmt.Sprintf("$%d", idx+1) var paramStr string switch val := arg.(type) { case string: // 转义字符串中的单引号,避免SQL语法错误 escapedVal := strings.ReplaceAll(val, "'", "''") paramStr = fmt.Sprintf("'%s'", escapedVal) case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: // 数字类型直接转字符串,无需加引号 paramStr = fmt.Sprintf("%v", val) case time.Time: // 日期类型转成数据库兼容的字符串格式 paramStr = fmt.Sprintf("'%s'", val.Format("2006-01-02 15:04:05")) case nil: paramStr = "NULL" default: // 其他类型默认转成带引号的字符串,可根据需要扩展 paramStr = fmt.Sprintf("'%v'", val) } formattedQuery = strings.ReplaceAll(formattedQuery, placeholder, paramStr) } return formattedQuery }
使用示例
执行SQL时,调用该函数生成可直接复制验证的SQL并日志:
teamID := "ABC" playerID := "123" query := "SELECT * from players where team_id = $1 AND id = $2" // 执行SQL rows, err := db.QueryContext(ctx, query, teamID, playerID) if err != nil { // 日志输出带实际参数的错误SQL log.Printf("SQL执行失败: %s", formatSQLWithParams(query, teamID, playerID)) return err } // 可选:日志成功执行的SQL log.Printf("执行SQL: %s", formatSQLWithParams(query, teamID, playerID))
二、包装sqlx DB对象实现自动日志
如果不想每次执行都手动调用函数,可以包装sqlx的DB或Tx对象,拦截所有查询请求,自动完成参数替换和日志输出:
import "context" import "database/sql" type LoggedDB struct { *sqlx.DB } func (db *LoggedDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { formattedSQL := formatSQLWithParams(query, args...) log.Printf("执行SQL: %s", formattedSQL) return db.DB.QueryContext(ctx, query, args...) } func (db *LoggedDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { formattedSQL := formatSQLWithParams(query, args...) log.Printf("执行SQL: %s", formattedSQL) return db.DB.ExecContext(ctx, query, args...) } // 其他需要拦截的方法(如QueryxContext、Exec等)可类似扩展
使用示例
// 将原sqlx.DB包装成LoggedDB loggedDB := &LoggedDB{DB: db} // 后续使用loggedDB执行SQL,会自动输出带参数的日志 rows, err := loggedDB.QueryContext(ctx, "SELECT * from players where team_id = $1 AND id = $2", "ABC", "123")
注意事项
- 敏感信息脱敏:如果参数包含密码、手机号等敏感数据,需要在替换时进行脱敏处理,避免泄露。
- 性能影响:频繁的字符串替换可能会带来轻微性能损耗,生产环境可根据需要控制日志级别(如仅Debug级别输出)。
- 兼容性:该方法仅适用于sqlx使用
$N格式占位符的场景(如PostgreSQL),如果是MySQL使用?占位符,需要修改占位符的匹配逻辑。
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

