常见日志门面和技术介绍:
| 门面 | 技术 | 简介 | 又日志的规范,可以近似的认为是所有日志技术需要实现的公共接口 | 实现记录日志的技术,有的用的自己的规范接口,有的是用的公共的规范接口 | 常用 | slf4j,jcl | log4j,log4j2,logback,jul | 说明 | jcl只有被jul所使用,slf4j被绝大多数日志框架所使用 当然只有logback是默认实现的slf4j规范,log4j和log4j2有自己的默认规范,要想实现slf4j规范需要额外的引入jar包和进行其他操作,关于他们的整合参照视频,深究工作原理参照视频,参考文章 |
SpringBoot整合各种日志框架:
各种日志框架的整合参考视频(包括JUL、JCL,SLF4J,LogBack,Log4j,Log4j2 maven使用及SpringBoot整合,不包括Log4j,Log4j2实现Slf4j接口,如果有需要请参考视频)
上述视频教程的参考demo及书面教程
建议有条件的观看视频 ,没条件的看上述的书面材料
关于各种技术(比如log4j、log4j2、logback)的配置文件详情可自行百度,下面是一些推荐网站
Log4j.properties配置文件详解
Log4j.xml配置文件详解
Log4j配置文件视频教程
log4j2.xml配置文件详解(配置教程不可用)
Log4j2.properties配置文件详解
logback配置文件详解及配置教程
logback配置文件详解及配置教程2
默认配置:
依赖于开发者选择的日志框架,这些对应的配置文件会被加载;
日志框架 | 配置文件 | Logback | logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy | Log4j | log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml | Log4j2 | log4j2-spring.xml, log4j2.xml | JDK(JAVA Util Logging) | logging.properties |
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,spring boot可以为它添加一些spring boot特有的配置项,默认的命名规则,并且放在 src/main/resources 下面即可。
因为springBoot默认整合的就是logback,所以简单的使用logback,不需要加logback的配置文件,只要在yml文件中增加配置即可,具体参照视频最后两节进行学习。
SpringBoot整合log4J:
- springboot 默认就是使用SLF4J作为日志门面,logback作为日志实现来记录日志。
- SpringBoot整合log4J? 需要替换日志启动器,具体做法如下:
-
首先放入log4j的配置文件(properties或xml都可,下面是个简单的示例)
log4j.rootLogger=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%X{requestID} - %C -%M -- %d{yyyy-MM-dd HH:mm:ss} -- %m%n
- 然后加入依赖
<!--日志 start-->
<!--log4j核心依赖-必须加-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--log4j实现slf4j依赖,可不加,加了之后实现slf4j接口,建议加-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--日志end-->
- ?然后剔除原先的日志启动器
- ?找到logback-classic右击选择Exclude即可
- 打印出来的就是log4j的日志
-
?SpringBoot整合log4J2:
-
springboot 默认就是使用SLF4J作为日志门面,logback作为日志实现来记录日志。 -
SpringBoot整合JUL、JCL、log4j、logback? 见参考demo -
SpringBoot整合log4J2? 需要替换日志启动器,具体做法如下: -
Log4j2? 不建议使用properties方式??? 因为spring默认不识别log4j2的配置文件 -
首先放入log4j2的配置文件
- 然后加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
- 然后剔除原先的日志启动器
- 找到spring-boot-starter-logging右击选择Exclude即可
?SpringBoot 链路跟踪技术? MDC
-
参考视频 -
参考资料 -
MDC? 异步下的跨线程操作(参考资料1 ---参考资料2)
MDC是一种链路跟踪技术,所谓的链路跟踪技术,就是可以对每一次请求的日志加以区分,底层的原理是threadLocal。具体的配置方式如下:
各种日志的配置方式见参考资料? ,但是都大差不差? ,以? log4j为例,其他的见参考资料:
首先在配置文件中加入:
在输出的日志格式加%X{s},s为MDC中的key值,如下,key值为requestID?
然后只需要在代码中加入? 该key值以及对应的value即可
?得到的日志每一次不同的请求都会标记一个reuqestID
- ?关于异步情况下,MDC的跨线程传输问题。
- log4j、log4j2自身对MDC已经做了优化,所以如果springboot整合的是log4j、log4j2就不需要考虑MDC同步和异步的问题,如:
log4j线程外只加了一次,可以看到即使进行异步调用,MDC也会进行传播。?
- ?但是springBoot 默认使用的是logBack,logBack对MDC的异步是没有经过处理的,所以当整合了logBack又进行异步操作时,需要手动的进行异步传输,参照参考资料和上述文档,
只在异步前加了一次MDC,然后日志只要是异步的,MDC就传输不到
所以,logback下需要进行异步操作,见参考资料1和参考资料2?
因为在实际应用中,所有的异步都是结合线程池来做的,所以一般我们会结合?runnable来实现
?这样就简化了很多繁杂的操作。
?日志脱敏
-
本次日志脱敏主要是针对logBack,log4j,log4j2也可以参考 -
参考资料1? 和参考资料2 -
参考demo
简述下上述demo是怎么添加日志脱敏的,首先在 appender 标签里面添加??encoder 和?layout 标签? ,具体内容如下,完整版见上述demo:
<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="com.example.demo.util.MaskingPatternLayout">
<maskPattern>mobileNo---(mobileNo=)(\d{3})\d{4}(\d{4})---$1$2****$3</maskPattern><!--toString-->
<maskPattern>mobileNo---("mobileNo":)("\d{3})\d{4}(\d{4})"---$1$2****$3</maskPattern><!--JSON-->
<maskPattern>手机号---("手机号":)("\d{3})\d{4}(\d{4})"---$1$2****$3</maskPattern><!--JSON-->
<maskPattern>idCardNo---(idCardNo=)(\w{4})\w{7,10}(\w{4})---$1$2******$3</maskPattern><!--toString-->
<maskPattern>idCardNo---("idCardNo":)("\w{4})\w{7,10}(\w{4})"---$1$2******$3</maskPattern><!--JSON-->
<maskPattern>name---(name=)([\u4E00-\u9FA5]{1})[\u4E00-\u9FA5]{1,}---$1$2**</maskPattern><!--toString-->
<maskPattern>name---("name":)("[\u4E00-\u9FA5]{1})[\u4E00-\u9FA5]{1,}"---$1$2**</maskPattern><!--JSON-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
</layout>
</encoder>
</appender>
然后添加相应的解析类,解析该配置文件:
package com.example.demo.util;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MaskingPatternLayout extends PatternLayout {
/*
* 日志脱敏开关
* */
private static Boolean converterCanRun = Boolean.TRUE;
/*
* list 用来存储正则表达式以及脱敏范围
* */
private static List<String> regulars = new ArrayList<>();
public void addMaskPattern(String maskPattern) {
System.out.println(maskPattern);
regulars.add(maskPattern);
}
@Override
public String doLayout(ILoggingEvent event) {
return maskMessage(super.doLayout(event));
}
private String maskMessage(String message) {
//创建二维数组 用来存储 获取到的配置文件
String[][] values = new String[regulars.size()][3];
//然后依次分解 存入数组中
for(int order = 0;order < regulars.size();order++){
String tmp = regulars.get(order);
//以---(英文)为分隔符 然后进行存储
String[] regs = tmp.split("---");
if(regs.length == 3){
values[order] = regs;
}
}
//存入二维数组后 遍历 进行正则的匹配
if(!converterCanRun){
return message;
}
for(int order = 0; order <values.length ;order++){
if(message.contains(values[order][0])){
Matcher matcher = Pattern.compile(values[order][1]).matcher(message);
message = matcher.replaceAll(values[order][2]);
}
}
return message;
}
}
然后添加相应的使用类即可? :
@RequestMapping("getUser/{id}")
public String GetUser(@PathVariable int id, Model model) {
logger.info("mobileNo=17855911518");
logger.info("idCardNo=140321199606482668");
logger.info("name=王爬爬");
log.info("mobileNo=17855911518============idCardNo=140321199606482668=============name=王爬爬");
Map<String, String> user = new HashMap<String, String>();
user.put("mobileNo", "17855911518");
user.put("手机号", "17855911518");
user.put("idCardNo", "140321199606482668");
user.put("name", "王爬爬");
JSONObject userDetails = new JSONObject(user);
logger.info("User JSON: {}", userDetails);
User u = userService.Sel(id);
model.addAttribute("user", u);
logger.error("xxxxxxxxxxx" + u.toString());
return "index";
}
最后观察输出的日志记录:
?可以看到,所有需要加密的都已经进行加密了。
|