Observability
Connect stays close to net/http
, which means any logging, tracing, or metrics that work with an http.Handler
or http.Client
will also work with Connect. In particular, the otelhttp OpenTelemetry package and the ochttp OpenCensus package both integrate seamlessly with Connect servers and clients.
For more detailed, RPC-focused metrics, use the otelconnect package. otelconnect works with your OpenTelemetry metrics and tracing setup to capture information such as:
rpc.system
: Was this call gRPC, gRPC-Web, or Connect?rpc.service
andrpc.method
: What service and method was called?responses_per_rpc
: How many messages were written to streaming responses?error_code
/status_code
: What specific gRPC or Connect error was returned?
OpenTelemetry can be quite complex, so this guide assumes that readers are familiar with:
- What observability is.
- A basic understanding of OpenTelemetry metrics and tracing.
- How TextMapPropagators, MeterProviders, and TraceProviders are initialized and used.
Enabling OpenTelemetry for Connect
Once you have OpenTelemetry set up in your application, enabling OpenTelemetry in a Connect project is as simple as adding the otelconnect.NewInterceptor option on Connect handler and client constructors. If you do not have OpenTelemetry in your application, you can refer to the OpenTelemetry Go getting started guide.
func main() {
mux := http.NewServeMux()
mux.Handle(pingv1connect.NewPingServiceHandler(
&pingv1connect.UnimplementedPingServiceHandler{},
connect.WithInterceptor(
otelconnect.NewInterceptor(),
),
))
http.ListenAndServe("localhost:8080", mux)
}
func makeRequest() {
client := pingv1connect.NewPingServiceClient(
http.DefaultClient,
"http://localhost:8080",
connect.WithInterceptor(
otelconnect.NewInterceptor(),
),
)
resp, err := client.Ping(
context.Background(),
connect.NewRequest(&pingv1.PingRequest{}),
)
if err != nil {
log.Fatal(err)
}
log.Print(resp)
}
By default, this will use:
- TextMapPropagator from
otel.GetTextMapPropagator()
- MeterProvider from
global.MeterProvider()
- TracerProvider from
otel.GetTracerProvider()
Using custom MeterProvider, TraceProvider and TextMapPropagators
When running multiple applications in a single binary, or if different sections of code should use different exporters, pass the correct exporters to otelconnect.NewInterceptor explicitly:
- otelconnect.WithTracerProvider to set the TracerProvider
- otelconnect.WithMeterProvider to set the MeterProvider
- otelconnect.WithPropagator to set the TextMapPropagator
// newInterceptor instruments Connect clients and handlers using custom OpenTelemetry metrics, tracing, and propagation.
func newInterceptor(tp trace.TracerProvider, mp metric.MeterProvider, p propagation.TextMapPropagator) connect.Interceptor {
return otelconnect.NewInterceptor(
otelconnect.WithTracerProvider(tp),
otelconnect.WithMeterProvider(mp),
otelconnect.WithTextMapPropagator(p),
)
}
Configuration for internal microservices
By default, otelconnect-instrumented servers are conservative and behave as though they're internet-facing. They don't trust any tracing information sent by the client, and will create new trace spans for each request. The new spans are linked to the remote span for reference (using OpenTelemetry's trace.Link), but tracing UIs will display the request as a new top-level transaction.
If your server is deployed as an internal microservice, configure otelconnect to trust the client's tracing information using otelconnect.WithTrustRemote. With this option, servers will create child spans for each request.
Reducing metrics and tracing cardinality
By default, the OpenTelemetry RPC conventions produce high-cardinality server-side metric and tracing output. In particular, servers tag all metrics and trace data with the server's IP address and the remote port number. To drop these attributes, use otelconnect.WithoutServerPeerAttributes. For more customizable attribute filtering, use otelconnect.WithFilter.