Skip to content

Commit 10ca6dd

Browse files
juzi214032colorful3
authored andcommitted
feat(Logback): 增强 Logback 功能
1.将不同级别日志分文件夹输出 2.基于 ServletFliter 实现 Access Log
1 parent 7f6f802 commit 10ca6dd

6 files changed

Lines changed: 275 additions & 39 deletions

File tree

src/main/java/io/github/talelin/latticy/LatticyApplication.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
/**
1010
* @author pedro@TaleLin
1111
*/
12-
@SpringBootApplication(scanBasePackages = {"io.github.talelin.latticy"})
13-
@MapperScan(basePackages = {"io.github.talelin.latticy.mapper"})
1412
@RestController
13+
@MapperScan(basePackages = {"io.github.talelin.latticy.mapper"})
14+
@SpringBootApplication(scanBasePackages = {"io.github.talelin.latticy"})
1515
public class LatticyApplication {
1616

1717
public static void main(String[] args) {

src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
66
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
77
import io.github.talelin.autoconfigure.bean.PermissionMetaCollector;
8-
import io.github.talelin.latticy.module.file.FileProperties;
98
import io.github.talelin.latticy.common.interceptor.RequestLogInterceptor;
9+
import io.github.talelin.latticy.module.file.FileProperties;
10+
import io.github.talelin.latticy.module.log.MDCAccessServletFilter;
1011
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
1112
import org.springframework.boot.context.properties.EnableConfigurationProperties;
13+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
1214
import org.springframework.context.annotation.Bean;
1315
import org.springframework.context.annotation.Configuration;
1416

@@ -56,4 +58,18 @@ public Jackson2ObjectMapperBuilderCustomizer customJackson() {
5658
jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
5759
};
5860
}
61+
62+
/**
63+
* 用于将 request 相关信息(如请求 url)放入 MDC 中供日志使用
64+
*
65+
* @return Logback 的 MDCInsertingServletFilter
66+
*/
67+
@Bean
68+
public FilterRegistrationBean<MDCAccessServletFilter> mdcInsertingServletFilter() {
69+
FilterRegistrationBean<MDCAccessServletFilter> filterRegistrationBean = new FilterRegistrationBean<>();
70+
MDCAccessServletFilter mdcAccessServletFilter = new MDCAccessServletFilter();
71+
filterRegistrationBean.setFilter(mdcAccessServletFilter);
72+
filterRegistrationBean.setName("mdc-access-servlet-filter");
73+
return filterRegistrationBean;
74+
}
5975
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.github.talelin.latticy.module.log;
2+
3+
/**
4+
* @author Juzi@TaleLin
5+
* @date 2020/6/20 10:22
6+
*/
7+
public class MDCAccessConstant {
8+
public static final String REQUEST_METHOD_MDC_KEY = "req.method";
9+
public static final String RESPONSE_STATUS_MDC_KEY = "res.status";
10+
public static final String REQUEST_REFERER_MDC_KEY = "req.referer";
11+
public static final String REQUEST_PROTOCOL_MDC_KEY = "req.protocol";
12+
public static final String REQUEST_USER_AGENT_MDC_KEY = "req.userAgent";
13+
public static final String REQUEST_REMOTE_HOST_MDC_KEY = "req.remoteHost";
14+
public static final String REQUEST_REMOTE_ADDR_MDC_KEY = "req.remoteAddr";
15+
public static final String REQUEST_REQUEST_URI_MDC_KEY = "req.requestURI";
16+
public static final String REQUEST_REQUEST_URL_MDC_KEY = "req.requestURL";
17+
public static final String REQUEST_QUERY_STRING_MDC_KEY = "req.queryString";
18+
public static final String REQUEST_X_FORWARDED_FOR_MDC_KEY = "req.xForwardedFor";
19+
public static final String REQUEST_BODY_BYTES_SENT_MDC_KEY = "req.bodyBytesSent";
20+
public static final String REQUEST_REMOTE_PORT_MDC_KEY = "req.remotePort";
21+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.github.talelin.latticy.module.log;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.slf4j.MDC;
6+
7+
import javax.servlet.Filter;
8+
import javax.servlet.FilterChain;
9+
import javax.servlet.ServletException;
10+
import javax.servlet.ServletRequest;
11+
import javax.servlet.ServletResponse;
12+
import javax.servlet.http.HttpServletRequest;
13+
import javax.servlet.http.HttpServletResponse;
14+
import java.io.IOException;
15+
16+
/**
17+
* 记录 access log 的过滤器
18+
* 会把 request、response 相关信息(如 IP、URL)放入 Logback 的 MDC中
19+
*
20+
* @author Juzi@TaleLin
21+
* @date 2020/6/20 09:58
22+
*/
23+
public class MDCAccessServletFilter implements Filter {
24+
25+
private static final Logger log = LoggerFactory.getLogger(MDCAccessServletFilter.class);
26+
27+
@Override
28+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
29+
putRequestMDC(request);
30+
try {
31+
chain.doFilter(request, response);
32+
putResponseMDC(response);
33+
accessLog();
34+
} finally {
35+
clearMDC();
36+
}
37+
}
38+
39+
/**
40+
* 记录 access log
41+
*/
42+
public void accessLog() {
43+
log.info("");
44+
}
45+
46+
public void putResponseMDC(ServletResponse servletResponse) {
47+
if (servletResponse instanceof HttpServletResponse) {
48+
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
49+
MDC.put(MDCAccessConstant.RESPONSE_STATUS_MDC_KEY, String.valueOf(httpServletResponse.getStatus()));
50+
}
51+
}
52+
53+
/**
54+
* 放入 request 信息
55+
*/
56+
public void putRequestMDC(ServletRequest servletRequest) throws IOException {
57+
MDC.put(MDCAccessConstant.REQUEST_REMOTE_HOST_MDC_KEY, servletRequest.getRemoteHost());
58+
MDC.put(MDCAccessConstant.REQUEST_REMOTE_ADDR_MDC_KEY, servletRequest.getRemoteAddr());
59+
MDC.put(MDCAccessConstant.REQUEST_PROTOCOL_MDC_KEY, servletRequest.getProtocol());
60+
MDC.put(MDCAccessConstant.REQUEST_PROTOCOL_MDC_KEY, servletRequest.getProtocol());
61+
MDC.put(MDCAccessConstant.REQUEST_REMOTE_PORT_MDC_KEY, String.valueOf(servletRequest.getRemotePort()));
62+
MDC.put(MDCAccessConstant.REQUEST_BODY_BYTES_SENT_MDC_KEY, String.valueOf(servletRequest.getContentLength()));
63+
64+
if (servletRequest instanceof HttpServletRequest) {
65+
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
66+
MDC.put(MDCAccessConstant.REQUEST_REQUEST_URI_MDC_KEY, httpServletRequest.getRequestURI());
67+
StringBuffer requestUrl = httpServletRequest.getRequestURL();
68+
if (requestUrl != null) {
69+
MDC.put(MDCAccessConstant.REQUEST_REQUEST_URL_MDC_KEY, requestUrl.toString());
70+
}
71+
MDC.put(MDCAccessConstant.REQUEST_METHOD_MDC_KEY, httpServletRequest.getMethod());
72+
MDC.put(MDCAccessConstant.REQUEST_QUERY_STRING_MDC_KEY, httpServletRequest.getQueryString());
73+
MDC.put(MDCAccessConstant.REQUEST_USER_AGENT_MDC_KEY, httpServletRequest.getHeader("User-Agent"));
74+
MDC.put(MDCAccessConstant.REQUEST_X_FORWARDED_FOR_MDC_KEY, httpServletRequest.getHeader("X-Forwarded-For"));
75+
MDC.put(MDCAccessConstant.REQUEST_REFERER_MDC_KEY, httpServletRequest.getHeader("referer"));
76+
}
77+
}
78+
79+
/**
80+
* 清理 MDC,特别重要
81+
* 因为 MDC 由 ThreadLocal 实现,如果不清理,线程池的复用机制会导致 MDC 数据受到污染
82+
*/
83+
public void clearMDC() {
84+
MDC.remove(MDCAccessConstant.REQUEST_METHOD_MDC_KEY);
85+
MDC.remove(MDCAccessConstant.RESPONSE_STATUS_MDC_KEY);
86+
MDC.remove(MDCAccessConstant.REQUEST_REFERER_MDC_KEY);
87+
MDC.remove(MDCAccessConstant.REQUEST_PROTOCOL_MDC_KEY);
88+
MDC.remove(MDCAccessConstant.REQUEST_USER_AGENT_MDC_KEY);
89+
MDC.remove(MDCAccessConstant.REQUEST_REMOTE_HOST_MDC_KEY);
90+
MDC.remove(MDCAccessConstant.REQUEST_REMOTE_ADDR_MDC_KEY);
91+
MDC.remove(MDCAccessConstant.REQUEST_REQUEST_URI_MDC_KEY);
92+
MDC.remove(MDCAccessConstant.REQUEST_REQUEST_URL_MDC_KEY);
93+
MDC.remove(MDCAccessConstant.REQUEST_QUERY_STRING_MDC_KEY);
94+
MDC.remove(MDCAccessConstant.REQUEST_X_FORWARDED_FOR_MDC_KEY);
95+
MDC.remove(MDCAccessConstant.REQUEST_BODY_BYTES_SENT_MDC_KEY);
96+
MDC.remove(MDCAccessConstant.REQUEST_REMOTE_PORT_MDC_KEY);
97+
}
98+
99+
}

src/main/resources/application.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ mybatis-plus:
3131
mapper-locations: classpath:mapper/*.xml
3232

3333

34-
logging:
35-
# 日志配置文件(logback配置文件)
36-
config: classpath:logback-spring.xml
37-
38-
3934
lin:
4035
cms:
4136
# 开启行为日志记录(logger)
@@ -46,3 +41,7 @@ lin:
4641
token-refresh-expire: 2592000
4742
# 令牌 secret
4843
token-secret: x88Wf0991079889x8796a0Ac68f9ecJJU17c5Vbe8beod7d8d3e695*4
44+
logging:
45+
file:
46+
max-history:
47+
config:

src/main/resources/logback-spring.xml

Lines changed: 132 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,162 @@
1-
<?xml version="1.0" encoding="UTF-8"?>
2-
3-
<configuration scan="true" scanPeriod="10 seconds">
4-
<contextName>logback</contextName>
5-
6-
<property name="log.path" value="logs/"/>
7-
1+
<configuration>
2+
<!--给控制台输出增加颜色-->
83
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
9-
104
<conversionRule conversionWord="wex"
115
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
12-
136
<conversionRule conversionWord="wEx"
147
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
15-
8+
169
<property name="CONSOLE_LOG_PATTERN"
1710
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
18-
11+
12+
<!--输出到控制台-->
1913
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
20-
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
21-
<level>debug</level>
22-
</filter>
2314
<encoder>
2415
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
2516
<charset>UTF-8</charset>
2617
</encoder>
2718
</appender>
28-
29-
<timestamp key="namedByDay" datePattern="yyyy-MM-dd"/>
30-
31-
<appender name="FILE" class="io.github.talelin.core.logger.AdvanceRollingFileAppender">
32-
<dir>${log.path}</dir>
33-
<maxFileSize>5MB</maxFileSize>
34-
19+
20+
<!--输出到 access 文件夹-->
21+
<appender name="FILE_ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
22+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
23+
<fileNamePattern>logs/access/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
24+
<maxFileSize>5MB</maxFileSize>
25+
</rollingPolicy>
3526
<encoder>
36-
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
3727
<charset>UTF-8</charset>
28+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{req.remoteAddr}:%X{req.remotePort}] "%X{req.method}
29+
%X{req.requestURI} %X{req.protocol}" %X{res.status} %X{req.bodyBytesSent} "%X{req.referer}"
30+
"%X{req.userAgent}"%n
31+
</pattern>
3832
</encoder>
39-
40-
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
41-
<level>debug</level>
33+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
34+
<level>INFO</level>
35+
<onMatch>ACCEPT</onMatch>
36+
<onMismatch>DENY</onMismatch>
4237
</filter>
4338
</appender>
44-
39+
40+
<!--输出到 error 文件夹-->
41+
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
42+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
43+
<fileNamePattern>logs/error/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
44+
<maxFileSize>5MB</maxFileSize>
45+
</rollingPolicy>
46+
<encoder>
47+
<charset>UTF-8</charset>
48+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] "%X{req.method} %X{req.requestURI} %X{req.protocol}" %-5level
49+
%logger{50} - %msg%n
50+
</pattern>
51+
</encoder>
52+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
53+
<level>ERROR</level>
54+
<onMatch>ACCEPT</onMatch>
55+
<onMismatch>DENY</onMismatch>
56+
</filter>
57+
</appender>
58+
59+
<!--输出到 warn 文件夹-->
60+
<appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
61+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
62+
<fileNamePattern>logs/warn/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
63+
<maxFileSize>5MB</maxFileSize>
64+
</rollingPolicy>
65+
<encoder>
66+
<charset>UTF-8</charset>
67+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] "%X{req.method} %X{req.requestURI} %X{req.protocol}" %-5level
68+
%logger{50} - %msg%n
69+
</pattern>
70+
</encoder>
71+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
72+
<level>WARN</level>
73+
<onMatch>ACCEPT</onMatch>
74+
<onMismatch>DENY</onMismatch>
75+
</filter>
76+
</appender>
77+
78+
<!--输出到 info 文件夹-->
79+
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
80+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
81+
<fileNamePattern>logs/info/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
82+
<maxFileSize>5MB</maxFileSize>
83+
</rollingPolicy>
84+
<encoder>
85+
<charset>UTF-8</charset>
86+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] "%X{req.method} %X{req.requestURI} %X{req.protocol}" %-5level
87+
%logger{50} - %msg%n
88+
</pattern>
89+
</encoder>
90+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
91+
<level>INFO</level>
92+
<onMatch>ACCEPT</onMatch>
93+
<onMismatch>DENY</onMismatch>
94+
</filter>
95+
</appender>
96+
97+
<!--输出到 debug 文件夹-->
98+
<appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
99+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
100+
<fileNamePattern>logs/debug/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
101+
<maxFileSize>5MB</maxFileSize>
102+
</rollingPolicy>
103+
<encoder>
104+
<charset>UTF-8</charset>
105+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] "%X{req.method} %X{req.requestURI} %X{req.protocol}" %-5level
106+
%logger{50} - %msg%n
107+
</pattern>
108+
</encoder>
109+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
110+
<level>DEBUG</level>
111+
<onMatch>ACCEPT</onMatch>
112+
<onMismatch>DENY</onMismatch>
113+
</filter>
114+
</appender>
115+
116+
<!--输出到 trace 文件夹-->
117+
<appender name="FILE_TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
118+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
119+
<fileNamePattern>logs/trace/%d{yyyy-MM,aux}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
120+
<maxFileSize>5MB</maxFileSize>
121+
</rollingPolicy>
122+
<encoder>
123+
<charset>UTF-8</charset>
124+
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] "%X{req.method} %X{req.requestURI} %X{req.protocol}" %-5level
125+
%logger{50} - %msg%n
126+
</pattern>
127+
</encoder>
128+
<filter class="ch.qos.logback.classic.filter.LevelFilter">
129+
<level>TRACE</level>
130+
<onMatch>ACCEPT</onMatch>
131+
<onMismatch>DENY</onMismatch>
132+
</filter>
133+
</appender>
134+
135+
<logger name="io.github.talelin.latticy.module.log.MDCAccessServletFilter" level="INFO" additivit="false">
136+
<appender-ref ref="FILE_ACCESS"/>
137+
</logger>
138+
45139
<springProfile name="dev">
46140
<root level="INFO">
47141
<appender-ref ref="CONSOLE"/>
48-
<appender-ref ref="FILE"/>
142+
<appender-ref ref="FILE_ERROR"/>
143+
<appender-ref ref="FILE_WARN"/>
144+
<appender-ref ref="FILE_INFO"/>
145+
<appender-ref ref="FILE_DEBUG"/>
146+
<appender-ref ref="FILE_TRACE"/>
49147
</root>
50148
</springProfile>
51-
149+
52150
<springProfile name="prod">
53151
<root level="INFO">
54-
<!--<appender-ref ref="CONSOLE"/>-->
55-
<appender-ref ref="FILE"/>
152+
<appender-ref ref="FILE_ERROR"/>
153+
<appender-ref ref="FILE_WARN"/>
154+
<appender-ref ref="FILE_INFO"/>
155+
<appender-ref ref="FILE_DEBUG"/>
156+
<appender-ref ref="FILE_TRACE"/>
56157
</root>
57158
</springProfile>
58-
159+
59160
<springProfile name="test">
60161
<root level="INFO">
61162
<appender-ref ref="CONSOLE"/>

0 commit comments

Comments
 (0)