一、简介

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的更多相关文章

  1. Spring Boot 日志记录 SLF4J

    Spring Boot 日志记录 SLF4J 2016年01月12日 09:25:28 阅读数:54086 在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人 ...

  2. 54. spring boot日志升级篇—logback【从零开始学Spring Boot】

    在<44. Spring Boot日志记录SLF4J>章节中有关相关的介绍,这里我们在深入的了解下logback框架. 为什么要使用logback ? --在开发中不建议使用System. ...

  3. Springboot 系列(四)Spring Boot 日志框架

    注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 Spring 框架选择使用了 JCL 作为默 ...

  4. 十四、Spring Boot 日志记录 SLF4J

    在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...

  5. spring boot 日志介绍 以及 logback配置示例

    https://www.cnblogs.com/flying607/p/7827460.html 以下是springboot的一个局部依赖关系: 可以看到,java util logging(jul) ...

  6. 【串线篇】spring boot日志框架

    一.日志框架 小张:开发一个大型系统: 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? 2.框架来记录系统的一些运行时信息:日志框架 ...

  7. (44). Spring Boot日志记录SLF4J【从零开始学Spring Boot】

    在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...

  8. Spring Boot日志集成实战

    Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...

  9. Spring Boot日志集成

    Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...

  10. 50. Spring Boot日志升级篇—log4j【从零开始学Spring Boot】

    如果你使用的是spring boot 1.4.0版本的话,那么你可能需要配合以下文章进行学习 90.Spring Boot 1.4 使用log4j错误[从零开始学Spring Boot] Log4j是 ...

随机推荐

  1. 训练千亿参数模型的法宝,昇腾CANN异构计算架构来了~

    摘要:盘古的训练以「昇腾AI处理器」为基座,同时借助了「CANN 异构计算架构」,让硬件算力得以充分释放,大大缩短了训练时间! 2021年4月,"华为云盘古大模型"一炮走红AI人工 ...

  2. 解析WeNet云端推理部署代码

    摘要:WeNet是一款开源端到端ASR工具包,它与ESPnet等开源语音项目相比,最大的优势在于提供了从训练到部署的一整套工具链,使ASR服务的工业落地更加简单. 本文分享自华为云社区<WeNe ...

  3. Python 获取控制台输入的值

    获取控制台输入参数 if __name__ == '__main__': while 1: question = input('用户:') answer = "你的问题是:" + ...

  4. Spring Boot CMD 运行日志输出中文乱码

    Spring Boot 在Windows CMD 中运行,日志输出中文乱码name="CONSOLE" 设置成 charset utf-8 ,在windows cmd 中运行时,l ...

  5. 玩转Python:处理图像,两个非常重要的库,很实用,附代码

    在Python中,图像处理是一个涉及图像分析.编辑和处理的广泛领域.有几个流行的库通常用于处理图像,每个库都有其特殊的功能和优势.以下是一些常用的Python图像处理库: 1. Pillow (PIL ...

  6. 浅谈splice( )与slice( )

    1.splice( ) 概念:splice( )用于修改原始数组,它可以删除.插入.替换数组的元素,并返回被删除的元素组成的新数组. 语法:splice(start,deleteCount,item1 ...

  7. es报错记录

    ElasticsearchException[failed to bind service]; nested: AccessDeniedException[/usr/share/elasticsear ...

  8. 你想怎么使用 Serverless 函数计算?(评测赢好礼 )

    随着云计算发展,云原生热度攀升,Serverless 架构崭露头角且发展势头迅猛.不仅被更多开发者所关注,市场占有率也逐年提高.阿里云函数计算(Function Compute)是一个事件驱动的全托管 ...

  9. 一次完整的HTTP服务过程

  10. The requested URL could not be retrieved

    在开发过程中,调用对外接口,返回了一长串的标签提示,如下 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "ht ...