jmoiron / sqlx

general purpose extensions to golang's database/sql
http://jmoiron.github.io/sqlx/
MIT License
16.3k stars 1.09k forks source link

go语言里的sqlx请问怎么打印最终于执行的sql语句 #940

Open feikeq opened 2 months ago

feikeq commented 2 months ago

没有这功能在调式的时候很不方便啊!

yzx9 commented 2 weeks ago

You could try wrapping the database driver with sqlhooks. This suggestion originally came from a StackOverflow post, though I haven't been able to locate it again.

var logger *slog.Logger
const SlowThreshold = 300 * time.Millisecond

func init() {
    hook := customHook{
        slowThreshold: SlowThreshold,
    }
    sql.Register("mysql-custom", sqlhooks.Wrap(&mysql.MySQLDriver{}, &hook))
}

type customHook struct {
    slowThreshold time.Duration
}

// make sure hook implement `sqlhooks.Hooks`
var _ interface{ sqlhooks.Hooks } = (*customHook)(nil)

type hookKey string

const hookStartAt hookKey = "startAt"

func (h *customHook) Before(ctx context.Context, query string, args ...any) (context.Context, error) {
    ctx = context.WithValue(ctx, hookStartAt, time.Now())
    return ctx, nil
}

// not run if on error
func (h *customHook) After(ctx context.Context, query string, args ...any) (context.Context, error) {
    if startAt, ok := ctx.Value(hookStartAt).(time.Time); ok {
        if took := time.Since(startAt); took >= h.slowThreshold { // warn if slow
            logger.Warn("slow query",
                slog.String("query", string(readFriendly(query))),
                slog.Any("args", args),
                slog.Int64("took", int64(took)))
        }
    }

    return ctx, nil
}

type ErrDown struct {
    err   error
    query string
    args  []any
    took  time.Duration
}

// OnError will be called if any error happens
func (h *customHook) OnError(ctx context.Context, err error, query string, args ...any) error {
    // Not a user error: driver is telling sql package that an
    // optional interface method is not implemented. There is
    // nothing to instrument here.
    // https://golang.org/pkg/database/sql/driver/#ErrSkip
    // https://github.com/DataDog/dd-trace-go/issues/270
    if errors.Is(err, driver.ErrSkip) {
        return nil
    }

    if err == nil {
        return nil
    }

    custom := ErrDown{
        err:   err,
        query: query,
        args:  args,
    }
    if startAt, ok := ctx.Value(hookStartAt).(time.Time); ok {
        custom.took = time.Since(startAt)
    }
    return custom
}