SpringBoot使用Slf4j+Log4j完成项目的日志记录


前言

本示例采用SpringBoot项目使用SpringAOP记录日志,Slf4j作为日志门面,Log4j2作为日志实现实,实现开发中的日志记录.


部分效果展示 :

日志文件 :

日志信息 :

代码具体实现如下 :

  1. 因为SpringBoot自动集成了Slf4j日志门面并且同样集成了logback等日志实现,Log4j2和Logback并不能共存,所以我们要先排除依赖,并添加Log4j2的依赖与SpringAOP的依赖。
    避坑 : 在网上有很多人说是在 spring-boot-starter-web 这个启动器里面进行依赖排除,但是经过我的测试这种方法有时候并不是有效的,所以复制上面的依赖就好。
 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><!-- 排除springboot自带的logback框架 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><!-- 引入log4j2依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><!-- SpringAOP启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.4.5</version></dependency>

二、编写log4j2-spring.xml : log4j2的配置文件

代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--status = "warn" 日志框架本身的输入日志级别monitorInterval = "5" 自动加载配置文件的时间间隔不低于5s-->
<Configuration status="warn" monitorInterval="5"><!-- 日志级别以及优先级排序 :在log4j2中, 共有8个级别,按照从低到高为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。All:最低等级的,用于打开所有日志记录.Trace:是追踪,就是程序推进一下.Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.Info:消息在粗粒度级别上突出强调应用程序的运行过程.Warn:输出警告及warn以下级别的日志.Error:输出错误信息日志.Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.OFF:最高等级的,用于关闭所有日志记录.程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。--><!--集中配置属性进行管理--><Properties><!--定义格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符%logger{36} 表示 Logger 名字最长36个字符--><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level}[%thread] %style{%logger{36}}{cyan} : %msg%n" /><!-- 定义日志存储的路径,绝对路径 --><property name="FILE_PATH" value="存储的绝对路径" /><property name="FILE_NAME" value="项目名称" /></Properties><!--日志处理--><Appenders><!--*********************控制台日志***********************--><!--target: SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.--><console name="Console" target="SYSTEM_OUT"><!--输出日志的格式和颜色--><PatternLayout pattern="${LOG_PATTERN}" disableAnsi="false" noConsoleNoAnsi="false"/><!--控制台只输出level及其以上级别的信息(onMatch)放行,其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/></console><!--*********************文件日志***********************--><!--按照一定规则查分日志文件的appender /logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log/logs:放在logs这个目录下,/$${date:yyyy-MM-dd}:以天为单位生成文件夹myrollog-%d{yyyy-MM-dd-HH-mm}-%d: 以分钟为单位到达了指定大小在进行拆分.gz 进行压缩归档--><!--error 运行时异常日志信息--><RollingFile name = "errorRollingFile" fileName = "${FILE_NAME}/error日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/error-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志级别过滤器,文件只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n"/><!--在系统每次启动时,触发拆分规则,生产一个新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小进行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照时间节点进行拆分--><TimeBasedTriggeringPolicy/><!--在同一个目录下,文件的个数限定为30个,超过按照实际进行覆盖--><DefaultRolloverStrategy max="30"/></RollingFile><!--fatal 正常运行时日志--><RollingFile name = "fatalRollingFile" fileName = "${FILE_NAME}/fatal日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/fatal-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志级别过滤器,文件只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="fatal" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="${LOG_PATTERN}"/><!--在系统每次启动时,触发拆分规则,生产一个新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小进行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照时间节点进行拆分--><TimeBasedTriggeringPolicy/><!--在同一个目录下,文件的个数限定为30个,超过按照实际进行覆盖--><DefaultRolloverStrategy max="30"/></RollingFile><!--info 操作日志--><RollingFile name = "infoRollingFile" fileName = "${FILE_NAME}/info日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志级别过滤器,文件只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="${LOG_PATTERN}"/><!--在系统每次启动时,触发拆分规则,生产一个新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小进行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照时间节点进行拆分--><TimeBasedTriggeringPolicy/><!--在同一个目录下,文件的个数限定为30个,超过按照实际进行覆盖--><DefaultRolloverStrategy max="30"/></RollingFile></Appenders><!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。--><!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效--><loggers><!--过滤掉spring和mybatis的一些无用的信息--><logger name="org.mybatis" level="info" additivity="false"><AppenderRef ref="Console"/></logger><!--监控系统信息--><!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。--><Logger name="org.springframework" level="info" additivity="false"><AppenderRef ref="Console"/></Logger><root level="info"><!--控制台--><appender-ref ref="Console"/><!--用户操作文件--><appender-ref ref="infoRollingFile"/><!--调试错误文件--><appender-ref ref="errorRollingFile"/><!--正常运行文件--><appender-ref ref="fatalRollingFile"/></root></loggers></Configuration>

