日志是一个Web项目中必不可少的部分,借助它我们可以做许多事情,比如问题排查、访问统计、监控告警等。一般通过引入slf4j的一些实现框架来做日志功能,如log4j,logback,log4j2,其性能也是依次增强。在springboot中,默认使用的框架是logback。我们经常需要在方法开头或结尾加日志记录传入参数或返回结果,以此来复现当时的请求情况。但是手动添加日志,不仅繁琐重复,也影响代码的美观简洁。本文引入一个基于AOP实现的日志框架,并通过spring-boot-starter的方式完成集成。

原文地址:http://blog.jboost.cn/2019/06/27/springboot-aoplog.html

1. aop-logging项目

项目地址: https://github.com/ronwxy/aop-logging
该项目基于 https://github.com/nickvl/aop-logging.git , 在其基础上添加了ReqId来串联某次客户端请求(参考com.github.nickvl.xspring.core.log.aop.ReqIdFilter), 添加了方法执行时长(参考com.github.nickvl.xspring.core.log.aop.AOPLogger.logTheMethod方法中elapsedTime)。

该项目提供了基于注解的AOP日志功能。根据不同的日志级别,提供的注解有LogTrace,LogDebug,LogInfo,LogWarn,LogError,LogFatal,LogException,可修饰于类(等同于该类内所有方法上添加)与方法上,前面六个分别表示在不同日志级别下记录方法被调用的日志,LogException表示在方法抛出异常时,记录相应日志。这些注解都提供了一个LogPoint枚举类型的属性value,取值{IN,OUT,BOTH},分别表示在方法调用入口、方法调用返回前,以及包含两者的位置打印对应日志,默认为BOTH。

2. 集成

