What's the expected way to do logging AND tracing? #1677
Replies: 5 comments
-
I think this is probably necessary. |
Beta Was this translation helpful? Give feedback.
-
I want to have a instrumented logger and the otel tracer. Did you write a MultiTracer to support this use case? |
Beta Was this translation helpful? Give feedback.
-
I have the same problem. I was able to set up both loggers and tracers by providing the following implementation in my application. Here is a sample of my implementation using pgx. // https://github.com/jackc/pgx/issues/1061#issuecomment-1205942323
type MultiQueryTracer struct {
Tracers []pgx.QueryTracer
}
func (m *MultiQueryTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
for _, t := range m.Tracers {
ctx = t.TraceQueryStart(ctx, conn, data)
}
return ctx
}
func (m *MultiQueryTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
for _, t := range m.Tracers {
t.TraceQueryEnd(ctx, conn, data)
}
}
func New(ctx context.Context, slogger *slog.Logger) *pgxpool.Pool {
config, err := pgxpool.ParseConfig(dsn)
if err != nil {
panic(err)
}
// FYI: NewLoggerAdapter: https://github.com/mcosta74/pgx-slog
adapterLogger := logger.NewLoggerAdapter(slogger)
m := MultiQueryTracer{
Tracers: []pgx.QueryTracer{
// tracer: https://github.com/exaring/otelpgx
otelpgx.NewTracer(),
// logger
&tracelog.TraceLog{
Logger: adapterLogger,
LogLevel: tracelog.LogLevelTrace,
},
},
}
config.ConnConfig.Tracer = &m
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
panic(err)
}
if err := pool.Ping(ctx); err != nil {
panic(err)
}
return pool
} |
Beta Was this translation helpful? Give feedback.
-
If anyone's struggling with this, I threw this together based on a comment in this issue and another (which are in the code file). This uses the slog logger interface. https://gist.github.com/zaydek/91f27cdd35c6240701f81415c3ba7c07 |
Beta Was this translation helpful? Give feedback.
-
Here is our version of MultiTracer which covers all interfaces from Click to see MultiTracer implementation for pgxpackage xxx
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
// MultiTracer allows setting many See https://github.com/jackc/pgx/discussions/1677.
type MultiTracer struct {
Tracers []any
}
func NewMultiTracer(tracers ...any) *MultiTracer {
return &MultiTracer{Tracers: tracers}
}
func (m *MultiTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgx.QueryTracer); ok {
ctx = t.TraceQueryStart(ctx, conn, data)
}
}
return ctx
}
func (m *MultiTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.QueryTracer); ok {
t.TraceQueryEnd(ctx, conn, data)
}
}
}
func (m *MultiTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgx.CopyFromTracer); ok {
ctx = t.TraceCopyFromStart(ctx, conn, data)
}
}
return ctx
}
func (m *MultiTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.CopyFromTracer); ok {
t.TraceCopyFromEnd(ctx, conn, data)
}
}
}
func (m *MultiTracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgx.BatchTracer); ok {
ctx = t.TraceBatchStart(ctx, conn, data)
}
}
return ctx
}
func (m *MultiTracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.BatchTracer); ok {
t.TraceBatchQuery(ctx, conn, data)
}
}
}
func (m *MultiTracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.BatchTracer); ok {
t.TraceBatchEnd(ctx, conn, data)
}
}
}
func (m *MultiTracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgx.PrepareTracer); ok {
ctx = t.TracePrepareStart(ctx, conn, data)
}
}
return ctx
}
func (m *MultiTracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.PrepareTracer); ok {
t.TracePrepareEnd(ctx, conn, data)
}
}
}
func (m *MultiTracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgx.ConnectTracer); ok {
ctx = t.TraceConnectStart(ctx, data)
}
}
return ctx
}
func (m *MultiTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgx.ConnectTracer); ok {
t.TraceConnectEnd(ctx, data)
}
}
}
func (m *MultiTracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {
for _, t := range m.Tracers {
if t, ok := t.(pgxpool.AcquireTracer); ok {
ctx = t.TraceAcquireStart(ctx, pool, data)
}
}
return ctx
}
func (m *MultiTracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {
for _, t := range m.Tracers {
if t, ok := t.(pgxpool.AcquireTracer); ok {
t.TraceAcquireEnd(ctx, pool, data)
}
}
}
func (m *MultiTracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {
for _, t := range m.Tracers {
if t, ok := t.(pgxpool.ReleaseTracer); ok {
t.TraceRelease(pool, data)
}
}
} Example usage: var tracers []any
tracers = append(tracers, &tracelog.TraceLog{...})
tracers = append(tracers, otelpgx.NewTracer(otelpgx.WithTrimSQLInSpanName()))
if len(tracers) > 0 {
poolConf.ConnConfig.Tracer = NewMultiTracer(tracers...)
} |
Beta Was this translation helpful? Give feedback.
-
Hi! I'm in the process of porting from v4 to v5. At our company, we use Datadog for tracing. But Datadog does not have a tracing wrapper for v4 pgx; it does have a way to add tracing to
sql.DB
. A pgx-native tracer seems to be in the works for v5. I prefer to usezerolog
for logging.As a result, in v4 I use a l somewhat complex initialization process:
This accomplishes zerolog for logging and Datadog for tracing and has been in production for a year.
As I look at the
Tracer
interface inv5
, I'm having trouble understanding what the expected method is for accomplishing both logging and tracing? Do I need to implement anMultiTracer
adaptor, which relays all of theTracer
calls to multiple underlying Tracers, one of which is logging, and one of which is my tracing client (Datadog)?Beta Was this translation helpful? Give feedback.
All reactions