小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
- 在项目上线环境中,需要记录程序运行时产生的各种错误信息、状态信息、调试信息、执行时间记录等日志信息。可以用于查找问题、定位数据等等操作。
- 日志的具体实现可以有log4j和logback等,这里我们使用SLF4J作为日志系统的实现。
使用SLF4J
- 使用idea工具可以安装lombok插件,并引入maven包:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
- 在需要打印日志的类上增加@Slf4j注解,如果查看编译后的class文件,就会发现注解@Slf4j会编码成下列代码:
public static final Logger log = LoggerFactory.getLogger(TestLog.class);
- 打印日志时使用下列方式打印信息即可:
/**
* @Author: ZRH
* @Date: 2021/9/27 17:25
*/
@Slf4j
public class TestLog {
//public static final Logger log = LoggerFactory.getLogger(TestLog.class);
public static void main (String[] args) {
Map<String, String> params = new HashMap<>();
params.put("Phones", "13668200646,15222222222,15648523699");
params.put("timestamp", "1231");
params.put("NAME", "张三");
params.put("身份证", "150303195208077885,15030319520807158X,15030319520807908X");
String phone = "15236547789";
log.info(phone);
log.info("测试日志电话:{}", phone);
log.info("测试日志json对象:{}", JSONObject.toJSONString(params));
}
}
------------------
打印结果如下:
15236547789
测试日志电话:15236547789
测试日志json对象:{"Phones":"13668200646,15222222222,15648523699","身份证":"150303195208077885,15030319520807158X,15030319520807908X","timestamp":"1231","NAME":"张三"}
脱敏操作
-
这里可以发现,打印的日志把完整的手机号也打印出来了,如果有一些客户敏感信息,如身份证、手机号、银行卡等等信息,是需要进行脱敏的,
-
特别是如果线上日志是对接了第三方日志系统(如阿里云SLS日志服务等),之前就有用户数据信息没有经过日志脱敏就直接打印输出,导致数据泄漏引发一系列问题 -_-
-
首先需要自定义实现消息脱敏类SensitiveLogDataConverter并继承MessageConverter类,由logback提供的一个日志消息转化超类。代码如下:
/**
* 自定义日志脱敏类
*
* @Author: ZRH
* @Date: 2021/9/27 17:22
*/
public final class SensitiveLogDataConverter extends MessageConverter {
/**
* 银行卡和姓名正则匹配式
*/
private static Pattern bankCardPattern = Pattern.compile("(\D)([3-6]\d{3})(\d{8,12})(\d{4})(\D)");
private static Pattern namePattern = Pattern.compile("([^\u4e00-\u9fa5])([\u4e00-\u9fa5])([\u4e00-\u9fa5]{1,3})([^\u4e00-\u9fa5])");
/**
* 手机号和身份证正则匹配式
*/
private final static Pattern PHONE_PATTERN = Pattern.compile("(?<!\d)(1\d{10})(?!\d)");
private final static Pattern ID_CARD_PATTERN = Pattern.compile("(?<!\d)(\d{6})([19,20]\d{7})(\d{3}[0-9Xx])(?!\d)");
@Override
public String convert (ILoggingEvent event) {
try {
final Set<String> list;
String logMsg = event.getFormattedMessage();
if (!(list = validateIdCard(logMsg)).isEmpty()) {
for (String param : list) {
logMsg = logMsg.replaceAll(param, SensitiveInfoUtil.desensitizePhoneOrIdCard(param));
}
}
return logMsg;
} catch (Exception e) {
}
return super.convert(event);
}
/**
* 获取日志字符串内容中符合手机号和身份证匹配的内容项
*
* @param param
* @return
*/
private static Set<String> validateIdCard (String param) {
Set<String> set = new HashSet<>();
// 匹配手机号
Matcher phoneMatcher = PHONE_PATTERN.matcher(param);
while (phoneMatcher.find()) {
set.add(phoneMatcher.group());
}
// 匹配身份证
Matcher idCardMatcher = ID_CARD_PATTERN.matcher(param);
while (idCardMatcher.find()) {
set.add(idCardMatcher.group());
}
return set;
}
}
- 然后还需要配置一个自定义日志打印格式文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="logs/yx-api-admin.log"/>
<!-- 指定脱敏类的位置 -->
<conversionRule conversionWord="msg" converterClass="com.work.web.log.SensitiveLogDataConverter"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="info"/>
</root>
</configuration>
- 还是刚才的示例,查看最终的执行效果:
/**
* @Author: ZRH
* @Date: 2021/9/27 17:25
*/
@Slf4j
public class TestLog {
//public static final Logger log = LoggerFactory.getLogger(TestLog.class);
public static void main (String[] args) {
Map<String, String> params = new HashMap<>();
params.put("Phones", "13668200646,15222222222,15648523699");
params.put("timestamp", "1231");
params.put("NAME", "张三");
params.put("身份证", "150303195208077885,15030319520807158X,15030319520807908X");
String phone = "15236547789";
log.info(phone);
log.info("测试日志电话:{}", phone);
log.info("测试日志json对象:{}", JSONObject.toJSONString(params));
}
}
------------------
打印结果如下:
152****7789
测试日志电话:152****7789
测试日志json对象:{"Phones":"136****0646,152****2222,156****3699","身份证":"150***********7885,150***********158X,150***********908X","timestamp":"1231","NAME":"张三"}
最后
- 虚心学习,共同进步 -_-