8000 Implement AcquireTracer and ReleaseTracer for TraceLog by stampy88 · Pull Request #2313 · jackc/pgx · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Implement AcquireTracer and ReleaseTracer for TraceLog #2313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions tracelog/tracelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"unicode/utf8"

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

// LogLevel represents the pgx logging level. See LogLevel* constants for
Expand Down Expand Up @@ -103,7 +104,7 @@ func logQueryArgs(args []any) []any {
}
case string:
if len(v) > 64 {
var l int = 0
var l = 0
for w := 0; l < 64; l += w {
_, w = utf8.DecodeRuneInString(v[l:])
}
Expand All @@ -130,8 +131,9 @@ func DefaultTraceLogConfig() *TraceLogConfig {
}
}

// TraceLog implements pgx.QueryTracer, pgx.BatchTracer, pgx.ConnectTracer, and pgx.CopyFromTracer. Logger and LogLevel
// are required. Config will be automatically initialized on first use if nil.
// TraceLog implements pgx.QueryTracer, pgx.BatchTracer, pgx.ConnectTracer, pgx.CopyFromTracer, pgxpool.AcquireTracer,
// and pgxpool.ReleaseTracer. Logger and LogLevel are required. Config will be automatically initialized on the
// first use if nil.
type TraceLog struct {
Logger Logger
LogLevel LogLevel
Expand Down Expand Up @@ -160,6 +162,7 @@ const (
tracelogCopyFromCtxKey
tracelogConnectCtxKey
tracelogPrepareCtxKey
tracelogAcquireCtxKey
)

type traceQueryData struct {
Expand Down Expand Up @@ -347,6 +350,44 @@ func (tl *TraceLog) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pg
}
}

type traceAcquireData struct {
startTime time.Time
}

func (tl *TraceLog) TraceAcquireStart(ctx context.Context, _ *pgxpool.Pool, _ pgxpool.TraceAcquireStartData) context.Context {
return context.WithValue(ctx, tracelogAcquireCtxKey, &traceAcquireData{
startTime: time.Now(),
})
}

func (tl *TraceLog) TraceAcquireEnd(ctx context.Context, _ *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {
tl.ensureConfig()
acquireData := ctx.Value(tracelogAcquireCtxKey).(*traceAcquireData)

endTime := time.Now()
interval := endTime.Sub(acquireData.startTime)

if data.Err != nil {
if tl.shouldLog(LogLevelError) {
tl.Logger.Log(ctx, LogLevelError, "Acquire", map[string]any{"err": data.Err, tl.Config.TimeKey: interval})
}
return
}

if data.Conn != nil {
if tl.shouldLog(LogLevelDebug) {
tl.log(ctx, data.Conn, LogLevelDebug, "Acquire", map[string]any{tl.Config.TimeKey: interval})
}
}
}

func (tl *TraceLog) TraceRelease(_ *pgxpool.Pool, data pgxpool.TraceReleaseData) {
if tl.shouldLog(LogLevelDebug) {
// there is no context on the TraceRelease callback
tl.log(context.Background(), data.Conn, LogLevelDebug, "Release", map[string]any{})
}
}

func (tl *TraceLog) shouldLog(lvl LogLevel) bool {
return tl.LogLevel >= lvl
}
Expand Down
82 changes: 82 additions & 0 deletions tracelog/tracelog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,88 @@ func TestLogBatchStatementsOnBatchResultClose(t *testing.T) {
})
}

func TestLogAcquire(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()

logger := &testLogger{}
tracer := &tracelog.TraceLog{
Logger: logger,
LogLevel: tracelog.LogLevelTrace,
}

config := defaultConnTestRunner.CreateConfig(ctx, t)
config.Tracer = tracer

poolConfig, err := pgxpool.ParseConfig(config.ConnString())
require.NoError(t, err)

poolConfig.ConnConfig = config
pool1, err := pgxpool.NewWithConfig(ctx, poolConfig)
require.NoError(t, err)
defer pool1.Close()

conn1, err := pool1.Acquire(ctx)
require.NoError(t, err)
defer conn1.Release()
require.Len(t, logger.logs, 2) // Has both the Connect and Acquire logs
require.Equal(t, "Acquire", logger.logs[1].msg)
require.Equal(t, tracelog.LogLevelDebug, logger.logs[1].lvl)

logger.Clear()

// create a 2nd pool with a bad host to verify the error handling
poolConfig, err = pgxpool.ParseConfig("host=/invalid")
require.NoError(t, err)
poolConfig.ConnConfig.Tracer = tracer

pool2, err := pgxpool.NewWithConfig(ctx, poolConfig)
require.NoError(t, err)
defer pool2.Close()

conn2, err := pool2.Acquire(ctx)
require.Error(t, err)
require.Nil(t, conn2)
require.Len(t, logger.logs, 2)
require.Equal(t, "Acquire", logger.logs[1].msg)
require.Equal(t, tracelog.LogLevelError, logger.logs[1].lvl)
}

func TestLogRelease(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()

logger := &testLogger{}
tracer := &tracelog.TraceLog{
Logger: logger,
LogLevel: tracelog.LogLevelTrace,
}

config := defaultConnTestRunner.CreateConfig(ctx, t)
config.Tracer = tracer

poolConfig, err := pgxpool.ParseConfig(config.ConnString())
require.NoError(t, err)

poolConfig.ConnConfig = config
pool1, err := pgxpool.NewWithConfig(ctx, poolConfig)
require.NoError(t, err)
defer pool1.Close()

conn1, err := pool1.Acquire(ctx)
require.NoError(t, err)

logger.Clear()
conn1.Release()
require.Len(t, logger.logs, 1)
require.Equal(t, "Release", logger.logs[0].msg)
require.Equal(t, tracelog.LogLevelDebug, logger.logs[0].lvl)
}

func TestLogPrepare(t *testing.T) {
t.Parallel()

Expand Down
0