본문 바로가기

WEB개발/WEB, WAS

TheadLocal & MDC & Log4j2

 서버에서 로그를  확인할 경우 grep으로 로그를 캐치할 시 라인단위로 인식하기 때문에 모든 trace를 볼 수 없다.  MDC와 log4j2를 사용하면 각 접속한 IP별로 로그를 확인 할 수 있다.

 

ThreadLocal 

 

ThreadLocal 변수를 선언하면 멀티 스레드 환경에서 각 스레드마다 독립적인 변수를 가지고, get(), set() 메소드를 통해 값에 대해 접근할 수 있다.

 

public class TestThread implements Runnable {
private ThreadLocal threadLocal = new ThreadLocal<DataObj>() {
@Override protected DataObj initialValue() {
DataObj dataObj = new DataObj();
return dataObj;
}
};
@Override
public void run() {
DataObj dataObj = (DataObj) threadLocal.get();
dataObj.setTrxNo(Math.random() + "");
threadLocal.set(dataObj);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(((DataObj) threadLocal.get()).getTrxNo());
}

> 두 개의 쓰레드 런

0.5002465179334552
0.3244909506839274

 

MDC (Mapped Diagnostic Context)

 

 멀티 클라이언트 환경에서 다른 클라이언트와 값을 구별하여 로그를 추적할 수 있도록 제공되는 map이다.

ThreadLocal에 구별할 수 있는 키 값을 저장하여 Thread가 존재하는 동안 계속해서 사용할 수 있도록 하는 방법으로

현재 log4j 및 logback만 MDC기능을 제공하고 있다.

 

 MDC는 static으로 올라간 맵에 저장된다

public class MDCManager {
private String clientIp;
public String getClientIp() {
return clientIp;
}
public void setClientIp(String clientIp) {
MDC.put("clientIp" , clientIp);
this.clientIp = clientIp;
}
}

 

Log4j2

pom.xml 로깅

<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<!-- Log4j2 SLF4J Bridge -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.5</version>
</dependency>
<!-- SLF4J JCL Bridge -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.18</version>
</dependency>
<!-- SLF4J Log4j 1.x Bridge -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.18</version>
</dependency>
<!-- Logging -->

 

log4j2.xml 설정

쓰레드로컬에 저장된 아이피별로 라우팅 시킨다.

 

패턴에 클라이언트 아이피가 있다면 라우팅 시킨다. 그러나 그 값이 DEFAULT라면 DEFAULT로 라우트한다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<!-- Appenders -->
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [$${ctx:clientIp}][%t] %C{2} (%F:%L) - %m%n" />
</Console>
<Routing name="ROUTING">
<Routes pattern="$${ctx:clientIp}">
<Route>
<RollingFile name="Rolling-${ctx:clientIp}"
fileName="C:/logs/myapp/${ctx:clientIp}_myapp.out"
filePattern="C:/logs/myapp/${ctx:clientIp}_myapp-%d{yyyy-MM-dd}.out">
<PatternLayout>
<pattern>%d{yyyy-MM-dd:hh:mm:ss} [%p][%C{1}][%M (%L)}][$${ctx:clientIp}] | %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
</Route>
<Route key="DEFAULT">
<RollingFile name="Rolling-default"
fileName="C:/logs/myapp/default_myapp.out"
filePattern="C:/logs/myapp/default_myapp-%d{yyyy-MM-dd}.out">
<PatternLayout>
<pattern>%d{yyyy-MM-dd:hh:mm:ss} [%p][%C{1}][%M (%L)}][$${ctx:clientIp}] | %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<DefaultRolloverStrategy max="1"/>
</RollingFile>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<!-- Application Loggers -->
<Logger name="com.woo.test" level="debug">
<AppenderRef ref="STDOUT" />
<AppenderRef ref="ROUTING" />
</Logger>
<Root level="debug">
</Root>
</Loggers>
</Configuration>

 

 


스프링부트

 

Spring에서는 기본적으로 Logback을 이용해서 로깅을 하기 때문에 다른 로깅 라이브러리인 Log4j2를 그냥 추가하게 되면, 로깅 라이브러리끼리 충돌이 발생한다.
때문에 Log4j2를 적용하기 위해서는 Logback 라이브러리를 제거해야 한다.

 

@WebFilter("/*")
public class ClientIpFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 클라이언트 IP를 MDC에 설정
String clientIp = request.getRemoteAddr();
MDC.put("clientIp", clientIp);
try {
chain.doFilter(request, response);
} finally {
// 요청 처리 후 MDC에서 제거
MDC.remove("clientIp");
}
}
@Override
public void destroy() {}
}

 

1. @ServletComponentScan 추가

@WebFilter로 정의된 필터를 등록하려면 @ServletComponentScan을 추가해야 합니다.

@SpringBootApplication
@ServletComponentScan // @WebFilter, @WebServlet 등을 스캔
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation("org.springframework.boot:spring-boot-starter-log4j2")
}
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
}

 

 

request.getRemoteAddr(); 에서 IPv6값이 나옴 -D옵션 추가필요

RUN&DEBUG > Add Configuration클릭

 

"vmArgs": "-Djava.net.preferIPv4Stack=true", // 여기에서 JVM 옵션 추가

 

 

 


 

https://055055.tistory.com/96