基本思想

  1. 凡在目标实例上或在目标实例方法(非静态方法)上标注自定义注解@AutoLog,其方法执行时将触发AOP操作;
  2. @AutoLog只有一个参数,用来控制是否打印该方法的参数和返回结果的json字符串,默认不打印,通过@AutoLog(true)开启
  3. 通过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打印日志的更多相关文章

  1. SSM框架下结合 log4j、slf4j打印日志

    首先加入log4j和slf4j的jar包 <!-- 日志处理 <!-- slf4j日志包--> <dependency> <groupId>org.slf4j ...

  2. (WebFlux)002、如何打印日志与链路ID

    一.背景 最近在持续改造项目,想通过日志查看用户所有的接口链路日志.在原来基于SpirngMVC的时候,那是比较好处理的,通过ThreadLocal,放入TraceId,就可以把一个TraceId传到 ...

  3. SpringBoot整合Slf4j+logback日志框架

    一.Slf4j简单介绍与优势 1.介绍 Slf4j的全称是Simple Loging Facade For Java(Java简单日志门面),它仅仅是一个为Java程序提供日志输出的统一接口,并不是一 ...

  4. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  5. Spring Boot 2.0 教程 | AOP 切面统一打印请求日志

    欢迎关注微信公众号: 小哈学Java 文章首发于个人网站 https://www.exception.site/springboot/spring-boot-aop-web-request 本节中,您 ...

  6. AOP与Filter拦截请求打印日志实用例子

    相信各位同道在写代码的时候,肯定会写一些日志打印,因为这对往后的运维而言,至关重要的. 那么我们请求一个restfull接口的时候,哪些信息是应该被日志记录的呢? 以下做了一个基本的简单例子,这里只是 ...

  7. 关于spring 事务 和 AOP 管理事务和打印日志问题

    关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . ​ 可以在server层总使用@Transactional,进行方法内的事务管 ...

  8. Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...

  9. springboot aop + logback + 统一异常处理 打印日志

    1.src/resources路径下新建logback.xml 控制台彩色日志打印 info日志和异常日志分不同文件存储 每天自动生成日志 结合myibatis方便日志打印(debug模式) < ...

随机推荐

  1. 综合出现NSScanner: nil string argument libc++abi.dylib: terminat错误的解决方案

    在开发中出现了这个错误,断点查找很久,没找到问题所在的代码,google下,发现了下面这几点会产生这个错误: 首先,顾名思义,错误原因是我们在调用某个方法的时候,传入了一个空字符串(注意区别于字符串内 ...

  2. new、delete、以及queue类

    本来以为很容易的,结果还是写了我两个小时. 用指针模拟queue类,再加上类,各种错误,总算是解决掉了-- #include<iostream> #include<cstdlib&g ...

  3. 01-老马jQuery教程-jQuery入口函数及选择器

    前言 这套jQuery教程是老马专门为寒门子弟而录制,希望大家看到后能转发给更多的寒门子弟.视频都是免费,请参考课程地址:https://chuanke.baidu.com/s5508922.html ...

  4. 2. KNN和KdTree算法实现

    1. K近邻算法(KNN) 2. KNN和KdTree算法实现 1. 前言 KNN一直是一个机器学习入门需要接触的第一个算法,它有着简单,易懂,可操作性强的一些特点.今天我久带领大家先看看sklear ...

  5. jsp传给java属性,java生成json串,方便以后取出来

    前台代码 $.ajax({ url : '<%=basePath%>userorderother/canUpdateCust.do', type : 'POST', data: {'kdc ...

  6. NLP实现文本分词+在线词云实现工具

    实现文本分词+在线词云实现工具 词云是NLP中比较简单而且效果较好的一种表达方式,说到可视化,R语言当仍不让,可见R语言︱文本挖掘——词云wordcloud2包 当然用代码写词云还是比较费劲的,网上也 ...

  7. [转]oracle在删除表\表空间\用户时,如何释放磁盘空间

    一.drop表 执行drop table xx 语句 drop后的表被放在回收站(user_recyclebin)里,而不是直接删除掉.这样,回收站里的表信息就可以被恢复,或彻底清除. 通过查询回收站 ...

  8. Thrift——栗子

    这张经典的图:黄色部分是用户实现的业务逻辑,褐色部分是根据Thrift定义的服务接口描述文件(IDL,接口定义语言)生成的客户端和服务端代码框架,红色部分是根据Thrift文件生成代码实现数据的读写操 ...

  9. [Java程序员面试宝典]读书笔记

    ClassLoader具备层次关系,且不止一种.不同的类装载器分布创建的同一个类的字节码数据属于完全不同的对象,没有任何关联 通过Class.forName(String className),能够动 ...

  10. [dig]使用dig查看当前网络连通情况

    1. dig domain, 通过server可以查到该域名被哪个server给解析了 2. dig @dns domain 不走/etc/resolve.conf,直接走指定的dns ------- ...