三、添加全局异步日志 : log4j2.component.properties

Log4j2的最大优点就是它的异步Logger,主要就是性能更好,这个这里不做过多解释。

#全局异步日志开启,提高日志性能
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

四、声明log4j2的配置文件路径 :

在application.properties的SpringBoot主配置文件中添加如下配置 :

我这里是给配置文件添加了个config的包目录,你要改成你自己的路径。

五、自定义注解类 :

自定义注解 : 主要是作用就是为了自定义方法操作,用于向AOP中添加方法操作的日志信息,既然已经到项目日志阶段那么我相信你也已经不是一个小白了,注解就是一个标注这里也不做过多解释。

//作用在方法上
@Target(ElementType.METHOD)
//运行时
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotationMethod {//模块名称String module() default "";//操作名称String operator() default "";//扩展属性String value() default "";
}

六、创建HttpContextUtil工具类用于在IP工具类中获取IP使用 :

public class HttpContextUtil {public static HttpServletRequest getHttpServletRequest(){return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();}}

七、创建IP获取工具类 :

用于获取访问的用户IP,并记录到日志中。

@Slf4j
public class IpUtils {/*** 获取IP地址* <p>* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址*/public static String getIpAddr(HttpServletRequest request) {String ip = null, unknown = "unknown", seperator = ",";int maxLength = 15;try {ip = request.getHeader("x-forwarded-for");if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}} catch (Exception e) {log.error("IpUtils ERROR ", e);}// 使用代理,则获取第一个IP地址if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {int idx = ip.indexOf(seperator);if (idx > 0) {ip = ip.substring(0, idx);}}return ip;}/*** 获取ip地址** @return*/public static String getIpAddr() {HttpServletRequest request = HttpContextUtil.getHttpServletRequest();return getIpAddr(request);}
}

八、 编写AOP日志切面类

此类用于保存和执行具体的日志信息。

