AOP-配合slf4j打印日志
基本思想
- 凡在目标实例上或在目标实例方法(非静态方法)上标注自定义注解
@AutoLog,其方法执行时将触发AOP操作; @AutoLog只有一个参数,用来控制是否打印该方法的参数和返回结果的json字符串,默认不打印,通过@AutoLog(true)开启- 通过AOP拦截方法并打印日志
代码
package com.yan.mssm.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoLog {
// log params and result when true
boolean value() default false;
}
package com.yan.mssm.aop;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.el.MethodNotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Aspect
public class AutoLogAspectJ {
private static final String PREFIX = "[";
private static final String SUFFIX = "]";
private static final String END = "end";
private static final String BEGIN = "begin";
private static final String PARAMS = "params";
private static final String RESULT = "result";
private static final String DELIMITER = " || ";
private static final int STRING_MAX_LENGTH = 1 << 10;
private static final Class<AutoLog> AUTO_LOG_CLASS = AutoLog.class;
private static final String LOG_FORMAT = "[AOP-LOG]->Method {}: {}";
private static final Class<RequestMapping> REQUEST_MAPPING_CLASS = RequestMapping.class;
private static Logger LOGGER;
private Method method;
private String url;
private Class<?> targetClass;
private String methodSignature;
private boolean debug;
private static Object getCglibProxyTargetObject(Object proxy) {
Field h;
try {
h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
return ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
} catch (Exception e) {
return null;
}
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) {
try {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
return ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
} catch (Exception e) {
return null;
}
}
private Object getTarget(Object proxy) {
if (!AopUtils.isAopProxy(proxy)) {
return proxy;
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
}
return getCglibProxyTargetObject(proxy);
}
@Around(value = "@within(com.yan.mssm.aop.AutoLog) || @annotation(com.yan.mssm.aop.AutoLog)")
public Object test(ProceedingJoinPoint point) throws Throwable {
init(point);
Optional.ofNullable(url).ifPresent(u -> LOGGER.info("[AOP-LOG]->Input URL:{}", u));
LOGGER.info(LOG_FORMAT, BEGIN, methodSignature);
printParams(point);
Object result = point.proceed();
printResult(result);
LOGGER.info(LOG_FORMAT, END, methodSignature);
return result;
}
private void init(ProceedingJoinPoint point) {
Signature signature = point.getSignature();
methodSignature = signature.toString();
targetClass = point.getTarget().getClass();
LOGGER = LoggerFactory.getLogger(targetClass);
method = Arrays.stream(targetClass.getDeclaredMethods())
.filter(method -> method.toString().equals(signature.toLongString()))
.findFirst().orElseThrow(MethodNotFoundException::new);
setUrl();
debug = isDebug();
}
private void printResult(Object result) {
if (!debug) {
return;
}
Optional.ofNullable(result).ifPresent(r -> LOGGER.info(LOG_FORMAT, RESULT, r));
}
private void printParams(ProceedingJoinPoint point) {
if (!debug) {
return;
}
Object[] args = point.getArgs();
if (args.length == 0) {
return;
}
MethodSignature methodSignature = (MethodSignature) point.getSignature();
String[] paramNames = methodSignature.getParameterNames();
Map<String, Object> argMap = new LinkedHashMap<>();
for (int i = 0; i < args.length; i++) {
String argName = paramNames[i];
argMap.put(argName, getTarget(args[i]));
}
String paramString = toJsonString(argMap);
Optional.ofNullable(paramString)
.ifPresent(str -> LOGGER.info(LOG_FORMAT, PARAMS, str));
}
private boolean isDebug() {
return (targetClass.isAnnotationPresent(AUTO_LOG_CLASS) && targetClass.getAnnotation(AUTO_LOG_CLASS).value())
|| (method.isAnnotationPresent(AutoLog.class) && method.getAnnotation(AUTO_LOG_CLASS).value());
}
private void setUrl() {
boolean controller = targetClass.isAnnotationPresent(RestController.class)
|| targetClass.isAnnotationPresent(Controller.class);
if (!controller) {
url = null;
return;
}
List<String> urlList = getUrlList();
url = getCollect(urlList);
}
private List<String> getUrlList() {
List<String> urlList = new ArrayList<>();
String[] urls;
boolean hasControllerMapping = hasControllerMapping();
boolean hasMethodMapping = hasMethodMapping();
if (!hasControllerMapping && !hasMethodMapping) {
return urlList;
}
if (!hasControllerMapping) {
urls = method.getAnnotation(REQUEST_MAPPING_CLASS).value();
urlList.addAll(Arrays.asList(urls));
return urlList;
}
if (!hasMethodMapping) {
urls = targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value();
urlList.addAll(Arrays.asList(urls));
return urlList;
}
Arrays.stream(targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value())
.forEach(ctlrUrl ->
Arrays.stream(method.getAnnotation(REQUEST_MAPPING_CLASS).value())
.forEach(methodUrl -> {
StringBuilder sbd = new StringBuilder();
urlList.add(sbd.append(ctlrUrl).append(methodUrl).toString());
}));
return urlList;
}
private boolean hasMethodMapping() {
return method.isAnnotationPresent(REQUEST_MAPPING_CLASS)
&& method.getAnnotation(REQUEST_MAPPING_CLASS).value().length > 0;
}
private boolean hasControllerMapping() {
return targetClass.isAnnotationPresent(REQUEST_MAPPING_CLASS)
&& targetClass.getAnnotation(REQUEST_MAPPING_CLASS).value().length > 0;
}
private String getCollect(List<String> urlList) {
return urlList.stream().collect(Collectors.joining(DELIMITER, PREFIX, SUFFIX));
}
private String toJsonString(Object object) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String result = null;
try {
byte[] bytes = objectMapper.writeValueAsBytes(object);
result = new String(bytes, "utf-8");
result = result.length() > STRING_MAX_LENGTH ? null : result;
} catch (Exception e) {
LOGGER.error("AutoLogAspectJ.toJsonString exception:\n{}", e.getMessage());
}
return result;
}
}
AOP-配合slf4j打印日志的更多相关文章
- SSM框架下结合 log4j、slf4j打印日志
首先加入log4j和slf4j的jar包 <!-- 日志处理 <!-- slf4j日志包--> <dependency> <groupId>org.slf4j ...
- (WebFlux)002、如何打印日志与链路ID
一.背景 最近在持续改造项目,想通过日志查看用户所有的接口链路日志.在原来基于SpirngMVC的时候,那是比较好处理的,通过ThreadLocal,放入TraceId,就可以把一个TraceId传到 ...
- SpringBoot整合Slf4j+logback日志框架
一.Slf4j简单介绍与优势 1.介绍 Slf4j的全称是Simple Loging Facade For Java(Java简单日志门面),它仅仅是一个为Java程序提供日志输出的统一接口,并不是一 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- Spring Boot 2.0 教程 | AOP 切面统一打印请求日志
欢迎关注微信公众号: 小哈学Java 文章首发于个人网站 https://www.exception.site/springboot/spring-boot-aop-web-request 本节中,您 ...
- AOP与Filter拦截请求打印日志实用例子
相信各位同道在写代码的时候,肯定会写一些日志打印,因为这对往后的运维而言,至关重要的. 那么我们请求一个restfull接口的时候,哪些信息是应该被日志记录的呢? 以下做了一个基本的简单例子,这里只是 ...
- 关于spring 事务 和 AOP 管理事务和打印日志问题
关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . 可以在server层总使用@Transactional,进行方法内的事务管 ...
- Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志
其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...
- springboot aop + logback + 统一异常处理 打印日志
1.src/resources路径下新建logback.xml 控制台彩色日志打印 info日志和异常日志分不同文件存储 每天自动生成日志 结合myibatis方便日志打印(debug模式) < ...
随机推荐
- 【Ubuntu】ubuntu系统下python3和python2环境自由切换
shell里执行: sudo update-alternatives --install /usr/bin/python python /usr/local/lib/python2.7 100sudo ...
- JIRA REST java client API实际应用
[本文出自天外归云的博客园] 前提 1.需要安装maven环境: 2.在本地创建maven项目并修改maven配置文件“pom.xml”,添加如下内容: <dependency> < ...
- iOS友盟社会化分享U-Share分享面板不显示的问题(基本配置没有错误)
//要先是window可视化 [self.window makeKeyAndVisible]; //添加友盟分享[[UMSocialManager defaultManager] openLog:YE ...
- [phy]在uboot阶段失能phy芯片
在uboot阶段power down掉phy芯片 uenvcmd=mdio 0x800; mmc rescan mdio为uboot支持命令 7:phy芯片地址 0:phy芯片工作模式寄存器地址 0x ...
- ubuntu linux下建立stm32开发环境: GCC安装以及工程Makefile建立
http://blog.csdn.net/embbnux/article/details/17616809
- PPP或PPPOE身份验证PAP和CHAP
PPP或PPPOE都支持身份验证,有两种验证方式:PAP和CHAP. PAP,Passwd Authentication Protocol,密码验证协议,以客户端明文方式传递用户名和密码,服务器和本省 ...
- ELK(Logstash+Elasticsearch+Kibana)的原理和详细搭建
一. Elastic Stack Elastic Stack是ELK的官方称呼,网址:https://www.elastic.co/cn/products ,其作用是“构建在开源基础之上, Elast ...
- Hbase 学习(九) 华为二级索引(原理)
这个是华为的二级索引方案,已经开放源代码了,下面是网上的一篇讲解原理的帖子,发出来和大家共享一下. 经过本人认真阅读了一下代码,发现这个源码仅供参考,想要集成到原有的集群当中是有点儿难度的,它对hba ...
- TF-IDF词项权重计算
一.TF-IDF 词项频率: df:term frequency. term在文档中出现的频率.tf越大,词项越重要. 文档频率: tf:document frequecy.有多少文档包括此term, ...
- 从商业角度探讨API设计
为Web设计.实现和维护API不仅仅是一项挑战:对很多公司来说,这是一项势在必行的任务.本系列将带领读者走过一段旅程,从为API确定业务用例到设计方法论,解决实现难题,并从长远的角度看待在Web上维护 ...