方法调用日志记录拦截器的一个实现

概述

工作中经常调用外部接口, 需要进行详细日志记录, 防止扯皮. 在调用失败以后, 要及时通知警告. 下面是拦截器的实现, 使用方式参考: https://my.oschina.net/u/1169457/blog/1489113

拦截器实现


/**
 * 拦截有注解的方法. 方法调用前, 会 info 级别打印所有入参; 调用以后, 会打印返回结果. <br/>
 * 如果产生异常, 会 error 级别打印异常信息, 同时发送报警邮件, 异常不会被拦截, 会正常抛出. <br/>
 * 报警邮件送达规则符合 {@link EmailWarnUtil} 定义规则.
 * 
 * @see
 *      https://my.oschina.net/u/1169457/blog/1489113
 *      http://todayleave.blogspot.jp/2016/02/spring-methodinterceptor.html
 *      http://blog.javaforge.net/post/76125490725/spring-aop-method-interceptor-annotation
 *
 */
@Slf4j
@Component
public class LogAndWarnInterceptor implements MethodInterceptor {

  @Autowired
  private JsonUtil jsonUtil;

  @Autowired
  private EmailWarnUtil emailWarnUtil;

  private ConcurrentHashMap<Method, List<String>> methodParameterNamesCache = new ConcurrentHashMap<>();

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    Object[] args = invocation.getArguments();
    List<String> paramNames = getMethodParamNames(method);
    String className = invocation.getThis().getClass().getSimpleName();
    String classMethodName = className + "." + method.getName();
    long timestamp = System.currentTimeMillis();


    log.info("before invoke method:{}, parameter names:{}, args:{}, timestamp:{}", classMethodName, paramNames,
        jsonUtil.toJsonString(args), timestamp);
    try {
      Object retVal = invocation.proceed();
      log.info("after invoke method:{}, result:{}, invoke timestamp:{}, call duration:{}", classMethodName,
          jsonUtil.toJsonString(retVal), timestamp, System.currentTimeMillis() - timestamp);
      return retVal;
    } catch (Exception exception) {
      String errMessage = "exception!!! invoke method:" + classMethodName + ",  parameter names:" + paramNames
          + ", args:" + jsonUtil.toJsonString(args) + ", invoke timestamp:" + timestamp;
      log.error(errMessage, exception);
      emailWarnUtil.muteWarn(NotifyEmailGroup.ERROR_WARN, errMessage, exception);
      throw exception;
      // throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR, "服务器调用后端服务错误, 请稍后重试.");
    }
  }

  /**
   * @see https://stackoverflow.com/questions/2237803/can-i-obtain-method-parameter-name-using-java-reflection
   *      <b>Bozho's answer</b>
   * 
   * @param method
   * @return
   */
  private List<String> getMethodParamNames(Method method) {
    List<String> paramNames = methodParameterNamesCache.get(method);
    if (paramNames != null) {
      return paramNames;
    }
    Parameter[] parameters = method.getParameters();
    paramNames = new ArrayList<>();
    for (Parameter parameter : parameters) {
      paramNames.add(parameter.getName());
    }
    methodParameterNamesCache.put(method, paramNames);
    return paramNames;
  }

}

pom 配置

在用反射获取方法签名(方法入参名字)时, 需要在pom文件添加 -parameters 参数, 而且jdk8以上版本. 要不然获取的参数名字将会是: arg0, rags1 这样. 更改配置以后, 需要 mvn clean package 一下生效.

<properties>
    <!-- PLUGIN VERSIONS -->
    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>

    <!-- OTHER PROPERTIES -->
    <java.version>1.8</java.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <compilerArgument>-parameters</compilerArgument>
                <testCompilerArgument>-parameters</testCompilerArgument>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

结果示例

2017-07-26 18:33:38 [XNIO-1 task-1] INFO  c.y.o.c.global.LogAndWarnInterceptor - before invoke method:SmsService.send, parameter names:[mobile, templeId, params], args:["12222222222","string",{}], timestamp:1501065218633
2017-07-26 18:33:38 [XNIO-1 task-1] INFO  c.y.o.c.global.LogAndWarnInterceptor - after invoke method:SmsService.send, result:, invoke timestamp:1501065218633, call duration:43

参考

https://my.oschina.net/u/1169457/blog/1489113
https://stackoverflow.com/questions/2237803/can-i-obtain-method-parameter-name-using-java-reflection

猜你喜欢

转载自my.oschina.net/u/1169457/blog/1489767