加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Spring AOP实现统一日志输出

发布时间:2020-12-15 01:16:57 所属栏目:大数据 来源:网络整理
导读:目的: 统一日志输出格式 思路: 1、 针对不同的调用场景定义不同的注解,目前想的是接口层和服务层。 2、我设想的接口层和服务层的区别在于: (1)接口层可以打印客户端IP,而服务层不需要 (2)接口层的异常需要统一处理并返回,而服务层的异常只需要向上

目的:

统一日志输出格式

思路:

1、针对不同的调用场景定义不同的注解,目前想的是接口层和服务层。

2、我设想的接口层和服务层的区别在于:

  (1)接口层可以打印客户端IP,而服务层不需要

  (2)接口层的异常需要统一处理并返回,而服务层的异常只需要向上抛出即可

3、就像Spring中的@Controller、@Service、@Repository注解那样,虽然作用是一样的,但是不同的注解用在不同的地方显得很清晰,层次感一下就出来了

4、AOP去拦截特定注解的方法调用

5、为了简化使用者的操作,采用Spring Boot自动配置

1. 注解定义

package com.cjs.example.annotation;

import java.lang.annotation.ElementType;
 java.lang.annotation.Retention;
 java.lang.annotation.RetentionPolicy;
 java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SystemControllerLog {

    String description() default "";

    boolean async() default false;

}
 SystemServiceLog {

    String description() ;

}

2. 定义一个类包含所有需要输出的字段

 com.cjs.example.service;

 lombok.Data;
 java.io.Serializable;

@Data
public class SystemLogStrategy implements Serializable {

    private boolean async;

    private String threadId;

     String location;

     String description;

     String className;

     String methodName;

     String arguments;

     String result;

     Long elapsedTime;


    public String format() {
        return "线程ID: {},注解位置: {},方法描述: {},目标类名: {},目标方法: {},调用参数: {},返回结果: {},花费时间: {}";
    }

     Object[] args() {
        return new Object[]{this.threadId,this.location,1)">this.description,1)">this.className,1)">this.methodName,1)">this.arguments,1)">this.result,1)">this.elapsedTime};
    }

}

3. 定义切面

 com.cjs.example.aspect;

 com.alibaba.fastjson.JSON;
 com.cjs.example.annotation.SystemControllerLog;
 com.cjs.example.annotation.SystemRpcLog;
 com.cjs.example.annotation.SystemServiceLog;
 com.cjs.example.enums.AnnotationTypeEnum;
 com.cjs.example.service.SystemLogStrategy;
 com.cjs.example.util.JsonUtil;
 com.cjs.example.util.ThreadUtil;
 org.aspectj.lang.ProceedingJoinPoint;
 org.aspectj.lang.Signature;
 org.aspectj.lang.annotation.Around;
 org.aspectj.lang.annotation.Aspect;
 org.aspectj.lang.annotation.Pointcut;
 org.slf4j.Logger;
 org.slf4j.LoggerFactory;

 java.lang.reflect.Method;

@Aspect
class SystemLogAspect {

    static final Logger LOG = LoggerFactory.getLogger(SystemLogAspect.);

    );

    @Pointcut("execution(* com.ourhours..*(..)) && !execution(* com.ourhours.logging..*(..))")
    void pointcut() {

    }

    @Around("pointcut()" Object doInvoke(ProceedingJoinPoint pjp) {
        long start = System.currentTimeMillis();

        Object result = null;

        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            LOG.error(throwable.getMessage(),throwable);
            throw new RuntimeException(throwable);
        } finally {
            long end = System.currentTimeMillis();
            long elapsedTime = end - start;

            printLog(pjp,result,elapsedTime);

        }

        return result;
    }

    /**
     * 打印日志
     * @param pjp   连接点
     *  result    方法调用返回结果
     *  elapsedTime   方法调用花费时间
     */
    void printLog(ProceedingJoinPoint pjp,Object result,1)">long elapsedTime) {
        SystemLogStrategy strategy = getFocus(pjp);

        if (null != strategy) {
            strategy.setThreadId(ThreadUtil.getThreadId());
            strategy.setResult(JsonUtil.toJSONString(result));
            strategy.setElapsedTime(elapsedTime);
            if (strategy.isAsync()) {
                new Thread(()->LOG.info(strategy.format(),strategy.args())).start();
            }else {
                LOG.info(strategy.format(),strategy.args());
            }
        }
    }

    
     * 获取注解
      SystemLogStrategy getFocus(ProceedingJoinPoint pjp) {
        Signature signature = pjp.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        Object[] args = pjp.getArgs();
        String targetClassName = pjp.getTarget().getClass().getName();
         {
            Class<?> clazz = Class.forName(targetClassName);
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                 (methodName.equals(method.getName())) {
                    if (args.length == method.getParameterCount()) {

                        SystemLogStrategy strategy =  SystemLogStrategy();
                        strategy.setClassName(className);
                        strategy.setMethodName(methodName);

                        SystemControllerLog systemControllerLog = method.getAnnotation(SystemControllerLog.);
                         systemControllerLog) {
                            strategy.setArguments(JsonUtil.toJSONString(args));
                            strategy.setDescription(systemControllerLog.description());
                            strategy.setAsync(systemControllerLog.async());
                            strategy.setLocation(AnnotationTypeEnum.CONTROLLER.getName());
                             strategy;
                        }
                        SystemServiceLog systemServiceLog = method.getAnnotation(SystemServiceLog. systemServiceLog) {
                            strategy.setArguments(JsonUtil.toJSONString(args));
                            strategy.setDescription(systemServiceLog.description());
                            strategy.setAsync(systemServiceLog.async());
                            strategy.setLocation(AnnotationTypeEnum.SERVICE.getName());
                             strategy;
                        }
                        
                        ;
                    }
                }
            }
        }  (ClassNotFoundException e) {
            LOG.error(e.getMessage(),e);
        }
        ;
    }

}

