欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot 入门(五):集成 AOP 进行日志管理

程序员文章站 2022-04-25 20:01:17
...

本篇文章是接着 Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理写的,按照前面几篇博客的教程,可以搭建一个简单的项目,主要包含了 Pagehelper+MyBatis 分页查询,Generator 代码自动生成器,Shiro登录及权限管理。本篇博客主要是集成 AOP 进行日志管理

1.导入 jar 包

<!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
复制代码

2.配置 Logback-spring 文件

关于 Logback-spring 的配置网上很多,随便copy一份基本上就能使用,Logback-spring.xml 中主要配置了下列内容

  • (1).日志写道控制台
  • (2).日志写道本地文件中
  • (3).日志级别
  • (4).日志生成方式(按照日期滚动生成,还是按照日期单独生成)
  • (5).日志来源的配置,一般直接配置到 Control

我也是直接在copy了一份,代码如下

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback</contextName>
<!--
    &lt;!&ndash; 文件输出格式 &ndash;&gt;
    <property name="PATTERN" value="%-12(%d{yyyy-MM-dd HH:mm:ss.SSS}) |-%-5level [%thread] %c [%L] -| %msg%n" />
    &lt;!&ndash; test文件路径 &ndash;&gt;
    <property name="TEST_FILE_PATH" value="c:/log" />

    -->

    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--按天生成日志-->
    <appender name="logFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>
                applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
            </FileNamePattern>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss} -%msg%n
            </Pattern>
        </layout>
    </appender>

    <logger name="com.tswc.edu" additivity="false">
        <appender-ref ref="console"/>
        <appender-ref ref="logFile" />
    </logger>

    <root level="error">
        <appender-ref ref="console"/>
        <appender-ref ref="logFile" />
    </root>

</configuration>

复制代码

这里用户也可以配置多个级别使用于多个环境,对每个日志级别进行配置不同的属性,然后在 Application.xml 中选择不同的级别环境。在实际项目开发的过程中,一般配置2个环境,开发环境,生产环境。在开发环境中,只需要配置日志输出到控制台,便于开发人员调试。生成环境相反,需要配置日志输出到文件,控制台尽量不要输出日志,这样可以减少控制台对虚拟机内存的消耗,一旦产生 Bug ,用户查询日志文件即可

上述代码中即配置了日志输出到控制台,也配置了日志输出到日志文件

3.配置日志级别

只需要在 Application.xml 中配置即可:

日志级别分为5个等级,debug<info<warn<Error<Fatal,其中常用的级别为:debug和info

  • debug 级别最低,可以随意的使用于任何觉得有利于在调试时更详细的了解系统运行状态的东东;
  • info 重要,输出信息:用来反馈系统的当前状态给最终用户的; 后三个,警告、错误、严重错误,这三者应该都在系统运行时检测到了一个不正常的状态。
  • warn, 可修复,系统可继续运行下去;
  • Error, 可修复性,但无法确定系统会正常的工作下去;
  • Fatal, 相当严重,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话后果严重。

4.编写日志类

@Aspect
@Component
public class WebLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

    @Pointcut("execution( * com.tswc.edu.controller.*.*(..))")//两个..代表所有子目录,最后括号里的两个..代表所有参数
    public void logPointCut() {
    }

    //切点
    @Pointcut("@annotation(com.tswc.edu.annotation.Log)")
    public void logPointCutLog() {
    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        logger.info("请求地址 : " + request.getRequestURL().toString());
        //logger.info("方法描述 : " + );
        logger.info("HTTP METHOD : " + request.getMethod());
        // 获取真实的ip地址
        //logger.info("IP : " + IPAddressUtil.getClientIpAddress(request));
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
//        loggger.info("参数 : " + joinPoint.getArgs());

    }

    @AfterReturning(returning = "ret", pointcut = "logPointCut()")// returning的值和doAfterReturning的参数名一致
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容(返回值太复杂时,打印的是物理存储空间的地址)
        logger.debug("返回值 : " + ret);
    }

    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object ob = pjp.proceed();// ob 为方法的返回值
        logger.info("耗时 : " + (System.currentTimeMillis() - startTime));
        return ob;
    }

    @Around("logPointCutLog()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 执行方法
        Object result = point.proceed();
        // 获得注解
        Log controllerLog = getAnnotationLog(point);
        if (controllerLog == null) {
            return null;
        }
        String action = controllerLog.value();
        logger.info("请求目的:"+action);
        return result;
    }
    /**
     * 是否存在注解,如果存在就获取
     */
    private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            //System.out.println("123:"+method.getAnnotation(Log.class).value());
            return method.getAnnotation(Log.class);
        }
        //System.out.println("1234:"+method.getAnnotation(Log.class).value());
        return null;
    }
}
复制代码

这是个通用类,主要约定控制台或者日志文件中日志的格式,关于此公共类,网上有大量的讲解,这里就不详细说明了。

再次启动项目,控制台将输出日志,并将日志写入到文件中:

5.新增加部分

其中自定义日志文件可以不要,这里用户自己定义了日志输出的说明部分

自定义了 @Log 注记的识别,并配置一些文件说明,那么在请求到这个类的时候,日志中将输出文章描述部分

自定义配置文件的代码:

  • Log
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
//自定义注解类   ArchivesLog.java(获取Controller描述用的)
public @interface Log {
    String value() default "";
}
复制代码
  • LogAspect
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
    //切点
    @Pointcut("@annotation(com.tswc.edu.annotation.Log)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //异步保存日志
        //saveLog(point, time);
        return result;
    }

}
复制代码

其中LogAspect中也可以写一些对日志进行 CRUD 的业务逻辑操作,大多数情况下,此处可以将日志的保存逻辑写入到此类中。

6.问题

本项目在启动的时候,报了一个关于日志的警告,没有找到解决方案

项目中并没有用到log4j,不知道为什么会警告,项目中缺少log4j的配置文件,如果有大神知道原因,欢迎留言