Instrumenting Go with OpenTelemetry means using the OpenTelemetry Go SDK and library instrumentation to emit traces and metrics over OTLP.
Instrumenting Go with OpenTelemetry means using the OpenTelemetry Go SDK and library instrumentation to emit traces and metrics over OTLP. Go has no bytecode agent, so instrumentation is explicit but lightweight.
For a Go service you watch per-handler latency and error rate, the latency of downstream calls, and the Go runtime: goroutine count, heap allocation, and garbage-collection pauses. Two Go realities shape this: goroutines are cheap to create and easy to leak, so a climbing goroutine count is a leading indicator of a resource leak, and Go services are often high-throughput, so tail latency (p99) matters more than averages.
The most useful correlation is handler latency against goroutine count and GC pauses on one timeline: a latency problem that lines up with a goroutine climb is a leak, and one that lines up with GC is allocation pressure, two very different fixes.
Unlike Java or Node.js, Go has no bytecode agent, so you wire instrumentation explicitly in code, which is more work but gives precise control. You initialise the OpenTelemetry SDK with an OTLP exporter, add the library instrumentation, otelhttp for HTTP handlers and clients, otelgrpc for gRPC, and the appropriate wrappers for your database driver, and add the runtime instrumentation that emits goroutine, memory and GC metrics.
The discipline that makes or breaks Go tracing is context propagation: you must thread context.Context through your call chain so child spans attach to the request. Where it is passed correctly, a trace shows exactly which downstream call dominates a slow request; where it is dropped, the trace breaks and you lose the breakdown.
The signals that explain most Go service incidents:
| Signal | Source | What it tells you / watch for |
|---|---|---|
http.server.request.duration | otelhttp | Per-handler latency histogram; watch p99, not the average, for high-throughput services. |
runtime.go.goroutines | runtime instrumentation | Live goroutine count; a steady climb is the signature of a goroutine leak. |
runtime.go.mem.heap_alloc | runtime instrumentation | Heap in use; a rising floor indicates a memory leak. |
runtime.go.gc.pause_ns | runtime instrumentation | GC pause time; frequent or long pauses come from a high allocation rate. |
downstream span duration | otelhttp / db wrappers | Latency of DB and API calls inside a request; usually where a slow request's time goes. |
panic / error span status | SDK | Error-status spans and recovered panics that would otherwise be invisible. |
A Go dashboard should let you tell a leak from allocation pressure from a slow dependency. Row one: handler p99 latency and error rate. Row two: goroutine count and heap allocation over time (both should be flat under steady load; a rising trend is a leak), and GC pause time. Row three: downstream call latency by target.
# p99 latency by handler
histogram_quantile(0.99, http.server.request.duration) by (http.route)
# goroutine count: a steady climb under flat load = leak
runtime.go.goroutines
# GC pause time (allocation pressure)
rate(runtime.go.gc.pause_ns[5m])
Memory and file descriptors climb over time and the service eventually degrades, because goroutines are being started but never returning, often blocked on a channel or a request without a timeout. runtime.go.goroutines climbing under steady load is the tell. Add context deadlines and ensure every goroutine has an exit path.
Latency shows periodic spikes that line up with garbage collection, because the service allocates heavily and the GC runs often. runtime.go.gc.pause_ns confirms it. Reduce allocations on the hot path (reuse buffers, avoid unnecessary copies) rather than just adding memory.
One handler's p99 rises and its downstream span, a query or an API call, accounts for the extra time while the runtime panels stay flat. The fix is in the dependency; the trace makes that unambiguous.
Starting thresholds to tune:
| Condition | Signal | Meaning |
|---|---|---|
| handler 5xx rate > 2% for 5m | errors | A handler is failing. |
| p99 latency > SLO for 10m | latency | Handler regression. |
| goroutines climbing over 30m under flat load | leak | Goroutine leak developing. |
| GC pause time > 10% of wall time | GC | Allocation pressure hurting latency. |
Ops Singularity's TelemetryOps ingests Go OpenTelemetry data natively, and Sentinel AI resolves the service incidents it surfaces through governed Action Tickets. Explore TelemetryOps.
Go has no bytecode agent, so instrumentation is added explicitly in code using the OpenTelemetry Go SDK and library packages like otelhttp and otelgrpc, with context propagated through the call chain.
Per-handler latency and error rate, goroutine counts and memory, downstream call latency, and panics, with attention to tail latency for high-throughput services.
Bring a real incident. We will show you Sentinel investigate, act and verify end to end, with every action reversible and audited.