spring boot log4j2 最佳实践
为什么选择 log4j2
Log4j2 使用了 LMAX Disruptor 库。在多线程场景中,异步 Logger 的吞吐量比 Log4j 1.x 和 Logback 高 18 倍,延迟低几个数量级。如下是官网的性能对比:
上图来源:https://logging.apache.org/log4j/2.x/performance.html
最终效果
- 日志输出整齐有序;
- 不同级别日志采用不同颜色输出,更加清晰直观;
- 日志输出可带有用户标识或特定标识,有利于查看同组的多个请求轨迹、排查问题;
- 不同环境采用不同配置,本地测试日志只输出到控制台,其他环境日志输出到指定文件;
- 线上日志按天分割,历史日志按月压缩存放,定期删除之前日志,便于排查问题,防止日志累积达到磁盘上限;
- 警告错误日志再单独输出到错误日志文件中,便于快速定位问题;
日志效果图
代码示例
maven 依赖
点击查看代码
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
注入标识过滤器
点击查看代码
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UidLogFilter extends OncePerRequestFilter {
private static final String UID = "Uid";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String uid = request.getHeader(UID);
if (!StringUtils.hasLength(uid)){
uid = request.getParameter(UID);
}
/**
* 将用户标识放入日志上下文中
*/
MDC.put("uid", uid);
filterChain.doFilter(request, response);
}
}
BestPracticeApplication
点击查看代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@SpringBootApplication
public class BestPracticeApplication {
/**
* 根据请求参数输出不同级别的日志
*/
@GetMapping("/")
public String home(String level) {
String msg = "Hello World!";
try {
switch (level){
case "trace": log.trace(msg); break;
case "debug": log.debug(msg); break;
case "info": log.info(msg); break;
case "warn": log.warn(msg); break;
case "error": log.error(msg); break;
//带参数的日志输出方式
default: log.error("未知的日志级别: {}", level); break;
}
}catch (NullPointerException e){
//异常日志输出方式
log.error("日志级别为空", e);
}
return msg;
}
@Bean
public UidLogFilter uidLogFilter(){
return new UidLogFilter();
}
static {
System.setProperty("log4j.skipJansi", "false");
}
public static void main(String[] args) {
SpringApplication.run(BestPracticeApplication.class, args);
}
}
application.yml
点击查看代码
spring:
profiles:
active: local
---
#本地环境
spring:
config:
name: local
logging:
config: classpath:log4j2-local.xml
---
#测试环境
spring:
config:
name: test
---
#线上环境
spring:
config:
name: online
log4j2-local.xml
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
<Properties>
<Property name="LOG_PATTERN_LAYOUT">%d{HH:mm:ss,SSS} %highlight{[%-5.5level]}{STYLE=Logback} [%-5.5thread] %blue{[%-5.5X{uid}]} %cyan{[%-40.40c{1.}:%-4line]} - %msg%xEx%n</Property>
<Property name="DEFAULT_CHARSET">UTF-8</Property>
</Properties>
<Appenders>
<!-- 定义控制台输出 -->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout charset="${DEFAULT_CHARSET}" pattern="${LOG_PATTERN_LAYOUT}"/>
</Console>
</Appenders>
<Loggers>
<!-- 包名以cn.learncoding 开头的日志输出级别为TRACE -->
<Logger name="cn.learncoding" level="TRACE"/>
<!-- 默认日志输出级别为INFO -->
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
log4j2.xml
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
<Properties>
<!-- ${sys:catalina.home:-.}/logs 表示 取系统变量catalina.home指向的目录下面的logs目录,如果没有则取当前所在目录下面的logs目录 -->
<Property name="LOG_HOME">${sys:catalina.home:-.}/logs</Property>
<Property name="LOG_BACK_HOME">${sys:catalina.home:-.}/logs/backup</Property>
<!-- 日志输出格式 -->
<!-- %-5.5thread 表示最少长度为5,不足空格补齐,超出5则只保留后5位 -->
<!-- %highlight{[%-5.5level]}{STYLE=Logback} 表示此字段使用Logback格式的高亮颜色展示,最少长度为5,不足空格补齐,超出5则只保留后5位 -->
<!-- %blue{[%-5.5X{uid}]} 表示此上下文字段采用蓝色进行输出,最少长度为5,不足空格补齐,超出5则只保留后5位 -->
<Property name="LOG_PATTERN_LAYOUT">%d{HH:mm:ss,SSS} %highlight{[%-5.5level]}{STYLE=Logback} [%-5.5thread] %blue{[%-5.5X{uid}]} %cyan{[%-40.40c{1.}:%-4line]} - %msg%xEx%n </Property>
<Property name="DEFAULT_CHARSET">UTF-8</Property>
<Property name="ERROR_FILE_NAME">error</Property>
<Property name="INFO_FILE_NAME">info</Property>
</Properties>
<Appenders>
<!-- 配置日常日志 历史日志按月存放,按天分割压缩存储-->
<RollingFile name="${INFO_FILE_NAME}" fileName="${LOG_HOME}/${INFO_FILE_NAME}.log" filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${INFO_FILE_NAME}-%d{yyyy-MM-dd}.log.gz" append="true">
<PatternLayout charset="${DEFAULT_CHARSET}" pattern="${LOG_PATTERN_LAYOUT}"/>
<Policies>
<!-- 基于时间的滚动策略,按天分割 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<DefaultRolloverStrategy >
<!--删除30天前的日志-->
<Delete basePath="${LOG_BACK_HOME}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="30d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<!-- 配置错误日志 历史日志按月存放,按天分割压缩存储-->
<RollingFile name="${ERROR_FILE_NAME}" fileName="${LOG_HOME}/${ERROR_FILE_NAME}.log" filePattern="${LOG_BACK_HOME}/$${date:yyyy-MM}/${ERROR_FILE_NAME}-%d{yyyy-MM-dd}.log.gz" append="true">
<PatternLayout charset="${DEFAULT_CHARSET}" pattern="${LOG_PATTERN_LAYOUT}"/>
<Policies>
<!-- 基于时间的滚动策略,按天分割 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<Filters>
<!--级别大于等于WARN的日志可以写入-->
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingFile>
</Appenders>
<Loggers>
<!-- 默认日志输出级别为INFO -->
<Root level="INFO">
<AppenderRef ref="${INFO_FILE_NAME}"/>
<AppenderRef ref="${ERROR_FILE_NAME}"/>
</Root>
</Loggers>
</Configuration>
spring boot log4j2 最佳实践的更多相关文章
- Spring Boot Admin最佳实践
本文不进行Spring Boot Admin入门知识点说明 在Spring Boot Actuator中提供很多像health.metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标.Spr ...
- 开涛spring3(7.5) - 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践
7.5 集成Spring JDBC及最佳实践 大多数情况下Spring JDBC都是与IOC容器一起使用.通过配置方式使用Spring JDBC. 而且大部分时间都是使用JdbcTemplate类(或 ...
- 转:spring boot log4j2配置(使用log4j2.yml文件)---YAML 语言教程
转:spring boot log4j2配置(使用log4j2.yml文件) - CSDN博客http://blog.csdn.net/ClementAD/article/details/514988 ...
- Spring Boot 2 (五):Docker Compose + Spring Boot + Nginx + Mysql 实践
Spring Boot 2 (五):Docker Compose + Spring Boot + Nginx + Mysql 实践 Spring Boot + Nginx + Mysql 是实际工作中 ...
- Spring Boot统一异常处理实践
摘要: SpringBoot异常处理. 原文:Spring MVC/Boot 统一异常处理最佳实践 作者:赵俊 前言 在 Web 开发中, 我们经常会需要处理各种异常, 这是一件棘手的事情, 对于很多 ...
- SpringBoot系列: Spring项目异常处理最佳实践
===================================自定义异常类===================================稍具规模的项目, 一般都要自定义一组异常类, 这 ...
- Spring Boot Log4j2 日志学习
简介 Java 中比较常用的日志工具类,有: Log4j. SLF4j. Commons-logging(简称jcl). Logback. Log4j2(Log4j 升级版). Jdk Logging ...
- Spring Boot缓存应用实践
缓存是最直接有效提升系统性能的手段之一.个人认为用好用对缓存是优秀程序员的必备基本素质. 本文结合实际开发经验,从简单概念原理和代码入手,一步一步搭建一个简单的二级缓存系统. 一.通用缓存接口 1.缓 ...
- spring boot log4j2与三方依赖库log4j冲突无法初始化问题解决方法
因为从Spring Boot 1.4开始,spring boot就不支持log4j了,必须是log4j2或者logback,具体两者如何配置以及NDC的支持可以参考spring boot精华版. 这里 ...
随机推荐
- 新东方集团K12公益免费课战役记
作者:张建鑫, 曾任IBM高级软件架构师, 滴滴高级技术专家, 现任新东方集团高级技术总监 1月31日,集团领导决定由产品技术中心的新东方APP团队牵头做周一到周五的集团公益课, 提供给全国中小学生使 ...
- Python中管理数据库
前言:Python中是利用MySQL模块和数据库之间建立联系. MySQLdb 是用于Python链接Mysql数据库的接口,它实现了 Python 数据库 API 规范 V2.0,基于 MySQL ...
- openresty lua-resty-redis 封装 a wrapper for lua-resty-redis 优雅一点点
搜了一下别人的封装代码,感觉不够优雅,主要是 set_keepalive 的调用时机不太好 我自己下面的代码是利用 coroutine, 每次当前 phase 结束后自动调用 set_keepaliv ...
- vue 根据身份证计算出出生日期和判断性别
//获取生日和性别 getBirth(idCard) { var birthday = ""; if(idCard != null & ...
- 再见了,我的散装研发管理平台;再见了,4台ECS!
周末的时候,收到好几个云服务器临近过期的通知短信,准备续个费,居然都要大几千!因为这几个都是以前低价抢购的,掐指一算,如果都续费的话,要蚕食好多利润!作为一名自己养活自己的独立开发者,节省成本是必备技 ...
- shell循环语句while
格式1: while 条件 do 执行命令 done 格式2: while 条件;do 命令 done 例子: while [ 1 -eq 1 ];do echo "这一步需要先修改/dat ...
- AntDesign VUE:上传组件图片/视频宽高、文件大小、image/video/pdf文件类型等限制(Promise、Boolean)
文件大小限制 - Promise checkFileSize(file, rules) { return new Promise((resolve, reject) => { file.size ...
- jquery .play()报错is not a function
报错原因:play()方法属于DOM对象方法,$('#audio')为jquery对象解决办法:将jquery对象转换为DOM对象首先打印jquery对象$('#audio') 两种转换方式将一个jQ ...
- C# 将PPT转为OFD/DPT/DPS/ODP/POTX/UOP
本文分享在C#代码程序中,如何将PPT幻灯片文档转换为多种文件格式,如:OFD.DPT.DPS.ODP.POTX.UOP等.只需在加载PPT幻灯片源文档后,调用ppt.SaveToFile(strin ...
- Linux系列(4) - 目录处理命令(1)
前言 linux中一切皆文件.目录为目录文件,普通文件用来保存数据,目录文件用来保存文件 建立目录:mkdir mkdir -p [目录名] -p 递归创建目录,例子:mkdir -p LinuxTe ...