4. 配置

PS:

这里也可以用组件扫描,执行在Aspect上加@Component注解即可,但是这样的话有个问题。

就是,如果你的这个Aspect所在包不是Spring Boot启动类所在的包或者子包下就需要指定@ComponentScan,因为Spring Boot默认只扫描和启动类同一级或者下一级包。

?

 com.cjs.example.config;

 com.cjs.example.aspect.SystemLogAspect;
 org.springframework.boot.autoconfigure.AutoConfigureOrder;
 org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 org.springframework.context.annotation.Bean;
 org.springframework.context.annotation.Configuration;
 org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@AutoConfigureOrder(2147483647)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnClass(SystemLogAspect.)
@ConditionalOnMissingBean(SystemLogAspect.)
 SystemLogAutoConfiguration {

    @Bean
     SystemLogAspect systemLogAspect() {
         SystemLogAspect();
    }
}

?

5. 自动配置(resources/META-INF/spring.factories)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ourhours.logging.config.SystemLogAutoConfiguration

6. 其它工具类

?

6.1. 获取客户端IP

 com.cjs.example.util;

 org.springframework.web.context.request.RequestContextHolder;
 org.springframework.web.context.request.ServletRequestAttributes;

 javax.servlet.http.HttpServletRequest;

 HttpContextUtils {

    static HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
         servletRequestAttributes.getRequest();
    }

     String getIpAddress() {
        HttpServletRequest request = getHttpServletRequest();
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            .equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            .equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        }else if (ip != null && ip.length() > 15) {
            String[] ips = ip.split(",");
            for (int index = 0; index < ips.length; index++) {
                String strIp = (String) ips[index];
                if (!("unknown".equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
         ip;
    }
}

6.2. 格式化成JSON字符串

 com.alibaba.fastjson.serializer.SerializerFeature;

 JsonUtil {

     String toJSONString(Object object) {
         JSON.toJSONString(object,SerializerFeature.DisableCircularReferenceDetect);
    }

}

6.3. 存取线程ID

 java.util.UUID;

 ThreadUtil {

    final ThreadLocal<String> threadLocal = new ThreadLocal<>();

     String getThreadId() {
        String threadId = threadLocal.get();
        null == threadId) {
            threadId = UUID.randomUUID().toString();
            threadLocal.set(threadId);
        }
         threadId;
    }

}

7. 同时还提供静态方法

 com.cjs.example;

 Log {

    static Logger LOGGER =  SingletonHolder{
        static Log instance =  Log();
    }

     Log(){}

    static Log getInstance(Class<?> clazz){
        LOGGER = LoggerFactory.getLogger(clazz);
         SingletonHolder.instance;
    }

     info(String description,Object args,Object result) {
        LOGGER.info("线程ID: {},返回结果: {}",ThreadUtil.getThreadId(),description,JsonUtil.toJSONString(args),JsonUtil.toJSONString(result));
    }

     error(String description,Throwable t) {
        LOGGER.error("线程ID: {},JsonUtil.toJSONString(result),t);
    }

}

8. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    modelVersion>4.0.0</>

    groupId>com.cjs.exampleartifactId>cjs-loggingversion>0.0.1-SNAPSHOTpackaging>jarnameparent>
        >org.springframework.boot>spring-boot-starter-parent>2.0.2.RELEASErelativePath/> <!-- lookup parent from repository -->
    propertiesproject.build.sourceEncoding>UTF-8project.reporting.outputEncodingjava.version>1.8aspectj.version>1.8.13servlet.versionslf4j.version>1.7.25fastjson.version>1.2.47dependenciesdependency>
            >spring-boot-autoconfigureoptional>true>org.springframework>spring-web>javax.servlet>javax.servlet-api>${servlet.version}scope>provided>org.aspectj>aspectjweaver>${aspectj.version}>org.slf4j>slf4j-api>${slf4j.version}>com.alibaba>fastjson>${fastjson.version}buildpluginsplugin>
                >org.apache.maven.plugins>maven-compiler-plugin>3.7.0configuration>
                    sourcetargetencoding>UTF8>maven-source-pluginexecutionsexecution>
                        id>attach-sourcesgoals>
                            goal>

project>

8. 工程结构

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读