Compose gRPC interceptors¶
transit's grpc package mirrors the HTTP package for gRPC: server interceptors you
install on a grpc.Server, and client instrumentation you pass as dial options and
client interceptors.
Server interceptors¶
Server interceptors are grouped as an Interceptor{Unary, Stream} pair (either field
may be nil). NewInterceptorChain collects them and ServerOptions() turns the chain
into the grpc.ServerOption values that install it via ChainUnaryInterceptor /
ChainStreamInterceptor.
| Constructor | Returns | Purpose |
|---|---|---|
LoggingInterceptor(log, opts…) |
Interceptor |
One structured record per RPC. |
RateLimitInterceptor(log, cfg) |
Interceptor |
Token-bucket admission control. |
OTelStatsHandler(opts…) |
grpc.ServerOption |
OpenTelemetry server spans + rpc.server.* metrics. |
OTelStatsHandler is a stats handler, not an interceptor — that is the shape the OTel
gRPC contrib library ships — so pass it to grpc.NewServer alongside the chain's
options rather than into NewInterceptorChain:
chain := transitgrpc.NewInterceptorChain(
transitgrpc.LoggingInterceptor(log),
transitgrpc.RateLimitInterceptor(log, transitgrpc.DefaultRateLimitConfig()),
)
opts := append(chain.ServerOptions(), transitgrpc.OTelStatsHandler())
srv := grpc.NewServer(opts...)
LoggingInterceptor takes WithGRPCLogLevel, WithGRPCPathFilter and
WithoutGRPCLatency, mirroring the HTTP logger.
Client instrumentation¶
On the client, OpenTelemetry is a dial option and the circuit breaker is a standard gRPC client interceptor:
| Constructor | Returns | Purpose |
|---|---|---|
OTelClientHandler(opts…) |
grpc.DialOption |
Client spans + trace-context propagation. |
CircuitBreakerInterceptor(log, cfg) |
grpc.UnaryClientInterceptor |
Fail fast on a consistently failing server. |
CircuitBreakerStreamInterceptor(log, cfg) |
grpc.StreamClientInterceptor |
The streaming equivalent. |
conn, err := grpc.NewClient(
"dns:///orders.internal:443",
grpc.WithTransportCredentials(creds),
transitgrpc.OTelClientHandler(),
grpc.WithUnaryInterceptor(
transitgrpc.CircuitBreakerInterceptor(log, transitgrpc.DefaultCircuitBreakerConfig()),
),
)
OTelClientHandler injects the trace context into outgoing metadata using the global
propagator, so a downstream gRPC server continues the same trace rather than starting a
new one. Like the server handler, it reads the globally-installed providers and is a
noop until those are set up (see
go/observability).
Config merging¶
Both CircuitBreakerConfig and RateLimitConfig have Merge…Config helpers that apply
only explicitly-flagged override fields onto a base, leaving function fields (custom
failure predicates, key extractors) under your control. This is how a service layers
file/env/flag config onto the defaults without a field-by-field copy.