通过gRPC拦截器可实现非侵入式日志收集,1. 使用UnaryInterceptor记录请求方法、耗时、错误等信息;2. 在拦截器中提取trace_id、客户端IP并做脱敏处理;3. 结合grpc-middleware链式注册多个拦截器;4. 推荐使用zap等结构化日志库提升可维护性。

在使用 Golang 构建基于 gRPC 的微服务时,日志收集是可观测性的重要组成部分。通过 gRPC 拦截器(Interceptor),我们可以在不侵入业务逻辑的前提下,统一记录请求和响应信息,实现高效、集中的日志管理。
什么是 gRPC 拦截器
gRPC 拦截器类似于中间件,允许你在请求被处理之前或响应返回之后执行一些通用逻辑。Golang 的 gRPC-Go 库支持两种类型的拦截器:
- Unary Interceptor:用于处理一元调用(普通 RPC 方法)
- Stream Interceptor:用于处理流式调用(客户端流、服务端流、双向流)
日志收集主要关注一元拦截器,因为它覆盖了大多数常见的 API 调用场景。
实现一元日志拦截器
下面是一个简单的日志拦截器实现,用于记录每次请求的开始时间、方法名、耗时和错误信息。
立即学习“go语言免费学习笔记(深入)”;
func loggingUnaryInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
// 记录请求开始
start := time.Now()
log.Printf("Started %s at %v", info.FullMethod, start)
// 调用实际的处理函数
resp, err := handler(ctx, req)
// 记录请求结束
duration := time.Since(start)
if err != nil {
log.Printf("Completed %s with error: %v, duration: %v", info.FullMethod, err, duration)
} else {
log.Printf("Completed %s successfully, duration: %v", info.FullMethod, duration)
}
return resp, err
}
这个拦截器可以捕获每个请求的基本信息,并输出到标准日志中。你可以将其替换为更强大的日志库如 zap 或 logrus,以便支持结构化日志和日志级别控制。
注册拦截器到 gRPC 服务器
要在 gRPC 服务中启用该拦截器,需要在创建服务器时注册它:
server := grpc.NewServer(
grpc.UnaryInterceptor(loggingUnaryInterceptor),
)
如果你已经在使用其他拦截器(如认证、限流),可以使用 grpc-middleware 包来组合多个拦截器:
import "github.com/grpc-ecosystem/go-grpc-middleware"
server := grpc.NewServer(
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
loggingUnaryInterceptor,
authUnaryInterceptor,
recoveryInterceptor,
)),
)
增强日志内容建议
为了提升日志的实用性,可以在拦截器中加入以下信息:
- 从上下文中提取 trace_id 或 request_id,便于链路追踪
- 记录客户端 IP 地址(通过
peer.FromContext(ctx)获取) - 对敏感字段进行脱敏处理(如密码、token)
- 根据配置决定是否记录请求体/响应体(避免日志过大)
例如获取客户端地址:
if peer, ok := peer.FromContext(ctx); ok {
log.Printf("Client address: %s", peer.Addr.String())
}
基本上就这些。通过 gRPC 拦截器实现日志收集,既简洁又高效,能显著提升服务的可维护性和问题排查效率。只要合理设计日志格式和内容,就能为后续的监控、告警和分析打下良好基础。










