Spring Boot日志框架Slf4j+logback
一、简介
Slf4j
Java的简单日志记录外观(Simple Logging Facade for Java )可作为各种日志记录框架(例如java.util.logging,logback,log4j)的简单外观或抽象,允许终端用户在开发时插入所需的日志记录框架。简单来说,Slf4j定义的一种规范,java程序在记录日志时候的规范,这种规范是一个空壳,在实际开发中需要集成具体的日志框架来干活,这种具体的日志框架需要满足一些标准:符合Slf4j定义的标准;能够提供日志记录的功能。
Logback
一个“可靠、通用、快速而又灵活的Java日志框架”。logback是log4j的升级迭代产品,在许多地方相比于log4j有优势:
- 1:性能,提升近10倍,初始内存减少了许多
- 2:对Slf4j友好,同时引用这两个框架之后,甚至不需要额外的配置就可以很融洽的运行起来
- 3:自动重新加载配置文件
- 4:强大的研发团队和完善的文档
logback的三大核心模块:
logback-classic:log4j的一个改良版本,同时整合了对Slf4j的支持
logback-access:Servlet容器集成提供通过HTTP来访问日志的功能
logback-core:其他两个模块的基础模块
二、Spring Boot集成
- 1:pom中新增的dependency
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.2.3</version>
</dependency>
- 2: 在resources下新建logback配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="encoding" value="UTF-8"/>
<!--定义日志文件的存储地址 勿在LogBack的配置中使用相对路径-->
<property name="LOG_HOME" value="/tmp/debris-app-logs"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<!-- 如果命中就禁止这条日志 -->
<onMatch>DENY</onMatch>
<!-- 如果没有命中就使用这条规则 -->
<onMismatch>ACCEPT</onMismatch>
</filter>
<Append>true</Append>
<prudent>false</prudent>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd/HH:mm:ss.SSS}|%X{localIp}|%X{requestId}|%X{requestSeq}|^_^|[%t] %-5level %logger{50}
%line - %m%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/leading-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>256MB</maxFileSize>
<maxHistory>15</maxHistory>
<totalSizeCap>32GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="ACCESS_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Append>true</Append>
<prudent>false</prudent>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd/HH:mm:ss.SSS}|%X{localIp}|%X{requestId}|%X{requestSeq}|^_^|[%t] %-5level %logger{50}
%line - %m%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/leading-access-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>256MB</maxFileSize>
<maxHistory>15</maxHistory>
<totalSizeCap>32GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="LEADING_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<Append>true</Append>
<prudent>false</prudent>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd/HH:mm:ss.SSS}|%X{localIp}|%X{requestId}|%X{requestSeq}|^_^|[%t] %-5level %logger{50}
%line - %m%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/leading-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>256MB</maxFileSize>
<maxHistory>15</maxHistory>
<totalSizeCap>32GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %5p %c.%M:%L - %m%n</pattern>
</encoder>
</appender>
<root additivity="false" level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="LEADING_ERROR"/>
<appender-ref ref="STDOUT"/>
</root>
<logger name="AccessLog" additivity="false">
<appender-ref ref="ACCESS_LOG"/>
</logger>
</configuration>
3:编写测试代码,并启动程序
4:若设置应用logging级别为debug,可以看到日志也记录到了相应的文件中
- 5:记录应用每一个service层中的方法的入参和出参
编写切面aspect
package com.naylor.debrisapp.logback.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.naylor.debrisapp.logback.utils.ObjectUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;
/**
* @BelongsProject: debris-app
* @BelongsPackage: com.naylor.debrisapp.logback.aspect
* @Author: Chenml
* @CreateTime: 2020-08-21 16:09
* @Description: 日志
*/
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger("AccessLog");
private static final String[] ignoreMethods = new String[]{"login", "changePassword", "modifyPassword", "uploadFile", "downloadLatestFileByType", "syncPurchaseRequirement"};
private static final String[] sensitiveWords = null; //new String[]{"password", "token", "base64"};
// 大于100K的log不显示
private static final Integer maxLimit = 1024 * 100;
private static final Integer minLimit = 1024;
//统一记录service层方法入参
@Before("execution(* com.naylor..service..*.*(..))")
public void doBefore(JoinPoint jp) {
Signature signature = jp.getSignature();
if (signature != null) {
StringBuilder log = new StringBuilder("enter className:");
String className = signature.getDeclaringTypeName();
log.append(className);
String methodName = signature.getName();
log.append(",methodName:").append(methodName);
if (!ignoreMethod(methodName)) {
//region 排除含有敏感信息的参数输出逻辑
// String argStr = JSON.toJSONString(jp.getArgs(), LogPropertyFilter.LOG_FILE_FILTER, SerializerFeature.WriteClassName);
// // 排除含有敏感信息的参数输出
// if (!containsSensitiveWords(argStr)) {
// log.append(",args:").append(argStr);
// } else {
// log.append(",args:").append("sensitive word in args and forbidden to print.");
// }
//endregion
String argStr = JSON.toJSONString(jp.getArgs(), SerializerFeature.WriteClassName);
log.append(",args:").append(argStr);
} else {
log.append(",args:").append("ignore method and forbidden to print args.");
}
String logStr = log.toString();
if (Modifier.isPublic(signature.getModifiers())) {
logger.info("####### {}", logStr);
} else {
logger.debug("####### {}", logStr);
}
}
}
//统一记录service层方法出参
@AfterReturning(value = "execution(* com.naylor..service..*.*(..))", returning = "returnValue")
public void doAfterReturn(JoinPoint jp, Object returnValue) {
Signature signature = jp.getSignature();
if (signature != null) {
StringBuilder log = new StringBuilder("leave className:");
String className = signature.getDeclaringTypeName();
log.append(className);
String methodName = signature.getName();
log.append(",methodName:").append(methodName);
// 排除含有敏感信息的log输出
if (!ignoreMethod(methodName)) {
String argStr = ObjectUtil.toString(jp.getArgs());
//region
// if (!containsSensitiveWords(argStr)) {
// log.append(",args:").append(argStr);
// } else {
// log.append(",args:").append("sensitive word in args and forbidden to print.");
// }
//endregion
} else {
log.append(",args:").append("ignore method and forbidden to print args.");
}
log.append(",return:");
if (null != returnValue) {
log.append(returnValue.getClass().getName() + ":");
if (returnValue instanceof Collection) {
log.append("/size:").append(CollectionUtils.size(returnValue));
} else if (returnValue instanceof Map) {
log.append("/size:").append(CollectionUtils.size(((Map) returnValue).entrySet()));
} else {
String resStr = returnValue.toString();
log.append(resStr);
//region
// String printStr = null;
// if (!containsSensitiveWords(resStr)) {
// if (resStr.length() > maxLimit) {
// printStr = resStr.substring(0, minLimit);
// } else {
// printStr = resStr;
// }
// log.append(printStr);
// } else {
// log.append("sensitive word in response and forbidden to print.");
// }
//endregion
}
} else {
log.append("");
}
String logStr = log.toString();
if (Modifier.isPublic(signature.getModifiers())) {
logger.info("####### {}", logStr);
} else {
logger.debug("####### {}", logStr);
}
}
}
//过滤不记录入参出参的方法
private boolean ignoreMethod(String methodName) {
boolean result = false;
if (null != ignoreMethods && ignoreMethods.length > 0) {
for (String checkMethod : ignoreMethods) {
if (checkMethod.equalsIgnoreCase(methodName)) {
result = true;
break;
}
}
}
return result;
}
//过滤掉含有敏感信息的方法
private boolean containsSensitiveWords(String sensiWord) {
boolean result = false;
if (null != sensitiveWords && sensitiveWords.length > 0) {
for (String checkWord : sensitiveWords) {
if (sensiWord.contains(checkWord)) {
result = true;
break;
}
}
}
return result;
}
}
编写service
package com.naylor.debrisapp.logback.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @BelongsProject: debris-app
* @BelongsPackage: com.naylor.debrisapp.logback.service
* @Author: Chenml
* @CreateTime: 2020-08-21 17:22
* @Description: 测试实现
*/
@Service
@Slf4j
public class TestImpl implements Test {
@Override
public String getHello(String id) {
log.info("log.info.service");
return "Hello , World!";
}
}
在浏览器请求编写的service之后,入参和出参已经记录在了文件中
引用:
Logback.xml配置文件详解:https://www.jianshu.com/p/89bed7c7f1d7
https://www.jianshu.com/p/e3aeaf557f14
https://www.jianshu.com/p/b460f28153bb
https://www.jianshu.com/p/34cc56137c5a
logback简介和基本概念:https://www.cnblogs.com/yangyongjie/p/11146921.html
Spring Boot日志框架Slf4j+logback的更多相关文章
- Spring Boot 日志记录 SLF4J
Spring Boot 日志记录 SLF4J 2016年01月12日 09:25:28 阅读数:54086 在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人 ...
- 54. spring boot日志升级篇—logback【从零开始学Spring Boot】
在<44. Spring Boot日志记录SLF4J>章节中有关相关的介绍,这里我们在深入的了解下logback框架. 为什么要使用logback ? --在开发中不建议使用System. ...
- Springboot 系列(四)Spring Boot 日志框架
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 Spring 框架选择使用了 JCL 作为默 ...
- 十四、Spring Boot 日志记录 SLF4J
在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...
- spring boot 日志介绍 以及 logback配置示例
https://www.cnblogs.com/flying607/p/7827460.html 以下是springboot的一个局部依赖关系: 可以看到,java util logging(jul) ...
- 【串线篇】spring boot日志框架
一.日志框架 小张:开发一个大型系统: 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? 2.框架来记录系统的一些运行时信息:日志框架 ...
- (44). Spring Boot日志记录SLF4J【从零开始学Spring Boot】
在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...
- Spring Boot日志集成实战
Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...
- Spring Boot日志集成
Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...
- 50. Spring Boot日志升级篇—log4j【从零开始学Spring Boot】
如果你使用的是spring boot 1.4.0版本的话,那么你可能需要配合以下文章进行学习 90.Spring Boot 1.4 使用log4j错误[从零开始学Spring Boot] Log4j是 ...
随机推荐
- 云小课|CDN第5课 CDN入门之—我的网站可以用CDN加速吗?
摘要:CDN(Content Delivery Network,内容分发网络)通过将源站资源缓存到遍布各地的边缘节点服务器上,用户可以就近获取资源,从而达到加速的效果. 本文分享自华为云社区<[ ...
- chrome对页面重绘和回流以及优化进行优化
页面的绘制时间(paint time)是每一个前端开发都需要关注的的重要指标,它决定了你的页面流畅程度.而如何去观察页面的绘制时间,找到性能瓶颈,可以借助Chrome的开发者工具. 回流与重绘 1. ...
- 信创就用国产的生态,Solon v2.6.4 发布
Solon 是什么框架? Java 新的"生态级"应用开发框架.从零开始构建,有自己的标准规范与开放生态(历时六年,具备全球第二级别的生态规模). 相对于 Spring,有什么特点 ...
- WxJava for Solon - 咱也不知道为啥要写
? 应 Solon 技术交流群里小伙伴的要求,我分享下在 Solon 中使用 WxJava 的经验.类库. 具体实现 提供统一的 Yaml 配置 package cn.edu.hnuahe.mount ...
- HanLP — HMM隐马尔可夫模型 -- 训练&预测
BMES => B-begin:词语开始.M-middle:词语中间.E-end:词语结束.S-single:单独成词 训练的过程,就是求三个矩阵的过程 初始概率矩阵 转移概率矩阵 发射矩阵 每 ...
- 自媒体时代的贤内助——AI 视频云
视频,通常是一段由活动画面组成的传递信息的内容.在如今这个信息爆炸的时代,我们每天都要在浩瀚的内容海洋中寻找需求的信息.一个视频是否吸引人内容固然是最重要的,但是播放是否流畅,画质是否清晰的影响力也不 ...
- 体验有礼 | 1 分钟 Serverless 极速部署个人网盘,真网盘真好用!
你想自己搭一个无敌好用的网盘吗? 想 接着看,还有奖品呢! -- 当前,网盘几乎已成为现代人的标配,而市面上的网盘功能.费用各异,让用户们陷入了对比价格和功能的迷阵中.别对比了,动手吧!作为对存储.流 ...
- 函数计算 HTTP 触发器支持异步,解放双手搭建 Web 服务
作者| 阿里云Serverless技术专家 澈尔 当前阿里云函数计算支持两种类型的函数:事件函数和 HTTP 函数.其中 HTTP 函数结合 HTTP 触发器,能够支持用户直接通过 HTTP 请求利用 ...
- java调用百度地图接口输入名称查经度纬度
如何注册ak号请参考https://blog.csdn.net/weixin_42512684/article/details/115843299 package manager.tool; impo ...
- appium(三)使用方法
一.appium环境搭建(先决条件) 1.安装JDk 2.安装SDK 参考文章:https://www.cnblogs.com/mrwhite2020/p/13160994.html 3.安装appn ...