可以通过基于xml或基于java配置的方式来集成AOP日志功能,我这里基于java配置(基于xml的方式参考源码README文件)并且通过spring-boot-starter的形式进行封装(源码地址: https://github.com/ronwxy/base-spring-boot ),避免每个项目都需要配置。自动配置类如下

@Configuration
@ConditionalOnClass(AOPLogger.class)
@ConditionalOnMissingBean(AOPLogger.class)
public class AopLoggerAutoConfiguration { private static final boolean SKIP_NULL_FIELDS = true;
private static final Set<String> EXCLUDE_SECURE_FIELD_NAMES = Collections.emptySet(); @Bean
public AOPLogger aopLogger() {
AOPLogger aopLogger = new AOPLogger();
aopLogger.setLogAdapter(new UniversalLogAdapter(SKIP_NULL_FIELDS, EXCLUDE_SECURE_FIELD_NAMES));
return aopLogger;
} /**
* 注册一个过滤器,用来生成一个reqId,标记一次请求,从而将本次请求所产生的日志串联起来
* @param
* @return
*/
@Bean
public FilterRegistrationBean reqIdFilter() {
ReqIdFilter reqIdFilter = new ReqIdFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(reqIdFilter);
List<String> urlPatterns = Collections.singletonList("/*");
registrationBean.setUrlPatterns(urlPatterns);
registrationBean.setOrder(100);
return registrationBean;
}
}

将基础框架base-spring-boot通过mvn clean install进行本地安装后,即可在项目中通过依赖进行引入(基础框架中已在spring-boot-parent中引入,直接继承亦可),如

<dependency>
<groupId>cn.jboost.springboot</groupId>
<artifactId>aoplog-spring-boot-starter</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>

3. 使用

引入依赖之后,我们再定义一个日志配置文件logback-spring.xml,为了后面方便地将日志导入ELK做集中的日志分析管理,该配置文件中将日志以json格式输出,并根据日志级别分别写入debug.log,info.log,warn.log,error.log以及interface.log(专用于接口访问日志),配置示例如下(完整配置参考: https://github.com/ronwxy/springboot-demos/blob/master/springboot-aoplog/src/main/resources/logback-spring.xml)

<appender name="interfaceLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/elk/interface.log</file>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"project": "${projectName}",
"timestamp": "%date{\"yyyy-MM-dd'T'HH:mm:ss,SSSZ\"}",
"log_level": "%level",
"thread": "%thread",
"class_name": "%X{callingClass}",
"class_method":"%X{callingMethod}",
"line_number": null,
"message": "%message",
"stack_trace": "%exception{5}",
"req_id": "%X{reqId}",
"elapsed_time": "#asLong{%X{elapsedTime}}"
}
</pattern>
</pattern>
</providers>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/bak/interface.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
</appender>

为了将该日志配置文件可以不经修改地达到复用,将一些参数配置外置了,故需在配置文件applicaiton.yml中配置如下参数

logger:
path: D:\logs #默认当前项目路径下的logs目录
level: info # 默认info
apiPackage: cn.jboost.springboot.aoplog.controller #必须配置, api接口类所在包
rootPackage: cn.jboost.springboot #必须配置,项目根包,记录该包内各类通过slf4j输出的日志

最后,直接在需要记录访问日志的接口类上加注解@LogInfo就行了,如

@RestController
@RequestMapping("test")
@LogInfo
public class AoplogTestController { @GetMapping
public String test(@RequestParam String user){
return "Hi " + user;
}
}

注意:在pom.xml中默认添加的spring-boot-maven-plugin下需要添加repackage的goal才能自动生成日志目录与日志文件,如下所示

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

启动程序,调用@LogInfo标注的接口类下的API时,可以看到控制台有打印接口访问日志,如执行demo程序(源码: https://github.com/ronwxy/springboot-demos/tree/master/springboot-aoplog ),调用 http://localhost:8080/test?user=jboost 时,控制台打印日志如下

[-- ::] [INFO ] [http-nio--exec-] [cn.jboost.springboot.aoplog.controller.AoplogTestController:] --calling: test(user=jboost)
[-- ::] [INFO ] [http-nio--exec-] [cn.jboost.springboot.aoplog.controller.AoplogTestController:] --returning: test( arguments):Hi jboost

日志文件interface.log中打印日志如下,(其中req_id在本次请求的所有日志都相同,这样就可以将一次请求的所有日志串联起来,便于分析与定位问题;elapsed_time标明了方法执行时长,可用于接口性能监测)

{"project":"aoplog-test","timestamp":"2019-06-27T14:29:59,030+0800","log_level":"INFO","thread":"http-nio-8080-exec-1","class_name":"cn.jboost.springboot.aoplog.controller.AoplogTestController","class_method":"test","line_number":null,"message":"calling: test(user=jboost)","stack_trace":"","req_id":"5d146267aa147904bc014e71","elapsed_time":null}
{"project":"aoplog-test","timestamp":"2019-06-27T14:29:59,036+0800","log_level":"INFO","thread":"http-nio-8080-exec-1","class_name":"cn.jboost.springboot.aoplog.controller.AoplogTestController","class_method":"test","line_number":null,"message":"returning: test(1 arguments):Hi jboost","stack_trace":"","req_id":"5d146267aa147904bc014e71","elapsed_time":}

4. 总结

Web项目中经常需要通过查看接口请求及返回参数来定位问题,手动编写代码打印显得繁琐而重复。使用aop-logging通过简单的注解即可实现接口日志自动打印。本文介绍的方案与日志配置模板可直接用于实际项目开发。当然,注解不仅可用于Controller层,也可以用于Service等其它层,但一般Controller层加上即可,避免日志打印过多。

本文示例项目源码地址:https://github.com/ronwxy/springboot-demos/tree/master/springboot-aoplog

我的个人博客地址:http://blog.jboost.cn
我的github地址:https://github.com/ronwxy
我的微信公众号:jboost-ksxy (欢迎关注,及时获取技术干货分享)
——————————————————————————————————


欢迎关注我的微信公众号,及时获取最新分享

Spring Boot从入门到实战:集成AOPLog来记录接口访问日志的更多相关文章

  1. Spring Boot 从入门到实战汇总

    之前写过几篇spring boot入门到实战的博文,因为某些原因没能继续. 框架更新迭代很快,之前还是基于1.x,现在2.x都出来很久了.还是希望能从基于该框架项目开发的整体有一个比较系统的梳理,于是 ...

  2. Spring Boot从入门到实战:整合Web项目常用功能

    在Web应用开发过程中,一般都涵盖一些常用功能的实现,如数据库访问.异常处理.消息队列.缓存服务.OSS服务,以及接口日志配置,接口文档生成等.如果每个项目都来一套,则既费力又难以维护.可以通过Spr ...

  3. Spring Boot从入门到实战(十):异步处理

    原文地址:http://blog.jboost.cn/2019/07/22/springboot-async.html 在业务开发中,有时候会遇到一些非核心的附加功能,比如短信或微信模板消息通知,或者 ...

  4. spring boot 2.x 系列——spring-boot 集成 Swagger2 打造在线接口文档

    文章目录 一.Springfox 与 Swagger 简介 1.1 Springfox 1.2 Swagger 1.3 OpenApi.Swagger.Springfox的关系 二.spring bo ...

  5. Spring Boot从入门到实战:整合通用Mapper简化单表操作

    数据库访问是web应用必不可少的部分.现今最常用的数据库ORM框架有Hibernate与Mybatis,Hibernate貌似在传统IT企业用的较多,而Mybatis则在互联网企业应用较多.通用Map ...

  6. Spring Boot从入门到精通(六)集成Redis实现缓存机制

    Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言 ...

  7. Spring Boot从入门到精通(七)集成Redis实现Session共享

    单点登录(SSO)是指在多个应用系统中,登录用户只需要登录验证一次就可以访问所有相互信任的应用系统,Redis Session共享是实现单点登录的一种方式.本文是通过Spring Boot框架集成Re ...

  8. Spring Boot -01- 快速入门篇(图文教程)

    Spring Boot -01- 快速入门篇(图文教程) 今天开始不断整理 Spring Boot 2.0 版本学习笔记,大家可以在博客看到我的笔记,然后大家想看视频课程也可以到[慕课网]手机 app ...

  9. 图书-技术-SpringBoot:《Spring Boot 企业级应用开发实战》

    ylbtech-图书-技术-SpringBoot:<Spring Boot 企业级应用开发实战> Spring Boot 企业级应用开发实战,全书围绕如何整合以 Spring Boot 为 ...

随机推荐

  1. Extensible File System

    An extensible file system format for portable storage media is provided. The extensible file system ...

  2. C++利用结构

    #include <iostream> using std::cout; using std::endl; //定义结构 struct Box{ double length; double ...

  3. Qt 的几个核心机制总结之 布局(QWidget可以设置setSizePolicy,而QSizePolicy有Fixed,minimum,maximum,preferred,expanding,ignore等7个属性,还可以横竖分开)

    1.Qt布局的作用 Qt的布局是通过布局管理器来实现的,布局管理器负责在父类窗口部件区域构建子窗口部件,使得放置在窗体中的每个窗口部件都有一个适合的大小和位置,并且能够随着应用程序本身的变化而变化从而 ...

  4. 各个版本 Windows 10 系统中自带的 .NET Framework 版本

    原文各个版本 Windows 10 系统中自带的 .NET Framework 版本 Windows 名称 Windows 版本 自带的 .NET Framework 版本 Windows 10 Oc ...

  5. Qt、Qte与Qtopia(Qt嵌入式的发展历程)

    Qt的授权是分为两条线,商业版和开源版.如果使用商业版的Qt,那么开发出的程序可以是私有的和商业的:如果使用的是开源版的Qt,由于其使用的是GPL协议,那么可发出的程序也必须是GPL的.不过自从qt ...

  6. JSON格式的服务接口

    电商接口 京东获取单个商品价格接口: http://p.3.cn/prices/mgets?skuIds=J_商品ID&type=1 用例 ps:商品ID这么获取:http://item.jd ...

  7. 使用VisualTreeHelper.GetDrawing(Visual v)枚举所有Visual内容的对象

    原文:使用VisualTreeHelper.GetDrawing(Visual v)枚举所有Visual内容的对象 C#代码:public void RetrieveDrawing(Visual v) ...

  8. WPF中的菜单模板

    原文:WPF中的菜单模板 资源字典代码如下: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xa ...

  9. svm资料收集

    向量点乘(内积)和叉乘(外积.向量积)概念及几何意义解读: https://blog.csdn.net/dcrmg/article/details/52416832 三角形余弦定理:https://z ...

  10. 离散时间信号常见函数的实现(matlab)

    1. 单位样本序列 δ(n−n0)={1,n=n00,n≠n0 function [x, n] = impseq(n0, n1, n2) n = n1:n2; x = [n == n0]; 2. 单位 ...