概述
工作中经常调用外部接口, 需要进行详细日志记录, 防止扯皮. 在调用失败以后, 要及时通知警告. 下面是拦截器的实现, 使用方式参考: 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