Subchapter 93.4
references/go.mdMarkdown7 KBView on GitHub
AI agents: this is one page from PostHog’s docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt (opens in a new tab)
Cloud region: This page uses US Cloud hosts. For an EU Cloud project, use
https://eu.i.posthog.cominstead ofhttps://us.i.posthog.com(ingestion host), and usehttps://eu.posthog.cominstead ofhttps://app.posthog.com(app host for links).
1
Required
For the complete SDK reference, see the OpenTelemetry Go docs (opens in a new tab).
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttpotlptracehttp is the OTLP HTTP/protobuf trace exporter. The otlptracegrpc module sends gRPC, so pick otlptracehttp to match this guide.
2
Required
You’ll need your PostHog project token to authenticate trace requests. This is the same token you use for capturing events with the PostHog SDK.
Important: Use your project token which starts with
phc_. Do not use a personal API key (which starts withphx_).
You can find your project token in Project settings (opens in a new tab).
3
Required
Set up the OpenTelemetry SDK to export spans to PostHog over OTLP HTTP. Use WithEndpointURL so the full /i/v1/traces path is sent as-is.
package main
import (
"context"
"go.opentelemetry.io/otel
4
Required
Wrap the operations you want to measure in spans, and attach attributes for context.
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
var tracer = otel.Tracer
5
Recommended
Install the optional PostHog OpenTelemetry module:
go get github.com/posthog/posthog-go/otelRegister the context processor on your existing provider before requests start, then wrap your HTTP handler:
import posthogotel "github.com/posthog/posthog-go/otel"
tp.RegisterSpanProcessor(posthogotel.NewContextSpanProcessor(client))
handler :=6
Recommended
Once everything is configured, confirm spans are reaching PostHog:
Checkpoint
What you can do with your traces
| Action | Description |
|---|---|
| Why you need distributed tracing (opens in a new tab) | What a trace shows you that nothing else does |
| Explore traces | Read a trace as a waterfall to see where time goes |
| Filter spans | Narrow down by service, status, duration, and attributes |
| Propagate context | Pass trace context across services so spans join the same trace |
Call defer tp.Shutdown(ctx) in main so buffered spans flush on exit.
Alternatively, configure the exporter with environment variables and call otlptracehttp.New(ctx) with no options:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://us.i.posthog.com/i/v1/traces"
OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer <ph_project_token>"
OTEL_SERVICE_NAME="my-service"Note:
WithEndpointsets only the host and defaults the path to/v1/traces. To send to the full/i/v1/tracespath, useWithEndpointURL(or the per-signalOTEL_EXPORTER_OTLP_TRACES_ENDPOINT), as shown.
The wrapper accepts the core request middleware options (opens in a new tab). Configure tracing_headers (opens in a new tab) for your backend hostname in the browser SDK. Put OpenTelemetry HTTP middleware outside the PostHog wrapper to enrich its existing request span. Create child spans with r.Context() so the processor can read the request IDs.
The bridge adds missing sessionId and posthogDistinctId attributes. Existing sessionId and posthogDistinctId attributes take precedence. Set authenticated application identity with posthogDistinctId. Missing IDs stay absent. The optional module keeps your existing provider, exporter, and sampler. It does not change the core SDK dependencies.
Request context expires when the handler returns or panics. Spans that already started keep their attributes. New spans from an ended request context omit its IDs. For a separate background job, start from a fresh context, such as context.Background() with posthog.WithFreshRequestContext, and set the job’s IDs explicitly. Manual WithRequestContext use has no automatic HTTP lifetime management.