@@ -52,6 +52,9 @@ const (
5252 PGXOperationTypeKey = attribute .Key ("pgx.operation.type" )
5353 // DBClientOperationErrorsKey represents the count of operation errors
5454 DBClientOperationErrorsKey = attribute .Key ("db.client.operation.errors" )
55+ // PrepareDurationKey records the wall-clock time of a prepared-statement
56+ // round-trip, in milliseconds, as an attribute on the parent query span.
57+ PrepareDurationKey = attribute .Key ("pgx.prepare.duration" )
5558)
5659
5760type startTimeCtxKey struct {}
@@ -530,116 +533,41 @@ func (t *Tracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndDa
530533// TracePrepareStart is called at the beginning of Prepare calls. The returned
531534// context is used for the rest of the call and will be passed to
532535// TracePrepareEnd.
533- func (t * Tracer ) TracePrepareStart (ctx context.Context , conn * pgx.Conn , data pgx.TracePrepareStartData ) context.Context {
534- ctx = context .WithValue (ctx , startTimeCtxKey {}, time .Now ())
535-
536- if ! trace .SpanFromContext (ctx ).IsRecording () {
537- return ctx
538- }
539-
540- optsP := t .spanStartOptionsPool .Get ().(* []trace.SpanStartOption )
541- defer t .spanStartOptionsPool .Put (optsP )
542- attrsP := t .attributeSlicePool .Get ().(* []attribute.KeyValue )
543- defer t .attributeSlicePool .Put (attrsP )
544-
545- // reslice to empty
546- opts := (* optsP )[:0 ]
547- attrs := (* attrsP )[:0 ]
548-
549- attrs = append (attrs , t .tracerAttrs ... )
550-
551- if data .Name != "" {
552- attrs = append (attrs , PrepareStmtNameKey .String (data .Name ))
553- }
554-
555- if t .logConnectionDetails && conn != nil {
556- attrs = append (attrs , connectionAttributesFromConfig (conn .Config ())... )
557- }
558-
559- attrs = append (attrs , semconv .DBOperationName (t .spanNameCtxFunc (ctx , data .SQL )))
560-
561- if t .logSQLStatement {
562- attrs = append (attrs , semconv .DBQueryText (data .SQL ))
563- }
564-
565- opts = append (opts ,
566- trace .WithSpanKind (trace .SpanKindClient ),
567- trace .WithAttributes (attrs ... ),
568- )
569-
570- spanName := data .SQL
571- if t .trimQuerySpanName {
572- spanName = t .spanNameCtxFunc (ctx , data .SQL )
573- }
574- if t .prefixQuerySpanName {
575- spanName = "prepare " + spanName
576- }
577-
578- ctx , _ = t .tracer .Start (ctx , spanName , opts ... )
579-
580- return ctx
536+ //
537+ // No span is created for prepare. Instead, the prepare duration is recorded as
538+ // the pgx.prepare.duration attribute (milliseconds) on the parent query span.
539+ func (t * Tracer ) TracePrepareStart (ctx context.Context , _ * pgx.Conn , _ pgx.TracePrepareStartData ) context.Context {
540+ return context .WithValue (ctx , startTimeCtxKey {}, time .Now ())
581541}
582542
583543// TracePrepareEnd is called at the end of Prepare calls.
584544func (t * Tracer ) TracePrepareEnd (ctx context.Context , _ * pgx.Conn , data pgx.TracePrepareEndData ) {
585- span := trace .SpanFromContext (ctx )
586545 t .incrementOperationErrorCount (ctx , data .Err , pgxOperationPrepare )
587546 t .recordOperationDuration (ctx , pgxOperationPrepare )
588547
589- if ! span .IsRecording () {
590- return
548+ if startTime , ok := ctx .Value (startTimeCtxKey {}).(time.Time ); ok {
549+ span := trace .SpanFromContext (ctx )
550+ if span .IsRecording () {
551+ span .SetAttributes (PrepareDurationKey .Int64 (time .Since (startTime ).Milliseconds ()))
552+ }
591553 }
592-
593- recordSpanError (span , data .Err )
594- span .End ()
595554}
596555
597556// TraceAcquireStart is called at the beginning of Acquire.
598557// The returned context is used for the rest of the call and will be passed to the TraceAcquireEnd.
599- func (t * Tracer ) TraceAcquireStart (ctx context.Context , pool * pgxpool.Pool , data pgxpool.TraceAcquireStartData ) context.Context {
600- ctx = context .WithValue (ctx , startTimeCtxKey {}, time .Now ())
601-
602- if ! trace .SpanFromContext (ctx ).IsRecording () {
603- return ctx
604- }
605-
606- optsP := t .spanStartOptionsPool .Get ().(* []trace.SpanStartOption )
607- defer t .spanStartOptionsPool .Put (optsP )
608- attrsP := t .attributeSlicePool .Get ().(* []attribute.KeyValue )
609- defer t .attributeSlicePool .Put (attrsP )
610-
611- // reslice to empty
612- opts := (* optsP )[:0 ]
613- attrs := (* attrsP )[:0 ]
614-
615- attrs = append (attrs , t .tracerAttrs ... )
616-
617- if t .logConnectionDetails && pool != nil && pool .Config () != nil && pool .Config ().ConnConfig != nil {
618- attrs = append (attrs , connectionAttributesFromConfig (pool .Config ().ConnConfig )... )
619- }
620-
621- opts = append (opts ,
622- trace .WithSpanKind (trace .SpanKindClient ),
623- trace .WithAttributes (attrs ... ),
624- )
625-
626- ctx , _ = t .tracer .Start (ctx , "pool.acquire" , opts ... )
627-
628- return ctx
558+ //
559+ // No span is created for pool.acquire. Pool acquire duration is tracked via the
560+ // db.client.operation.duration metric (pgx.operation.type=acquire) and the pgxpool.*
561+ // metrics from RecordStats. As a span it adds noise without actionable signal; pool
562+ // contention is an environmental concern better diagnosed from aggregate metrics.
563+ func (t * Tracer ) TraceAcquireStart (ctx context.Context , _ * pgxpool.Pool , _ pgxpool.TraceAcquireStartData ) context.Context {
564+ return context .WithValue (ctx , startTimeCtxKey {}, time .Now ())
629565}
630566
631567// TraceAcquireEnd is called when a connection has been acquired.
632568func (t * Tracer ) TraceAcquireEnd (ctx context.Context , _ * pgxpool.Pool , data pgxpool.TraceAcquireEndData ) {
633- span := trace .SpanFromContext (ctx )
634569 t .incrementOperationErrorCount (ctx , data .Err , pgxOperationAcquire )
635570 t .recordOperationDuration (ctx , pgxOperationAcquire )
636-
637- if ! span .IsRecording () {
638- return
639- }
640-
641- recordSpanError (span , data .Err )
642- span .End ()
643571}
644572
645573func makeParamsAttribute (args []any ) attribute.KeyValue {
0 commit comments