/*** @author 码不多* @version 1.0* @description: 此类采用SpringAOP,用于记录日志*///声明AOP类
@Aspect
//声明组件
@Component
//使用Slf4j日志门面
@Slf4j
public class AopLogUtil {//定义切入点方法 标注这个自定义注解@Pointcut("@annotation(com.shouzhong.epidemicprevention.annotation.LogAnnotationMethod)")public void pt(){}/*** 功能描述: 执行通知日志的方法* @author 码不多* @date 2021/8/16* @param point* @return java.lang.Object*///拦截所有的Controller或者RestController或者自定义注解标识的方法@Around("@within(org.springframework.stereotype.Controller) ||"+"@within(org.springframework.web.bind.annotation.RestController) ||"+"pt()")private Object runAndSaveLog(ProceedingJoinPoint point){//获取获取当前时间long beginTime = System.currentTimeMillis();//执行原始方法Object result = null;try {result = point.proceed();} catch (Throwable throwable) {//出现异常打印error日志log.error("Aop中方法出现异常",throwable);}//获取方法执行时间long runtime = System.currentTimeMillis() - beginTime;//调用保存日志的方法recordLog(point,runtime);//将结果返回return  result;}/*** 功能描述: 保存日志的方法* @author 码不多* @date 2021/8/16* @param joinPoint time* @return void*/private void recordLog(ProceedingJoinPoint joinPoint,long time){//获取类名Object target = joinPoint.getTarget();String canonicalName = target.getClass().getCanonicalName();//获取模块名MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();//获取方法名Signature signature = joinPoint.getSignature();String methodName = signature.getName();//获取请求参数Object[] args = joinPoint.getArgs();String params = JSON.toJSONString(args[0]);//获取request,设置ip地址HttpServletRequest request = HttpContextUtil.getHttpServletRequest();//获取IP地址String ipAddr = IpUtils.getIpAddr(request);//获取注解对象LogAnnotationMethod logAnnotation = method.getAnnotation(LogAnnotationMethod.class);//添加类名到日志,采用占位符赋值log.info("类名: {}",canonicalName);//添加模块日志信息,采用占位符赋值,通过注解对象获取注解中的值log.info("模块名: {}",logAnnotation.module());//添加方法名到日志,采用占位符赋值log.info("方法名: {}",methodName);//添加操作到日志,采用占位符赋值,通过注解对象获取注解中的值log.info("操作: {}",logAnnotation.operator());//添加请求参数信息到日志,采用占位符赋值log.info("请求参数: {}:",params);//添加ip地址到日志,采用占位符赋值log.info("ip地址: {}",ipAddr);//添加执行时间到日志,采用占位符赋值log.info("执行时间: {} ms",time);//日志结束log.info("#####################log End####################");补充001 ://不使用IPUtils获取ip和一些其他请求头日志信息的方法: logger.info不在这里定义。还是在上面的代码中/*     // 接收请求,记录请求中的内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 记录下请求内容//请求的urllogger.info("URL : " + request.getRequestURL().toString());//请求的方法类型 : GET、POST...logger.info("HTTP_METHOD : " + request.getMethod());//IP地址logger.info("IP : " + request.getRemoteAddr());//类方法logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());//参数数组logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));//获取所有参数方法 ://获取请求参数们Enumeration<String> enu=request.getParameterNames();//遍历while(enu.hasMoreElements()){//取出请求参数名String paraName=(String)enu.nextElement();//请求的具体参数值System.out.println(paraName+": "+request.getParameter(paraName));}}*/补充002:/*//如果有文件上传的参数MultipartFile类型,为了避免冲突可以给它们添加进集合展示// 接收请求,记录请求中的内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//创建集合存储请求参数ArrayList paramsList = new ArrayList();//获取所有参数方法 :Enumeration<String> enu=request.getParameterNames();while(enu.hasMoreElements()){String paraName=(String)enu.nextElement();//将参数添加到集合paramsList.add(paraName+": "+request.getParameter(paraName));}log.info("请求参数 :{}",paramsList)*/}
}

九、控制器日志添加

在你需要记录操作日志的Controller层的请求映射的方法上添加你的自定义注解,并将你的方法日志信息添加到你自定义的属性值中。

    //自定义注解,声明方法日志@LogAnnotationMethod(module = "获得信息列表",operator = "分页查询或多条件查询信息需求列表")@RequestMapping("/InforDS")public Pagination<InforDemandSide> inforDemandSide(Pagination pagination){xxxxxxxxxxxxxxxxx;}

十、开启控制台Mybatis的sql输出 :

在application.properties的主配置文件中添加如下配置


简单总结一下 :

整体日志记录就是采用面向切面编程,你可以在配置文件中配置你的Appender或者修改你的日志级别。

如果你想在方法中使用日志记录具体的操作,你可以在你需要记录的类上添加@Slf4j这个注解,在你需要记录日志的地方直接使用 log.xxx() 直接记录你的日志即可。

如果这篇文章对你有帮助请为我点个小赞支持一下!

