Open
Description
After otelgrpc.StreamClientInterceptor() was deprecated, it is recommended to replace it with otelgrpc.NewClientHandler().
However, the execution order is different: interceptors run first, followed by StatsHandlers. From the code, this seems to be the intended behavior. But how should one proceed if it’s necessary for the trace_id and span_id fields to appear in the logs?
Before (logs with traicing fields):
func SpanContextFields(ctx context.Context) logging.Fields {
if spanCtx := trace.SpanContextFromContext(ctx); spanCtx.IsSampled() {
return logging.Fields{
traceIDKey, spanCtx.TraceID().String(),
spanIDKey, spanCtx.SpanID().String(),
}
}
return nil
}
streamInterceptors := []grpc.StreamClientInterceptor{
otelgrpc.StreamClientInterceptor(),
logging.StreamClientInterceptor(
logger.GRPCLogger(zap.L()),
logging.WithLevels(logger.GRPCClientCodeToLevel),
logging.WithFieldsFromContext(logger.SpanContextFields),
),
}
return grpc.NewClient(
target,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithChainUnaryInterceptor(append(unaryInterceptors, o.unaryInterceptors...)...),
grpc.WithChainStreamInterceptor(append(streamInterceptors, o.streamInterceptors...)...),
)
Now (logs without traicing fields):
func SpanContextFields(ctx context.Context) logging.Fields {
if spanCtx := trace.SpanContextFromContext(ctx); spanCtx.IsSampled() {
return logging.Fields{
traceIDKey, spanCtx.TraceID().String(),
spanIDKey, spanCtx.SpanID().String(),
}
}
return nil
}
streamInterceptors := []grpc.StreamClientInterceptor{
logging.StreamClientInterceptor(
logger.GRPCLogger(zap.L()),
logging.WithLevels(logger.GRPCClientCodeToLevel),
logging.WithFieldsFromContext(logger.SpanContextFields),
),
}
return grpc.NewClient(
target,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
grpc.WithChainUnaryInterceptor(append(unaryInterceptors, o.unaryInterceptors...)...),
grpc.WithChainStreamInterceptor(append(streamInterceptors, o.streamInterceptors...)...),
)
Activity
johanbrandhorst commentedon Jan 21, 2025
The statshandler runs both before and after the interceptor. I don't know if it's possible to reproduce the exact same behavior using the stats handler.