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是 ...
随机推荐
- 想提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中
摘要:Kubernetes 很多看起来比较"繁琐"的设计的主要目的,都是希望为开发者提供更多的"可扩展性",给使用者带来更多的"稳定性"和& ...
- JPEG/Exif/TIFF格式解读(4):win10照片旋转win7不识别
xif元数据根据不同的内容分布在五个不同的IFD中. IFD0中的数据是由TIFF定义的基本图像数据,其中有些与照片无关,所以Exif只实现其中一小部分.这部份数据在Photoshop中称为TIFF元 ...
- 如何上传苹果ipa安装包?
如何上传苹果ipa安装包? 引言 在将应用程序上架到苹果应用商店之前,开发者需要学习如何上传ipa安装包.本文将介绍使用appuploader工具将ipa安装包上传到苹果应用商店的详细步骤,并提供 ...
- 2023 年如何将您的应用提交到 App Store
您夜以继日地工作来创建您的梦想应用程序.最后,是时候向全世界宣布您的应用程序了.但不知道如何将您的应用提交到 App Store? 为您的商店获取现成的移动应用程序 将应用程序提交到 App Stor ...
- 性能持续突破!火山引擎ByteHouse上线向量检索能力
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 随着LLM技术应用及落地,数据库需要提高向量分析以及AI支持能力,向量数据库及向量检索等能力"异军突 ...
- Lambda 表达式各种用法,你都会了吗
公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享. 前言 Lambda表达式是 Java 8 中引入的最有影响力的功能之一.它们通过允许简洁而优雅地创建匿名函数来实现 Jav ...
- 【python爬虫】bs4遍历、搜索文档树 bs4使用css选择器 selenium基本使用 selenium查找标签 selenium执行js代码
目录 上节回顾 今日内容 0 bs4遍历文档树 1 bs4搜索文档树 1.1 find方法的其他参数 2 css选择器 3 selenium基本使用 4 无界面浏览器 4.1 模拟登录百度 5 sel ...
- SSL 证书变革之时已至,这些变化你都清楚吗?
现代社会最离不开的是"安全",无论是生命.财产.数据还是其他任何事物都需要各种手段来保证安全,互联网自然也无法免俗.HTTP 协议作为无法加密数据,让所有通信数据都在网络中明文&q ...
- map三层循环遍历,操作数据
let tempArr = this.oldCityList.map(item => { return { value: item.code, text: item.name, type: it ...
- java基础-集合-day14
目录 1. 数据结构 算法 2. 本章的重点 集合 3. collections 4. list 5. 泛型 6. 泛型通配符 7. linkedList 8. 模拟linkedList源码 --面试 ...