Java:gRPC中如何将输入输出报文以json格式打印到日志文件中

       gRPC是使用二进制格式传输报文的,有时项目需要记录请求调用输入输出日志,以方便错误排查。但以二进制记录报文的话不方便查看,如果记录的日志是以json或XML的形式,则更方便查看,下面本文将介绍如果将输入输出报文以json的形式记录到日志文件中。

       首先,实现一个用户打印日志的Marshaller,它创建时需要传入实现进行报文转换的Marshaller,此Marshaller只是把转换后的object转换成json(解析输入报文后),或者将转换前的object转换成json(生成输出报文前),并打印到日志文件中。

package com.example.tutorial.tool;

import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.protobuf.Message;
import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.util.JsonFormat.Printer;

import io.grpc.MethodDescriptor.Marshaller;

/**
 * 使用JSON格式记录输入与输出日志
 * 
 */
public class JsonLoggerMarshaller implements Marshaller {

  private static final Logger LOG = Logger.getLogger(JsonLoggerMarshaller.class.getName());

  private Marshaller baseMarshaller;

  private final Printer printer = JsonFormat.printer().omittingInsignificantWhitespace();

  public JsonLoggerMarshaller(Marshaller baseMarshaller) {
    this.baseMarshaller = baseMarshaller;
  }

  public InputStream stream(T value) {
    try {
      // 记录输出日志
      String info = printer.print(value);
      LOG.info("output:" + info);
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "do stream error", e);
    }

    return baseMarshaller.stream(value);
  }

  public T parse(InputStream stream) {
    T msg = baseMarshaller.parse(stream);

    try {
      // 记录输入日志
      String info = printer.print(msg);
      LOG.info("input:" + info);
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "do parse error", e);
    }

    return msg;
  }
}

        然后,使用如下代码将JsonLoggerMarshaller注册到服务中去。

package com.example.tutorial.tool;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.logging.Level;
import java.util.logging.Logger;

import io.grpc.MethodDescriptor;
import io.grpc.MethodDescriptor.Marshaller;
import io.grpc.MethodDescriptor.PrototypeMarshaller;

public class GlobalMarshallerInitializer {

  private static final Logger LOG = Logger.getLogger(GlobalMarshallerInitializer.class.getName());

  // 为记录json格式的日志,注册JsonLoggerMarshaller
  public static void initiateJsonLoggerMarshaller(Class serviceClass) {
    try {
      Field serviceNameField = serviceClass.getField("SERVICE_NAME");
      if (serviceNameField == null) {
        return;
      }

      for (Method m : serviceClass.getDeclaredMethods()) {

        if (Modifier.isStatic(m.getModifiers()) && m.getReturnType() == MethodDescriptor.class) {

          // 为各RPC调用方法生成默认的MethodDescriptor,默认MethodDescriptor中的Marshaller使用的是二进制格式传输报文
          MethodDescriptor md = (MethodDescriptor) m.invoke(null);

          Field requestMarshallerField = md.getClass().getDeclaredField("requestMarshaller");
          Field responseMarshallerField = md.getClass().getDeclaredField("responseMarshaller");

          // 使用反射机制设置输入与输出Marshaller
          requestMarshallerField.setAccessible(true);
          requestMarshallerField.set(md, new JsonLoggerMarshaller(md.getRequestMarshaller()));
          requestMarshallerField.setAccessible(false);

          responseMarshallerField.setAccessible(true);
          responseMarshallerField.set(md, new JsonLoggerMarshaller(md.getResponseMarshaller()));
          responseMarshallerField.setAccessible(false);

          LOG.info("finish to initiate method: " + md.getFullMethodName());
        }
      }
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "fail to initiate JsonLoggerMarshaller " + serviceClass.getName(), e);
    }
  }
}

        最后,只需要在服务端代码和客户端代码中,对需要日志功能的服务,调用上述代码进行初始化。

GlobalMarshallerInitializer.initiateJsonLoggerMarshaller(GreeterGrpc.class);

 

你可能感兴趣的:(Java)