基本思想

  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. 【word2vec】Distributed Representation——词向量

    Distributed Representation 这种表示,它最早是 Hinton 于 1986 年提出的,可以克服 one-hot representation 的缺点. 其基本想法是: 通过训 ...

  2. JAVA-JSP内置对象之request获得所有的参数名称

    相关资料:<21天学通Java Web开发> 获得所有的参数名称1.request对象的getParameterNames()方法可以获得所有的参数名称. RequestForm2.jsp ...

  3. browser_autopwn2

    browser_autopwn2是个啥玩意儿? 一个集合了浏览器漏洞的小框架 option msf > use auxiliary/server/browser_autopwn2 msf aux ...

  4. Linux给tomcat指定jdk

    在安装jenkins的时候,发现必须是jdk1.8,所以就只能单独安装一个tomcat,在给tomcat配置jdk1.8了,以免破坏以前的项目 安装就不多说了.这里需要修改两个配置文件: 安装的tom ...

  5. ajax无刷新方式对form表单进行赋值!

    /** * 把json数据填充到from表单中 */ <form id="editForm" action="user.php"> 用户名:< ...

  6. mac上使用zsh配置环境变量

    Mac配置环境变量的地方 一./etc/profile (建议不修改这个文件 ) 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. 二./etc/bashrc (一般在这个文件中添加系统级环境 ...

  7. 【Unity】使用Resources类管理资源

    最近参考了各位大神的资源,初步学习了Unity的资源管理模式,包括在编辑器管理(使用AssetDatabase)和在运行时管理(使用Resources和AssetBundle).在此简单总结运行时用R ...

  8. Java多线程编程中Future模式的详解<转>

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  9. c#获取网页代码、数据、资源

    //WebClient取网页源码 private string GetHtmlSource(string Url) { try { System.Net.WebClient wc = new Syst ...

  10. JS监听页面----无鼠标键盘动作,自动跳页

    function ScreenSaver(settings){ this.settings = settings; this.nTimeout = this.settings.timeout; doc ...