# Request Log
[INFO ] 2023/09/12 15:19:00.956 [127.0.0.1 772f27b1-d5d9-4453-8bbb-daa8f54df0cd] http-nio-10108-exec-1 [l.m.i.LoggingInterceptor.preHandle:127] REQ > [POST /hello],
headers={"Accept":"*/*", "User-Agent":"PostmanRuntime/7.26.10", "Connection":"keep-alive", "Postman-Token":"88ed20f8-cb8a-4a17-b4b0-6acb813abd39", "Host":"localhost:10009", "Accept-Encoding":"gzip, deflate, br", "Content-Length":"46", "Content-Type":"application/json"},
params={},
body={"name":"์น๋ฆฌ๋ฅผ","name2":"tt"}
# Response Log
[INFO ] 2023/09/12 15:22:06.643 [127.0.0.1 6b7da681-6fee-4de8-9e83-6c42d790ba9a] http-nio-10108-exec-4 [l.m.i.LoggingInterceptor.postHandle:209] RES > 201 [POST /hello] 6ms,
headers={"Accept":"*/*", "User-Agent":"PostmanRuntime/7.26.10", "Connection":"keep-alive", "Postman-Token":"88ed20f8-cb8a-4a17-b4b0-6acb813abd39", "Host":"localhost:10009", "Accept-Encoding":"gzip, deflate, br", "Content-Length":"46", "Content-Type":"application/json"},
payload={"name":"์น๋ฆฌ๋ฅผ"}
# Checked Exception Error Log
[ERROR] 2023/09/12 15:20:47.440 [127.0.0.1 1f125f13-ba47-42e7-90d3-7cb388a4dfcf] http-nio-10108-exec-3 [l.m.i.ErrorAspect.recordErrorLog:71] ERR > httpStatus=400, errorCode="003", errorType="org.springframework.web.bind.MethodArgumentNotValidException", message="[issuedDate] ๋์ด์ด์๋ ์๋ฉ๋๋ค",
stackTrace="Validation failed for argument [1] in public java.lang.String ..."
# Unchecked Exception Error Log
[ERROR] 2023/09/12 15:29:38.290 [127.0.0.1 faaa0aaa-2914-4202-8ce3-329f3cf7ddae] http-nio-10108-exec-4 [l.m.i.ErrorAspect.recordErrorLog:71] ERR > httpStatus=500, errorCode="", errorType="java.lang.NullPointerException", message="Cannot invoke \"net.test.api.module.dto.request.ReqDto.getNumber()\" because \"reqDto\" is null",
stackTrace="Cannot invoke ..."
ext {
version_log4j= '2.17.0'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
all {
// log4j2๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด, spring์ default์ธ logback์ ์ ์ธ
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
}
repositories {
mavenCentral()
}
dependencies {
implementation group:'log.munzi', name:'munzi-log', version:'0.1.12'
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${version_log4j}"
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${version_log4j}"
implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: "${version_log4j}"
implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: "${version_log4j}"
implementation group: 'org.apache.logging.log4j', name: 'log4j-web', version: "${version_log4j}"
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2'
implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: "${version_jackson}"
implementation group: 'org.bgee.log4jdbc-log4j2', name: 'log4jdbc-log4j2-jdbc4.1', version: '1.16'
implementation group: 'org.json', name: 'json', version: '20230618'
}
@EnableConfigurationProperties(ApiLogProperties.class) // ์ด๋ถ๋ถ ์ถ๊ฐ!
public class ApiApplication {
public static void main(String[] args) {
...
}
}
<LoggingConfig.java>
import com.fasterxml.jackson.databind.ObjectMapper;
import log.munzi.common.util.LoggingUtil;
import log.munzi.config.ApiLogProperties;
import log.munzi.error.ErrorAspect;
import log.munzi.interceptor.GlobalRequestWrappingFilter;
import log.munzi.interceptor.LoggingInterceptor;
import log.munzi.stacktrace.error.StackTraceErrorWriter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
@Slf4j
public class LoggingConfig {
private final ObjectMapper objectMapper;
private final ApiLogProperties apiLogProperties;
@Value("${spring.profiles.active}")
private String profile;
@Bean
public LoggingInterceptor loggingInterceptor() {
return new LoggingInterceptor(objectMapper, apiLogProperties);
}
@Bean
public StackTraceErrorWriter stackTraceErrorWriter() {
return new StackTraceErrorWriter();
}
@Bean
public GlobalRequestWrappingFilter globalRequestWrappingFilter() {
return new GlobalRequestWrappingFilter(apiLogProperties, profile);
}
@Bean
public ErrorAspect errorAspect() {
return new ErrorAspect(apiLogProperties, stackTraceErrorWriter());
}
@Bean
public LoggingUtil loggingUtil() {
return new LoggingUtil(loggingInterceptor(), apiLogProperties, profile, stackTraceErrorWriter());
}
}
GlobalRequestWrappingFilter๋ ๊ธฐ๋ณธ์ ์ผ๋ก Filter ์ฐ์ ์์ ์ ์ผ ์๋๋ก ๋ฑ๋ก์ด ๋๋ค. ํ์ง๋ง security ์ค์ ์ด ๋ค์ด๊ฐ๋ฉด Filter Chain์ ์ ๋๋ก ๋ค์ด๊ฐ์ง ์๋ ์ค๋ฅ(?)๊ฐ ์์ด์ SecurityConfig ์ค์ ์ ํด์ค ๋, addFilter๋ฅผ ํด์ค์ผ ํ๋ค.
<SecurityConfig.java (Security Configuration Class)>
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
...
http.addFilterBefore(munziLoginFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(globalRequestWrappingFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
๋ค์๊ณผ ๊ฐ์ด addFilter๋ฅผ ํด์ค๋ค. ๊ผญ UserNamePasswordAuthenticationFiler ํ์์ ํด์ค ํ์๋ ์๋ค. SpringSecurityFilter๋ค๋ณด๋ค๋ง ํ์์๋ก ๋ค์ด๊ฐ๊ฒ ์ค์ ํด์ฃผ๋ฉด ๋๋ค.
<WebMvcConfig.java>
import log.munzi.interceptor.LoggingInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final LoggingInterceptor loggingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/vendor/**", "/css/*", "/img/*");
}
}
5. globalRequestWrappingFilter ์ ์ ๋ค๋ฅธ filter ๋ฑ์ ๊ฑธ๋ ค์ Log๊ฐ ์์ฐํ์ ์ง์ ์ฐ์ด์ค์ผ ํ ๋
ex) Spring security filter์์ ์๋ฌ๊ฐ ๋์ globalRequestWrappingFilter์ interceptor๋ฅผ ๊ฑฐ์น์ง ๋ชปํด request, error ๋ก๊ทธ๋ฅผ ์ฐ์ง ๋ชปํ๋ ์ํฉ.
AuthenticationFailureHandler์ onAuthenticationFailure ์์ request ๊ฐ์ผ๋ก loggingUtil.recordRequestLog ๋ฅผ ์ฌ์ฉํด request log๋ฅผ ์ฐ๊ณ exception(AuthenticationException)๊ณผ return ๊ฐ(ProblemDetail)์ผ๋ก loggingUtil.recordErrorLog ๋ฅผ ์ฌ์ฉํด error log๋ฅผ ์ฐ๋๋ค.
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import log.munzi.common.util.LoggingUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.UnknownHostException;
/**
* ์ธ์ฆ ์คํจ handler
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class AuthenticationFailureCustomHandler implements AuthenticationFailureHandler {
private final ObjectMapper objectMapper;
private final LoggingUtil loggingUtil;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
String requestId = null;
try {
requestId = loggingUtil.recordRequestLog(request, true);
} catch (Exception e) {
log.error("recordRequestLog error", e);
}
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, exception.getMessage());
response.getWriter()
.write(objectMapper.writeValueAsString(problemDetail));
try {
loggingUtil.recordErrorLog(exception, problemDetail, requestId);
} catch (UnknownHostException | JSONException e) {
log.error("recordErrorLog error", e);
}
}
}
<application.yml>
spring:
output:
ansi:
enabled: ALWAYS # ๋ก๊ทธ ์๋ก๋ฌ๋ก ์์๊ฒ ๋์ค๊ฒ ์ค์
datasource: # log4jdbc-log4j2 ์ฌ์ฉ์ ํ์ํ ์ค์
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy # ๊ณ ์
url: jdbc:log4jdbc:mariadb~~~ # ๊ธฐ์กด jdbc:mariadb~~ ์ด๋ฐ์์ผ๋ก ์ผ๋ ๋ถ๋ถ ์ฌ์ด์ log4jdbc ์ถ๊ฐ
logging:
config: file:/apps/dkargo/munzi-log/config/log4j2-local.yml #log4j2.yml ํ์ผ์ ๊ฒฝ๋ก
# config: classpath:log4j2-local.yml # resources ํ์์ ๊ฒฝ์ฐ
api-log:
server-name: munzi-nene-project
request-id-header-key: X-Request-ID # requestId๋ฅผ ์ํ๋ ๊ฐ์ผ๋ก ์ฐ๊ณ ์ถ์ ๊ฒฝ์ฐ, header์ ๋ด์์ ์ฐ์ ์ ์๋๋ฐ ๊ทธ ๋ header์์ ์ฌ์ฉํ key
stack-trace-print-yn: true # default = false, true์ผ ๊ฒฝ์ฐ 500๋ฒ๋ ์๋ฌ๊ฐ ๋ฌ์ ๋ StackTrace๋ ๊ฐ์ด ์ฐ์
ignore-security-log: true # default = false, true์ผ ๊ฒฝ์ฐ์๋ง security์ฌ๋ ๋ก๊ทธ ์ฐ์
use: true # request, response ๋ก๊ทธ๋ฅผ ์ฐ๋์ง ์ฌ๋ถ
json-pretty: false # request, response ๋ก๊ทธ ๋ด json ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฌํด์ ๋ณด์ฌ์ค์ง ์ฌ๋ถ
debug-api: GET /api/debug/*
request:
max-body-size: 1 MB # request body max size
secret-api: POST /api/sjsj # ํด๋น api์ ๊ฒฝ์ฐ, body ์ ์ฒด๋ฅผ ๋ก๊ทธ์ ์์ฐ์
inactive-api: GET /api/webjars/*, GET /api/, GET /api/swagger*, GET /api/code/*, OPTIONS /api/code/*
response:
max-body-size: 10 KB # response body max size
secret-api:
inactive-api: GET /api/webjars/*, GET /api/, GET /api/swagger*, GET /api/code/*, OPTIONS /api/code/*
<log4j2.yml>
Configuration:
name: log4j2 local
status: INFO
monitorInterval: 5 # 1
Properties: # 2
Property:
- name: package-name
value: "xxx.xxx.xxx"
- name: log-path
value: "/apps/logs/munzi-log"
- name: log-filename
value: "munzi-log.log"
- name: req-res-log-filename
value: "munzi-log-req-res.log"
- name: err-stack-trace-log-filename
value: "munzi-log-err-stack-trace.log"
- name: event-log-filename
value: "munzi-log-event.log"
- name: scheduler-log-filename
value: "munzi-log-sch.log"
- name: log-db-filename
value: "munzi-db-log.log"
- name: log-pattern
value: "%highlight{[%-5p]}{FATAL=bg_red, ERROR=red, INFO=green, DEBUG=blue} %style{%d{yyyy/MM/dd HH:mm:ss.SSS}}{cyan} %style{[%X{applicationName} %X{requestId}]}{magenta} %style{%t}{yellow} %style{[%C{1.}.%M:%L]}{blue} %m%n"
- name: log-pattern-no-color
value: "[%-5p] %d{yyyy/MM/dd HH:mm:ss.SSS} [%X{applicationName} %X{requestId}] %t [%C{1.}.%M:%L] %m%n"
Appenders: # 3
Console:
name: Console_Appender
target: SYSTEM_OUT
PatternLayout:
pattern: ${log-pattern}
RollingFile:
- name: RollingFile_Appender
fileName: ${log-path}/${log-filename}
filePattern: ${log-path}/archive/${log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Debug_RollingFile_Appender
fileName: ${log-path}/${debug-log-filename}
filePattern: ${log-path}/archive/${debug-log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: DEBUG
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Info_RollingFile_Appender
fileName: ${log-path}/${log-filename}
filePattern: ${log-path}/archive/${log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: INFO
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: RollingFile_Appender_Color
fileName: ${log-path}/${log-filename}
filePattern: ${log-path}/archive/${log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern}
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: RollingDBFile_Appender
fileName: ${log-path}/${log-db-filename}
filePattern: ${log-path}/archive/${log-db-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern}
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Info_Req_Res_RollingFile_Appender
fileName: ${log-path}/${req-res-log-filename}
filePattern: ${log-path}/archive/${req-res-log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: INFO
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Err_StackTrace_RollingFile_Appender
fileName: ${log-path}/${err-stack-trace-log-filename}
filePattern: ${log-path}/archive/${err-stack-trace-log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: ERROR
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Info_Event_RollingFile_Appender
fileName: ${log-path}/${event-log-filename}
filePattern: ${log-path}/archive/${event-log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: INFO
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
- name: Info_Scheduler_RollingFile_Appender
fileName: ${log-path}/${scheduler-log-filename}
filePattern: ${log-path}/archive/${scheduler-log-filename}.%d{yyyy-MM-dd-hh-mm}.gz
PatternLayout:
pattern: ${log-pattern-no-color}
LevelRangeFilter:
minLevel: FATAL
maxLevel: INFO
onMatch: ACCEPT
onMismatch: DENY
Policies:
SizeBasedTriggeringPolicy:
size: 500 MB
DefaultRollOverStrategy:
max: 30
Delete:
basePath: ${log-path}/archive
maxDepth: 1
IfAccumulatedFileCount:
exceeds: 31
Loggers: # 4
Root: # 5
includeLocation: TRUE
level: INFO
AppenderRef:
- ref: Console_Appender
AsyncLogger: # 6
# package
- name: ${package-name}
includeLocation: TRUE
additivity: FALSE
level: DEBUG
AppenderRef:
- ref: Console_Appender
- ref: RollingFile_Appender
- name: org.springframework.web
includeLocation: TRUE
additivity: FALSE
level: ERROR
AppenderRef:
- ref: Console_Appender
- ref: RollingFile_Appender
- name: org.hibernate
includeLocation: TRUE
additivity: FALSE
level: ERROR
AppenderRef:
- ref: RollingDBFile_Appender
- name: log4jdbc.log4j2 # 7
includeLocation: TRUE
additivity: FALSE
level: DEBUG
AppenderRef:
- ref: Console_Appender
- ref: RollingFile_Appender
- ref: RollingDBFile_Appender
MarkerFilter:
# - marker: LOG4JDBC_JDBC # jdbc
# onMatch: DENY
# onMismatch: NEUTRAL
# - marker: LOG4JDBC_CONNECTION # jdbc.connection
# onMatch: DENY
# onMismatch: DENY
# - marker: LOG4JDBC_NON_STATEMENT # jdbc.sqltiming
# onMatch: ACCEPT
# onMismatch: DENY
# - marker: LOG4JDBC_SQL
# onMatch: DENY
# onMismatch: DENY
# - marker: LOG4JDBC_AUDIT # jdbc.audit
# onMatch: DENY
# onMismatch: DENY
# - marker: LOG4JDBC_RESULTSET # jdbc.resultset
# onMatch: DENY
# onMismatch: DENY
- marker: LOG4JDBC_RESULTSETTABLE # jdbc.resultsettable
onMatch: DENY
onMismatch: DENY
# 88888888
- name: log.munzi.interceptor
includeLocation: TRUE
additivity: FALSE
level: INFO
AppenderRef:
- ref: Console_Appender
- ref: Info_Req_Res_RollingFile_Appender
- ref: Debug_RollingFile_Appender
- name: log.munzi.error
includeLocation: TRUE
additivity: FALSE
level: ERROR
AppenderRef:
- ref: Console_Appender
- ref: Info_Req_Res_RollingFile_Appender
- name: log.munzi.stacktrace.error
includeLocation: TRUE
additivity: FALSE
level: ERROR
AppenderRef:
- ref: Console_Appender
- ref: Err_StackTrace_RollingFile_Appender
- name: xxx.xxx.xxx.event.component
includeLocation: TRUE
additivity: FALSE
level: INFO
AppenderRef:
- ref: Console_Appender
- ref: Info_Event_RollingFile_Appender
- name: xxx.xxx.xxx.scheduler
includeLocation: TRUE
additivity: FALSE
level: INFO
AppenderRef:
- ref: Console_Appender
- ref: Info_Scheduler_RollingFile_Appender
-
monitorInterval : ์๋ฒ๋ฅผ ์ฌ์์ ํ์ง ์์๋, ์ ์ฉ๋ ์๊ฐ ๋จ์๋ก(์ด ๋จ์) ํด๋น ํ์ผ์ ์์ ์ฌํญ์ ๋ฐ์ํด ์ฃผ๋ ์ค์
-
ํ์ผ ๋ด์์ ์ฌ์ฉํ ๋ณ์ ์ง์
-
Appender ์ค์ : ๋ก๊ทธ๋ฅผ ์ด๋ค ๋ฐฉ์์ผ๋ก ์ฐ์ ๊ฒ์ธ์ง ์ค์
ConsoleAppender, FileAppender, RollingFileAppender ๋ฑ ์กด์ฌ
Console : console์ System.out์ผ๋ก ์ฐ์
File : ํ์ผ์ ์ฐ์
RollingFile : ํ์ผ์ ์ฐ๊ณ , ํน์ ๊ธฐ์ค์ ๋ฐ๋ผ ์์ถ
์์ธํ ๋ด์ฉ์ ์๋ ๋งํฌ ์ฐธ๊ณ ! Log4j 2 ์ ๋๋ก ์ฌ์ฉํ๊ธฐ - ๊ฐ๋
-
Logger : ์ด๋์์ ์ฐ์์ง ์ค์ ํ ๋ถ๋ถ
-
Root : ๋ชจ๋ ๋ก๊ทธ
โ INFO ๋ ๋ฒจ ์ด์์ ๋ชจ๋ ๋ก๊ทธ๋ฅผ Console_Appender๋ฅผ ์ด์ฉํด ์ฐ๊ฒ ๋ค๊ณ ์ค์ ํ ๋ถ๋ถ
-
ํจํค์ง ๋จ์ ์ค์
๋๊ธฐ ๋ฐฉ์์ Logger๊ฐ ์๊ณ , ๋น๋๊ธฐ ๋ฐฉ์์ AsyncLogger๊ฐ ์๋๋ฐ
๋น๋๊ธฐ ๋ฐฉ์์ ์ฌ์ฉํ ๊ฒฝ์ฐ, includeLocation: true๋ฅผ ์ค์ ํด ์ค์ผ ํธ์ถํ ๊ฒฝ๋ก๋ฅผ ์ฐพ์์ฌ ์ ์์ผ๋ฏ๋ก ๋ถ์ฌ์ฃผ์.
name : package ๊ฒฝ๋ก
additivity : ์ค๋ณต ์ ๊ฑฐ ์ค์
-
log4jdbc-log4j2๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, log4jdbc.log4j2์์ ๋ชจ๋ ๋ก๊ทธ๋ฅผ ์ฐ๊ณ ์๊ธฐ ๋๋ฌธ์
๊ธฐ์กด์ application.yml์์ logging.jdbc.connection: ERROR ์์ผ๋ก ์ผ๋ ๋ถ๋ถ์ ์์ธํ๊ฒ ์ค์ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ์ MarkerFilter๋ฅผ ์ฌ์ฉํด์ ์ค์ ํด ์ฃผ๋ฉด ๋๋ค.
์ ์์ค๋ jdbc.resultsettable์ด๋ฉด ์ฐ๊ณ , ๊ทธ ์ธ์๋ ๋ชจ๋ ์ฐ์ง ์๊ฒ ๋ค๊ณ ์ค์ ํด ๋์ ๊ฒ์ด๋ค.
-
req, res, err ๋ก๊ทธ๋ฅผ log.munzi.interceptor์์ ์ฐ๊ธฐ ๋๋ฌธ์ ๋ค์์ ์ถ๊ฐํด ์ฃผ์ด์ผ ํ๋ค.
< DB๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ! >
<build.gradle ํ์ผ>
configurations {
...
all {
...
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'org.bgee.log4jdbc-log4j2', module: 'log4jdbc-log4j2-jdbc4.1'
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-data-jpa'
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
}