同步操作将从 czc/skill-list-demo 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
管理系统的日志相关的格式、日志文件、追踪id等功能。查看代码commit:第二章、日志管理
在sld-common下创建模块sld-common-log
<parent>
<groupId>cn.chenzecheng</groupId>
<artifactId>sld-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sld-common-log</artifactId>
logback-spring.xml
因为springboot
默认日志使用logback
框架,我们也就使用该框架。在resource下创建logback-spring.xm
l,设置格式和日志文件滚动配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_DIR" value="./logs"/>
<property name="LOG_MAX_FILE_SIZE" value="100MB"/>
<!--application.yml 传递参数,不使用logback 自带的<property>标签 -->
<springProperty scope="context" name="APP_ID" source="spring.application.name" default="default-application-name"/>
<springProperty scope="context" name="LOG_MAX_HISTORY" source="logback.MaxHistory" default="15"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %X{uri} | %X{requestId} | %-5level | ${PID} | ${APP_ID} | ${HOSTNAME}
| [%thread] | %logger{36}.%M\(%F:%L\) | %msg%n
</pattern>
<charset>UTF-8</charset>
</layout>
</appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/app.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %X{uri} | %X{requestId} | %-5level | ${PID} | ${APP_ID} | ${HOSTNAME}
| [%thread] | %logger{36}.%M\(%F:%L\) | %msg%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archived/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>${LOG_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_MAX_FILE_SIZE}</totalSizeCap>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile"/>
</root>
</configuration>
在服务的properties控制各个包的日志级别,配置服务名
server.port=8001
spring.application.name=sld-common-test
# logging.level.xxx(包名)设置对应的包的日志等级,root是所有
logging.level.root=info
logging.level.cn.chenzecheng.sld.common.test.controller.log=trace
在sld-common-test模块下cn.chenzecheng.sld.common.test.controller
包和cn.chenzecheng.sld.common.test.controller.log
包创建2个controller,分别调用接口进行日志等级测试
@GetMapping("/log")
public String testLog(){
log.error("test error");
log.warn("test warn");
log.info("test info");
log.debug("test debug");
log.trace("test trace");
return"test log";
}
实际开发工作中,我们把业务代码所在的包设为debug,其余设为info。生产环境则都是info,有问题排查时再改为debug或者trace。
uri
和requestId
的值上面的日志格式中,我们有2个参数uri
和requestId
,需要我们配置拦截器配合MDC进行设值。实际工作中用于查询指定请求的日志。
过滤器核心逻辑
@Slf4j
public class MdcFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse response1 = (HttpServletResponse) response;
try {
mdcRequestInfo(httpServletRequest, response1);
} catch (Exception e) {
log.error("doFilter error: ", e);
}
try {
chain.doFilter(request, response);
} finally {
MdcUtils.clear();
}
}
/**
* mdc记录请求跟踪信息
*/
private void mdcRequestInfo(HttpServletRequest httpServletRequest, HttpServletResponse response) {
MdcUtils.put(MdcConstants.LOCAL_IP_MDC_KEY, localIp);
String requestId = httpServletRequest.getHeader(MdcConstants.REQUEST_ID_HEADER);
if (Objects.isNull(requestId) || requestId.length() == 0) {
requestId = UUID.randomUUID().toString().replace("-", "");
}
//将requestId放入mdc
MdcUtils.put(MdcConstants.REQUEST_ID_MDC_KEY, requestId);
//将时间戳和uri放入mdc
MdcUtils.put(MdcConstants.TIMESTAMP, String.valueOf(System.currentTimeMillis()));
String pathUri = httpServletRequest.getRequestURI();
if (Objects.nonNull(pathUri) && requestId.length() > 0 && !pathUri.contains("jsessionid") && !pathUri.contains(";")) {
MdcUtils.put(MdcConstants.URI_MDC_KEY, pathUri);
}
}
}
注册拦截器
@Configuration
public class MdcFilterConfig {
@Bean
@Order(0)
public FilterRegistrationBean<MdcFilter> getMdcFilter() {
FilterRegistrationBean<MdcFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MdcFilter());
registration.addInitParameter(MdcConstants.MAPPED_HEADERS_KEY, Boolean.FALSE.toString());
registration.addInitParameter(MdcConstants.MAPPED_PARAMETERS, Boolean.FALSE.toString());
registration.addUrlPatterns("/*");
registration.setName("MdcFilter");
return registration;
}
}
不需要引入的项目配置 @ComponentScan 扫描上述的配置类的包,只要引入了该模块就自动启动
注意:SpringBoot3
用 spring.factories
的写法无法自动装配,改用 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
在resource/META-INF/spring
下创建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports
cn.chenzecheng.sld.common.log.mdc.MdcFilterConfig
Springboot使用MDC进行日志追踪_mdcutil_繁华尽头满是殇的博客-CSDN博客
核心思路是通过线程池的接口,将MDC上下进行线程间传递。
@NotNull
@Override
public Future<?> submit(@NotNull Runnable task){
return super.submit(MdcThreadUtil.wrap(task,MDC.getCopyOfContextMap()));
}
// MdcThreadUtil.wrap方法
public static Runnable wrap(final Runnable runnable,final Map<String, String> context){
return()->{
if(context==null){
MdcUtil.clear();
}else{
MdcUtil.setContextMap(context);
}
//设置requestId
setRequestIdIfAbsent();
try{
runnable.run();
}finally{
MdcUtil.clear();
}
};
}
先根据以上资料实现代码线程池的传递,测试。查看代码commit:第二章、日志管理
远程调用实现,在后续章节实现服务间调用之后再实现和测试。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。