SpringBoot使用Slf4j+Log4j2完成项目的日志记录相关推荐

  1. 在云环境上使用SLF4J对Java程序进行日志记录

    我开发了一个Java应用,部署到云环境上之后,用postman测试发现不能按照我期望的工作,但是返回的消息对我没有任何帮助. 因为部署在云端的应用很难像本地Java应用一样调试,所以我打算用SLF4J ...

  2. php实现项目的日志记录功能,tp5框架使用composer实现日志记录功能示例

    本文实例讲述了tp5框架使用composer实现日志记录功能.分享给大家供大家参考,具体如下: tp5实现日志记录 1.安装 psr/log composer require psr/log 它的作用 ...

  3. springboot 利用aop实现系统日志和操作日志记录

    1.目的 通过aop及注解的方式,记录异常信息和特定的操作日志到数据库. 2.引入依赖 <dependency><groupId>org.springframework.boo ...

  4. 一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)

    文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :<尼恩Java面试宝典>持续更新+ 史上最全 + 面 ...

  5. log4j2 无日志记录_在Log4j2中更好地执行非日志记录器调用

    log4j2 无日志记录 使用Log4j 1.x并希望避免在某些情况下可能会造成额外的性能影响(即使实际上未记录该消息)时,通常使用日志记录防护 . Java的简单日志记录外观 ( SLF4J )带给 ...

  6. SLF4j使用、统一系统中所有的日志记录到slf4j

    SLF4j使用 在系统中使用SLF4j 开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法:给系统导入slf4的jar和logback的实现jar import ...

  7. 一文带你扫盲日志记录的基础知识

    文章目录 一.日志记录入门 二.日志记录域 三.日志记录安全使用场景 四. 设计.实现和测试注意事项 五. 要记录的事件 六.事件属性 七.使用 OWASP 进行安全日志记录 监控应用程序基础结构的健 ...

  8. 安装程序未能打开日志文件_工地开工,安全日志记录这样写才规范!

    一.安全日志的理解 施工安全日志是从工程开始到竣工,由专职安全员对整个施工过程中的重要生产和技术活动的连续不断的详实记录.是项目每天安全施工的真实写照,也是工程施工安全事故原因分析的依据,施工安全日记 ...

  9. SpringBoot 项目使用 SLF4J+logback 进行日志记录,来增强可维护性

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:云深不知处 blog. ...

最新文章

  1. shell 调用mysql 存储过程_shell调用mysql的存储过程以及SQL
  2. python tkinter button颜色变不了_更改函数中Tkinter按钮的颜色
  3. jersey tomcat MySQL_tomcat web容器中,调用jersey client端报错的处理
  4. selenium webdriver(python)_selenium、webdriver及浏览器的关系及对应版本安装
  5. Python中从头开始实现神经网络 - 介绍
  6. 手机的小窗口怎么弄_荣耀9X如何设置桌面小工具?划重点,这个要考
  7. linux代码段起始地址设置,Arch Linux安装后的一些初始设置简介
  8. C++socket编程(三):3.5 accept读取用户的连接信息
  9. 输入输出(I/O)流。
  10. 个人练习-jq 鼠标移上移出查看图片(放大)提示
  11. java读取配置文件中文乱码
  12. 【测试沉思录】5. 测试人员如何快速熟悉新业务?
  13. 软件无线电 --- Rtlsdr系统原理框图
  14. windows核心编程之用户模式下的线程同步
  15. smb.conf - Samba组件的配置文件
  16. html 根据坐标画多边形,Canvas多边形绘制的实现方法
  17. 人民币大小写格式转换
  18. Mac电脑3D渲染和动画制作KeyShot Pro中文
  19. Android 一个TextView中设置文字不同字体大小和颜色的最完整方法
  20. Vue过滤器:第二天

热门文章

  1. java快捷键禁用_pycharm 掌握这些快捷键,你就是大神!!
  2. python使用ssh 中文_Python3制作简易SSH登录工具
  3. [转载] 微服务安全和治理
  4. c#c#继承窗体_C#继承能力问题和解答 套装5
  5. js手机号中间四位_11位手机号码隐藏中间四位数,学会Substitute函数一键搞定!...
  6. 小样本点云深度学习库_合成鲁棒的对抗样本来欺骗深度学习分类器
  7. Visual Studio项目版本转换器(c#项目版本转换器 v1.0)
  8. Android 运行时异常 Binary XML file line # : Error inflating class
  9. Apache httpd 配置HTTPS SSL访问 443
  10. 细分shared_ptr智能指针在各个版本的使用情况