gRPC的Go库提供了用于创建您自己的自定义拦截器(即中间件函数)的接口,而我正尝试编写两个日志记录拦截器。第一个是一元服务器拦截器,使用传递到拦截器函数中的对象,我可以轻松地记录请求参数。
func loggingUnary(context context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(context, req)
printLogMessage(err, info.FullMethod, context, time.Since(start), req)
return resp, err
}
如何使用无法方便地将请求对象作为参数传递的Stream Server Interceptor进行相同操作?还有另一种访问请求的方法吗?
func loggingStream(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
start := time.Now()
err := handler(srv, stream)
printLogMessage(err, info.FullMethod, stream.Context(), time.Since(start), "")
return err
}
答案 0 :(得分:2)
现在有点老了,但是将拦截扩展到流中的最简单方法是创建一个 grpc.ServerStream 包装器,然后将真实的ServerStream包装在拦截器中。这样,您的拦截代码就可以处理流中已接收和已发送的消息。
// A wrapper for the real grpc.ServerStream
type LoggingServerStream struct {
inner grpc.ServerStream
}
func (l LoggingServerStream) SetHeader(m metadata.MD) error {
return l.SetHeader(m)
}
func (l LoggingServerStream) SendHeader(m metadata.MD) error {
return l.SendHeader(m)
}
func (l LoggingServerStream) SetTrailer(m metadata.MD) {
l.SetTrailer(m)
}
func (l LoggingServerStream) Context() context.Context {
return l.Context()
}
func (l LoggingServerStream) SendMsg(m interface{}) error {
fmt.Printf("Sending Message: type=%s\n", reflect.TypeOf(m).String())
return l.SendMsg(m)
}
func (l LoggingServerStream) RecvMsg(m interface{}) error {
fmt.Printf("Receiving Message: type=%s\n", reflect.TypeOf(m).String())
return l.RecvMsg(m)
}
拦截器:
func LoggingStreamInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
return handler(srv, LoggingServerStream{inner:ss})
}
}
任何状态下,您都需要保存并放入包装纸。
答案 1 :(得分:0)
在创建流的请求的生存期内,可以多次调用流处理程序,这就是为什么该请求不属于处理程序(或任何拦截器)参数的原因。您可以将请求(或者更好的是,您要记录的数据的副本,而不是对请求本身的引用)放置在流上下文中(假设您在控制创建ServerStream对象的代码的控制之下)。我宁愿在创建流时记录一次请求参数,而不是在每次调用处理程序时都记录一次(因此每个请求仅记录一次)。