diff --git a/go.mod b/go.mod index f29aaf075..e73427632 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/docker/docker v27.4.1+incompatible github.com/envoyproxy/go-control-plane/envoy v1.32.3 github.com/envoyproxy/protoc-gen-validate v1.1.0 + github.com/exaring/otelpgx v0.8.0 github.com/go-chi/chi/v5 v5.2.0 github.com/go-jose/go-jose/v3 v3.0.3 github.com/google/btree v1.1.3 @@ -70,16 +71,16 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 go.opentelemetry.io/contrib/propagators/autoprop v0.57.0 - go.opentelemetry.io/otel v1.33.0 + go.opentelemetry.io/otel v1.34.0 go.opentelemetry.io/otel/bridge/opencensus v1.32.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 - go.opentelemetry.io/otel/metric v1.33.0 + go.opentelemetry.io/otel/metric v1.34.0 go.opentelemetry.io/otel/sdk v1.33.0 go.opentelemetry.io/otel/sdk/metric v1.32.0 - go.opentelemetry.io/otel/trace v1.33.0 + go.opentelemetry.io/otel/trace v1.34.0 go.opentelemetry.io/proto/otlp v1.4.0 go.uber.org/automaxprocs v1.6.0 go.uber.org/mock v0.5.0 diff --git a/go.sum b/go.sum index e77fe687b..029cdd535 100644 --- a/go.sum +++ b/go.sum @@ -208,6 +208,8 @@ github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJP github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/exaring/otelpgx v0.8.0 h1:uqoDIW9qKkyz479z2cGrmJ8OJypydyEA+xwey4ukvNo= +github.com/exaring/otelpgx v0.8.0/go.mod h1:ANkRZDfgfmN6yJS1xKMkshbnsHO8at5sYwtVEYOX8hc= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -708,8 +710,8 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 h1:K/fOyTMD6GELKTIJBaJ9k3 go.opentelemetry.io/contrib/propagators/jaeger v1.32.0/go.mod h1:ISE6hda//MTWvtngG7p4et3OCngsrTVfl7c6DjN17f8= go.opentelemetry.io/contrib/propagators/ot v1.32.0 h1:Poy02A4wOZubHyd2hpHPDgZW+rn6EIq0vCwTZJ6Lmu8= go.opentelemetry.io/contrib/propagators/ot v1.32.0/go.mod h1:cbhaURV+VR3NIMarzDYZU1RDEkXG1fNd1WMP1XCcGkY= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/bridge/opencensus v1.32.0 h1:OVbbFgPG60UolI8ZUs+Z75NnKiO0C9QltXBrqUDImS0= go.opentelemetry.io/otel/bridge/opencensus v1.32.0/go.mod h1:J5SEiJNu6zzqpcA6+AVpxUKzxNocUMsefgHRpS8zdW8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= @@ -722,14 +724,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= diff --git a/internal/databroker/server.go b/internal/databroker/server.go index 2671bf0cc..863a0f9d3 100644 --- a/internal/databroker/server.go +++ b/internal/databroker/server.go @@ -470,7 +470,7 @@ func (srv *Server) newBackendLocked(ctx context.Context) (storage.Backend, error // NB: the context passed to postgres.New here is a separate context scoped // to the lifetime of the server itself. 'ctx' may be a short-lived request // context, since the backend is lazy-initialized. - return postgres.New(srv.backendCtx, srv.cfg.storageConnectionString), nil + return postgres.New(srv.backendCtx, srv.cfg.storageConnectionString, postgres.WithTracerProvider(srv.tracerProvider)), nil default: return nil, fmt.Errorf("unsupported storage type: %s", srv.cfg.storageType) } diff --git a/pkg/storage/postgres/backend.go b/pkg/storage/postgres/backend.go index 91e9d7cfb..5a8123375 100644 --- a/pkg/storage/postgres/backend.go +++ b/pkg/storage/postgres/backend.go @@ -9,6 +9,7 @@ import ( "time" "github.com/cenkalti/backoff/v4" + "github.com/exaring/otelpgx" "github.com/jackc/pgx/v5/pgxpool" "google.golang.org/protobuf/types/known/fieldmaskpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -353,6 +354,11 @@ func (backend *Backend) init(ctx context.Context) (serverVersion uint64, pool *p return serverVersion, nil, err } + if backend.cfg.tracerProvider != nil { + config.ConnConfig.Tracer = otelpgx.NewTracer( + otelpgx.WithTracerProvider(backend.cfg.tracerProvider)) + } + pool, err = pgxpool.NewWithConfig(context.Background(), config) if err != nil { return serverVersion, nil, err diff --git a/pkg/storage/postgres/option.go b/pkg/storage/postgres/option.go index 7e548d25a..d27f9be26 100644 --- a/pkg/storage/postgres/option.go +++ b/pkg/storage/postgres/option.go @@ -2,6 +2,8 @@ package postgres import ( "time" + + oteltrace "go.opentelemetry.io/otel/trace" ) const ( @@ -10,8 +12,9 @@ const ( ) type config struct { - expiry time.Duration - registryTTL time.Duration + expiry time.Duration + registryTTL time.Duration + tracerProvider oteltrace.TracerProvider } // Option customizes a Backend. @@ -31,6 +34,12 @@ func WithRegistryTTL(ttl time.Duration) Option { } } +func WithTracerProvider(tracerProvider oteltrace.TracerProvider) Option { + return func(cfg *config) { + cfg.tracerProvider = tracerProvider + } +} + func getConfig(options ...Option) *config { cfg := new(config) WithExpiry(defaultExpiry)(cfg) diff --git a/pkg/storage/postgres/tracing_test.go b/pkg/storage/postgres/tracing_test.go new file mode 100644 index 000000000..3f00eb9f6 --- /dev/null +++ b/pkg/storage/postgres/tracing_test.go @@ -0,0 +1,70 @@ +package postgres_test + +import ( + "context" + "io" + "net/http" + "os" + "runtime" + "testing" + + "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/testenv" + "github.com/pomerium/pomerium/internal/testenv/scenarios" + "github.com/pomerium/pomerium/internal/testenv/snippets" + "github.com/pomerium/pomerium/internal/testenv/upstreams" + "github.com/pomerium/pomerium/internal/testutil" + "github.com/pomerium/pomerium/internal/testutil/tracetest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryTracing(t *testing.T) { + if os.Getenv("GITHUB_ACTION") != "" && runtime.GOOS == "darwin" { + t.Skip("Github action can not run docker on MacOS") + } + + testutil.WithTestPostgres(t, func(dsn string) { + receiver := scenarios.NewOTLPTraceReceiver() + env := testenv.New(t, testenv.WithTraceDebugFlags(testenv.StandardTraceDebugFlags), testenv.WithTraceClient(receiver.NewGRPCClient())) + env.Add(receiver) + + env.Add(testenv.ModifierFunc(func(_ context.Context, cfg *config.Config) { + cfg.Options.DataBrokerStorageType = config.StoragePostgresName + cfg.Options.DataBrokerStorageConnectionString = dsn + })) + up := upstreams.HTTP(nil, upstreams.WithDisplayName("Upstream")) + up.Handle("/foo", func(w http.ResponseWriter, _ *http.Request) { + w.Write([]byte("OK")) + }) + env.Add(scenarios.NewIDP([]*scenarios.User{{Email: "user@example.com"}})) + + route := up.Route(). + From(env.SubdomainURL("postgres-test")). + PPL(`{"allow":{"and":["email":{"is":"user@example.com"}]}}`) + env.AddUpstream(up) + + env.Start() + snippets.WaitStartupComplete(env) + + resp, err := up.Get(route, upstreams.AuthenticateAs("user@example.com"), upstreams.Path("/foo")) + assert.NoError(t, err) + io.ReadAll(resp.Body) + resp.Body.Close() + + env.Stop() + + results := tracetest.NewTraceResults(receiver.FlushResourceSpans()) + traces, exists := results.GetTraces().ByParticipant["Data Broker"] + require.True(t, exists) + require.Len(t, traces, 1) + var found bool + for _, span := range traces[0].Spans { + if span.Scope.GetName() == "github.com/exaring/otelpgx" { + found = true + break + } + } + assert.True(t, found, "no spans with otelpgx scope found") + }) +}