You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中发生panic时如何捕获并记录gRPC请求信息

解决gRPC双向流服务Panic时记录请求内容的问题

要实现panic时记录请求(尤其是Messages内容),需要基于go-grpc-middleware实现流式服务拦截器,同时自定义流包装器捕获并保存收到的请求数据,最后在panic恢复时提取这些数据日志。

步骤1:自定义请求捕获的流包装器

重写ServerStream的RecvMsg方法,每次收到客户端请求时保存一份副本,避免后续修改影响日志记录:

import (
    "log"
    "runtime/debug"

    "google.golang.org/grpc"
    grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
    pb "github.com/path/to/go-package" // 替换为你的proto生成代码路径
)

// requestCapturingStream 包装原生ServerStream,存储收到的所有请求
type requestCapturingStream struct {
    grpc.ServerStream
    receivedRequests []*pb.TheRequest
}

// RecvMsg 重写RecvMsg,捕获并保存每个请求
func (s *requestCapturingStream) RecvMsg(m interface{}) error {
    if req, ok := m.(*pb.TheRequest); ok {
        // 深拷贝请求对象,防止原数据被业务逻辑修改
        copiedReq := &pb.TheRequest{}
        *copiedReq = *req
        s.receivedRequests = append(s.receivedRequests, copiedReq)
    }
    return s.ServerStream.RecvMsg(m)
}

步骤2:实现Panic恢复拦截器

在拦截器中通过defer recover()捕获panic,同时取出保存的请求数据进行日志记录:

// PanicRecoveryInterceptor 流式服务的panic恢复拦截器,负责记录请求信息
func PanicRecoveryInterceptor() grpc.StreamServerInterceptor {
    return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
        // 初始化自定义流包装器
        capturingStream := &requestCapturingStream{ServerStream: ss}

        // 捕获panic并输出日志
        defer func() {
            if r := recover(); r != nil {
                // 记录panic详情和调用栈
                log.Printf("Panic触发: %v\n调用栈:\n%s", r, string(debug.Stack()))

                // 遍历所有收到的请求,根据oneof类型提取内容
                for idx, req := range capturingStream.receivedRequests {
                    switch payload := req.Payload.(type) {
                    case *pb.TheRequest_Messages:
                        log.Printf("收到的请求#%d - Messages内容: %v", idx+1, payload.Messages.Messages)
                    case *pb.TheRequest_Config:
                        log.Printf("收到的请求#%d - Config内容: %+v", idx+1, payload.Config)
                    }
                }
            }
        }()

        // 调用实际业务处理逻辑
        return handler(srv, capturingStream)
    }
}

步骤3:注册拦截器到gRPC服务

创建gRPC服务器时,将自定义拦截器通过grpc_middleware.ChainStreamServer注册进去:

// NewGRPCServer 创建带拦截器的gRPC服务器
func NewGRPCServer() *grpc.Server {
    return grpc.NewServer(
        grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
            PanicRecoveryInterceptor(),
            // 可添加其他需要的流式拦截器
        )),
        // 若有一元RPC服务,可添加grpc.UnaryInterceptor(...)配置一元拦截器
    )
}

关键注意事项

  • 必须使用grpc.StreamServerInterceptor:你的服务是双向流式RPC,一元拦截器无法处理流式请求
  • 深拷贝请求:确保日志记录的是请求接收时的原始状态,避免业务逻辑修改请求后日志失真
  • 日志适配:可替换标准库log为结构化日志库(如zap、logrus),提升日志可读性和可分析性

内容的提问来源于stack exchange,提问作者ardianbsyla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 12:15:33