Go monitoring and Golang APM
Wrap your handlers with OpenTelemetry Go and Obseria shows traces, goroutines, heap, GC and scheduler latency for every Go service, correlated with CPU profiles, logs and Kubernetes.


Why monitor Go with Obseria
Standard, stable instrumentation
Traces and metrics in the OpenTelemetry Go SDK are stable, and contrib packages cover net/http, gRPC, database/sql drivers and popular frameworks.
Goroutines and GC in context
Goroutine counts, heap size and the GC goal sit next to the traces they slow down, so leaks and allocation pressure are easy to spot.
Down to the function
Continuous profiling with under 1% overhead shows which functions burn CPU or allocate memory in production.
Go libraries and runtimes Obseria monitors
- net/http servers and clients. otelhttp wraps handlers and transports, creating server and client spans with route, status code and latency.
- gRPC. otelgrpc stats handlers trace unary and streaming calls and propagate context across services.
- Gin, Echo and other web frameworks. Contrib middleware for common routers adds spans with route templates instead of raw paths.
- database/sql, Redis and Kafka clients. Instrumented drivers and clients turn queries, cache calls and messages into spans with timing.
- Go runtime. The contrib runtime package reports goroutines, memory, GC goal, GOMAXPROCS and scheduler latency per OpenTelemetry semantic conventions.
- Containers and Kubernetes. The Obseria Agent enriches every Go service with pod, node and container metadata.
The Go signals that matter
Obseria collects these values automatically and shows them in ready-made dashboards and alerts.Response speed
Shows how quickly your Go services answer requests, so slow pages and APIs are noticed before customers complain.
Parallel work
Tells you whether the many small tasks inside the application finish normally or get stuck and wait too long.
Memory usage
Shows how much memory the application uses compared with its limit, so restarts caused by full memory are avoided.
Memory churn
Reveals how much short-lived data the application creates, which drives extra cleanup work and wasted computing power.
Show all 9 metrics in detail
| Metric | Unit | What it tells you |
|---|---|---|
http.server.request.duration | s | Latency histogram of inbound HTTP requests per route and status code, recorded by otelhttp. |
go.goroutine.count | {goroutine} | Live goroutines; a count that only goes up points to a goroutine leak. |
go.schedule.duration | s | Time goroutines wait to be scheduled; high values mean CPU starvation or GOMAXPROCS limits. |
go.memory.used | By | Memory used by the Go runtime, the basis for leak and capacity analysis. |
go.memory.limit | By | Soft memory limit from GOMEMLIMIT, reported when a limit is set. |
go.memory.allocated | By | Bytes allocated on the heap; a steep rate drives GC work. |
go.memory.allocations | {allocation} | Number of heap allocations, to find allocation-heavy code paths. |
go.memory.gc.goal | By | Heap size target for the next GC cycle, set by GOGC and GOMEMLIMIT. |
go.processor.limit | {thread} | GOMAXPROCS, the number of OS threads that can run Go code at once. |
Set up Go monitoring in minutes
- Step 1
Add the SDK and exporters
go get the OpenTelemetry Go SDK, the OTLP HTTP exporters for traces and metrics and the contrib runtime package.
- Step 2
Initialize providers
Create tracer and meter providers at startup, call runtime.Start and wrap your mux with otelhttp.NewHandler.
- Step 3
Point it to Obseria
Set OTEL_SERVICE_NAME, the Obseria OTLP endpoint and your token as environment variables; traces and runtime metrics arrive within minutes.
// Modules: go.opentelemetry.io/otel/sdk, .../otel/sdk/metric, .../otlptracehttp,
// .../otlpmetrichttp, go.opentelemetry.io/contrib/instrumentation/runtime and .../otelhttp
// OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS
// (Authorization=Bearer%20${OBSERIA_TOKEN}) are read from the environment.
func main() {
ctx := context.Background()
traceExp, err := otlptracehttp.New(ctx)
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(traceExp))
defer tp.Shutdown(ctx)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
metricExp, err := otlpmetrichttp.New(ctx)
if err != nil {
log.Fatal(err)
}
reader := sdkmetric.NewPeriodicReader(metricExp, sdkmetric.WithProducer(runtime.NewProducer()))
mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
defer mp.Shutdown(ctx)
otel.SetMeterProvider(mp)
runtime.Start() // go.goroutine.count, go.memory.used, go.memory.gc.goal ...
mux := http.NewServeMux()
mux.HandleFunc("/orders", handleOrders)
log.Fatal(http.ListenAndServe(":8080", otelhttp.NewHandler(mux, "orders-api")))
}Out of the box
Dashboards, alerts and SLOs that are ready the moment data arrives.
- Go runtime: goroutines, heap, GC goal, scheduler
- Service health with RED metrics per route
- Alert on steadily rising goroutine count
- Alert on memory approaching GOMEMLIMIT
- Slowest gRPC methods and downstream calls
Go monitoring: what matters
Why Go monitoring matters
Go is the language of cloud infrastructure and high-throughput backends: API gateways, payment services, Kubernetes operators and CLIs. Go services are usually lean and fast, which is exactly why problems are easy to miss until they hurt. A goroutine leak grows quietly for days, a container memory limit that does not match GOMEMLIMIT causes sudden restarts, and a CPU limit that is lower than GOMAXPROCS leads to throttling and tail latency. Go monitoring brings these runtime details together with request traces so you can tell code, runtime and infrastructure apart.
Unlike Java or Python, Go compiles to a static binary, so the main path for Golang APM is the OpenTelemetry Go SDK plus contrib instrumentation libraries: otelhttp for net/http, otelgrpc for gRPC and wrappers for database drivers and frameworks. Traces and metrics in the Go SDK are stable. For teams that cannot touch code, OpenTelemetry also works on zero-code options for Go, including the eBPF-based opentelemetry-go-instrumentation project and compile-time instrumentation; check their current maturity before relying on them in production.
Signals that matter for Go performance monitoring
- Request latency and errors:
http.server.request.durationper route plus error spans give rate, errors and duration for each service. - Goroutines:
go.goroutine.countshould rise and fall with traffic. A count that only rises is the classic sign of a leak, usually a blocked channel or a missing context cancellation. - Scheduler latency:
go.schedule.durationshows how long runnable goroutines wait for a thread. High values with moderate CPU often mean CPU throttling in the container. - Memory and GC:
go.memory.used,go.memory.gc.goalandgo.memory.limitshow how close the heap is to its target and limit;go.memory.allocatedandgo.memory.allocationsshow the pressure behind GC work. - Parallelism:
go.processor.limitreveals the effective GOMAXPROCS, which should match the CPU you actually get.
Common Go performance problems and how to detect them
Goroutine leaks
Goroutines that wait forever on a channel, a lock or a request without timeout accumulate. Memory follows the goroutine count upward. A trend alert on goroutines per instance catches the leak long before an out-of-memory kill.
GC pressure
Short-lived allocations in hot paths, such as JSON encoding or string building, force frequent GC cycles. Allocation profiles from continuous profiling show which functions to optimize, and the heap goal metric shows the effect.
CPU throttling in containers
If GOMAXPROCS is higher than the container CPU limit, the runtime schedules more work than the quota allows. You see rising scheduler latency and p99 spikes while average CPU looks fine.
Slow downstream calls
Missing timeouts and exhausted connection pools let one slow dependency block many goroutines. Client spans for HTTP, gRPC and SQL make the waiting time visible in every trace.
Retries that amplify load
Aggressive retries in Go clients can turn a short slowdown of one dependency into an overload. Traces with repeated client spans for the same operation, together with a jump in outbound request rate, reveal retry storms quickly.
Best practices for Golang monitoring
- Pass
context.Contextthrough every call so spans connect and cancellations propagate. - Set
service.name,service.versionanddeployment.environment.namevia environment variables to compare releases. - Align
GOMEMLIMITand GOMAXPROCS with container limits, and monitor both runtime and cgroup metrics. - Use route templates as span names to keep cardinality low.
- Combine traces with continuous profiling to move from a slow span to the responsible function.
How Obseria helps with Golang APM
Obseria accepts OpenTelemetry Go traces and metrics over OTLP, so you keep the standard SDK and no proprietary agent is needed. The Obseria Agent adds host, container and Kubernetes context, and TopoGraph maps each Go service to its callers, databases and queues. Continuous profiling with under 1% overhead links hot functions to slow traces. When latency rises, Obseria Intelligence correlates goroutine growth, GC pressure, throttling or a deployment and presents the root cause with its chain of evidence. Dashboards, alerts and SLOs are included.
Related integrations
Related capabilities
Distributed Tracing
Follow each request across services, queues and databases with OpenTelemetry-native distributed tracing, and find the slow span or failing dependency in seconds.
Learn moreContinuous Profiling
Always-on continuous profiling shows which functions consume CPU, memory and locks in production, with under 1% overhead and direct links to traces.
Learn moreKubernetes Monitoring
See every cluster, node, pod and workload in context. Obseria connects Kubernetes metrics, events, logs and traces so you find the cause of a failing rollout in minutes.
Learn moreLearn more
What is APM (application performance monitoring)?
What is APM? Learn how application performance monitoring measures latency, throughput and errors, how distributed tracing works, and which metrics matter.
What is OpenTelemetry?
What is OpenTelemetry? Learn how OTel's APIs, SDKs, Collector, OTLP and semantic conventions produce vendor-neutral traces, metrics, logs and profiles.
Go monitoring FAQ
The common approach is the OpenTelemetry Go SDK with contrib libraries such as otelhttp and otelgrpc, which needs a few lines at startup. OpenTelemetry also develops eBPF-based and compile-time zero-code options for Go; evaluate their current status for your workloads.
Everything the contrib runtime package emits, including go.goroutine.count, go.memory.used, go.memory.gc.goal, go.processor.limit and, with its producer, go.schedule.duration.
The OpenTelemetry Go SDK is designed for production use. Overhead depends on span volume, so sample routine traffic and keep span names low in cardinality. Continuous profiling in Obseria runs at under 1% overhead.
Yes. Obseria ingests Prometheus metrics via scraping or remote write and supports PromQL, so existing client_golang metrics and dashboards keep working next to OpenTelemetry data.
Yes. Deploy the Obseria Agent as a DaemonSet via Helm and set the OTEL_* variables in your pod spec. Traces are enriched with pod, namespace and node metadata.
Start monitoring Go today
Connect Go in minutes with OpenTelemetry or the Obseria Agent. Free for 14 days.