基本思想

  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. LeetCode: 【L4】N-Queens 解题报告

    [L4]N-Queens 解题报告 N-Queens Total Accepted: 16418 Total Submissions: 63309 My Submissions The n-queen ...

  2. Python爬网——获取安卓手机统计数据

    [本文出自天外归云的博客园] 1. 在安卓网上对热门机型进行爬网,取前五十: # -*- coding: utf-8 -*- import requests,re from bs4 import Be ...

  3. SQL Server 数据库自建表

    sysobjects是系统自建的表,里面存储了在数据库内创建的每个对象(约束.默认值.日志.规则.存储过程等). 列名 数据类型 描述 name sysname 对象名 id int 对象标识号 xt ...

  4. strcpy和memcpy的区别(转)

    转自:http://www.cnblogs.com/stoneJin/archive/2011/09/16/2179248.html strcpy和memcpy都是标准C库函数,它们有下面的特点.st ...

  5. python-urllib&urllib2模块

    GET #!/usr/bin/env python # encoding: utf-8 import urllib import urllib2 url = "http://127.0.0. ...

  6. ssm框架结合axis2实例步骤

    本文亲测: 1.从官网下载axis2相关api,地址是:http://axis.apache.org/axis2/java/core/download.html,我下载的是axis2-1.7.6-bi ...

  7. layer关闭弹出层,弹出打印

    常规的话,下面能够完成关闭弹出层 var index = parent.layer.getFrameIndex(window.name); //延迟关闭 解决打印窗口弹不出来的情况 parent.la ...

  8. 【转】如何使用visual studio将你的程序打包成安装包

    原文地址:https://www.cnblogs.com/SolarWings/p/6132310.html 很久很久以前,我一直有一个梦想,那就是做出一个自己的游戏,这个游戏很像模像样,除了拥有一个 ...

  9. Cacti的库表结构-Data

    cacti 的数据都是存放在rrdtool 中的,数据库存放的其实只是配置数据,cacti 的逻辑对象主要分为三种,data (数据).graph (图片).host (设备),这在它的表设计中也能很 ...

  10. SpringMVC REST 风格静态资源访问配置

    1 在web.xml中使用默认servlet处理静态资源,缺点是如果静态资源过多,则配置量会比较大,一旦有遗漏,则会造成资源无法正常显示或404错误. <!-- 静态资源访问控制 --> ...