grpcutil

grpcutil carries trace IDs through gRPC metadata, maps between gRPC and HTTP status codes, and defines the instrumentation contract of gRPC servers and clients.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/grpcutil"

The shared helpers under grpcserver and grpcclient, exported for custom interceptors and for services that use plain grpc-go.

ctx, id := grpcutil.OutgoingContextWithTraceID(ctx, grpcutil.DefaultTraceIDMetadataKey, traceid.DefaultValue)

Trace IDs in metadata

gRPC carries request-scoped values in metadata, not HTTP headers. The helpers bridge metadata and the traceid context value:

FunctionDirection
TraceIDFromIncomingContextincoming metadata to a validated ID, or the default
OutgoingContextWithTraceIDcontext value to outgoing metadata, replacing any existing value
ValidTraceIDMetadataKeywhether a key can carry a trace ID at all

Both directions validate the ID with traceid.Valid. A value arriving from a caller is input, and an unvalidated one ends up verbatim in every log line of the request.

DefaultTraceIDMetadataKey is x-request-id. gRPC metadata keys are case-insensitive and sent in lowercase, so it interoperates with HTTP services that use X-Request-ID, and a request keeps one ID across both transports.

One error, two transports

A service exposing REST and gRPC should report the same failure the same way on both.

  • HTTPStatusFromCode maps a gRPC code to the HTTP status documented for it in google.rpc.Code.
  • CodeFromHTTPStatus maps an HTTP status returned by application logic to the closest gRPC code.
  • ErrorCode returns the code of any error, including wrapped status errors and context errors.

The mapping is not symmetric, because the code sets are not the same size: several gRPC codes share one HTTP status. Map from your domain errors to each transport directly when the distinction matters, and use these where an existing classification has to cross over.

RPC logging helpers

SplitFullMethod and ParseFullMethod split /package.Service/Method into service and method; the second also rejects malformed names. DumpMessage renders a protobuf message as JSON for a log entry, capped by encoded size and passed through a RedactFn.

The instrumentation contract

ServerInstrumenter and ClientInstrumenter supply the server or dial options that add metrics or tracing. A server instrumenter that also implements ServerInitializer is handed the server once every service is registered, which is how prometheusgrpc creates its zero-valued series.

They exist as separate interfaces because metrics.Client is stable within v1 and cannot gain gRPC methods. Keeping them here also keeps gRPC out of the build of every metrics backend.

Dependencies

Reaches google.golang.org/grpc, google.golang.org/protobuf, google.golang.org/genproto/googleapis/rpc, and golang.org/x/net, x/sys and x/text.