Spring Boot Log4j2 설정하기

Jae Bin
39 min readDec 3, 2023

--

System.out.println() 의 문제점

  • 동기화(Synchronization): System.out.println() 시 호출되는 newline() 에는 synchronized 키워드를 통해 동기화를 진행하고 있다.
  • Blocking I/O: synchronized 영역의 코드를 진행할 때, multi-thread 간의 Blocking 작업이 일어난다.
  • 로그 저장소: System.out은 콘솔에 출력되기 때문에 별도로 파일로 저장하여 관리하기가 힘들다.

Logger

spring boot 의 Logger는 Log4j -> Logback -> Log4j2 로 발전되어 왔다.
기본적으로 spring boot starter 에 설정되어 있는 logger 는 Logback 이다.

Log Level은 다음과 같은 계층을 가진다.

Trace < Debug < Info < Warning < Error < Fatal

로그의 수준이 INFO라면 INFO 수준을 포함하여 그보다 중요도가 같거나 높은 로그들만 찍히게 된다.

이외에도 모든 로그를 찍겠다는 ALL 설정과 모든 로그를 찍지 않겠다는 OFF 설정이 있고, ALLTrace 와 같은 설정으로 보면 된다.

Log4j2 란

spring 에서는 자체적으로 Logback이라는 로깅 라이브러리를 사용하고 있다. Log4j2Logback에 비해 어느 상황에서나 더 빠른 속도를 가지고 있으며 자바의 람다식을 활용할 수 있는 메소드도 정의되어 있다.
때문에, 기존의 LogbackLog4j2 로 바꾸는 것이 좋다.

Log4j2 설정

1. 의존성 추가

dependencies {
implementation("org.springframework.boot:spring-boot-starter-log4j2")
}

2. build.gradle 에서 Logback 제거하기

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

configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
}

3. application 설정에 Logging 설정하기

로그 설정 파일은 application.yml 또는 application.properties 에 설정할 수 있다. XML 파일을 이용해서 설정을 분리하여 구성할 수도 있다.
분리하여 XML 파일을 만들 경우 파일의 classpath를 지정해주어야 한다.

# log4j2.xml 파일 경로 지정
logging:
config: classpath:log4j2.xml

4. log4j2.xml 파일 설정

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Properties>
<Property name="logPath">./logs</Property>
<Property name="logPattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%t] %c{1} - %msg%n</Property>
<Property name="serviceName">application</Property>
</Properties>
<Appenders>
<Console name="console">
<PatternLayout pattern="${logPattern}"/>
</Console>
<RollingFile
name="file"
append="true"
fileName="${logPath}/${serviceName}.log"
filePattern="${logPath}/${serviceName}.%d{yyyy-MM-dd}.%i.log.gz">
<PatternLayout pattern="${logPattern}"/>
<Policies>
<SizeBasedTriggeringPolicy size="5MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
<DefaultRolloverStrategy>
<Delete basePath="${logPath}" maxDepth="1">
<IfFileName glob="${serviceName}.*.log"/>
<IfLastModified age="15d"/>
</Delete>
</DefaultRolloverStrategy>
</Appenders>
<Loggers>
<Logger name="consolelog" level="info" additivity="false">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</Logger>
</Loggers>
</Configuration>
  • Property 를 설정하여, xml 파일 내부의 변수로 사용한다.
    Logger 설정을 할 때 사용할, logPath, logPattern, serviceName 을 먼저 정의하였다. 변수는 ${variable} 형태로 사용할 수 있다.
<Properties>
<Property name="logPath">./logs</Property>
<Property name="logPattern">[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%t] %c{1} - %msg%n</Property>
<Property name="serviceName">application</Property>
</Properties>
  • <Appenders>: 로그 이벤트를 콘솔과 파일에 출력하는 데 사용되는 것으로, 다양한 appenders(추가자)를 내부에 정의한다.
    차후 Logger 에서 이 appender 를 참조하여 사용한다.
<Appenders>
...
</Appenders>
  • <Console>: 콘솔에 로그를 출력하는 appender를 정의한다.
  • name="console": appender의 이름을 "console"로 지정한다.
  • <PatternLayout>: 출력되는 로그의 형식을 정의한다.
<Appenders>
<Console name="console">
<PatternLayout pattern="${logPattern}"/>
</Console>
</Appenders>
  • <RollingFile>: 파일에 로그를 기록하는 appender를 정의한다. rolling file appender는 파일의 크기 또는 시간에 따라 새로운 파일을 생성한다.
  • name="file": 이 appender의 이름을 "file"로 지정한다.
  • append="true": 기존 파일에 로그를 추가할 것인지 여부를 나타낸다.
  • fileName="${logPath}/${serviceName}.log": 로그 파일의 경로와 이름을 정의한다.
  • filePattern="${logPath}/${serviceName}.%d{yyyy-MM-dd}.%i.log.gz": rolling file 패턴을 정의한다. 시간별로 로그를 분할하여 압축한 형태로 저장한다.
  • <PatternLayout>: 파일에 기록될 로그의 형식을 정의한다.
<Appenders>
<RollingFile
name="file"
append="true"
fileName="${logPath}/${serviceName}.log"
filePattern="${logPath}/${serviceName}.%d{yyyy-MM-dd}.%i.log.gz">
<PatternLayout pattern="${logPattern}"/>
</RollingFile>
</Appenders>
  • <Policies>: rolling file의 행동을 결정하는 정책을 정의한다.
  • <SizeBasedTriggeringPolicy size="5MB"/>: 파일 크기가 5MB가 되면 새로운 파일로 롤오버된다.
  • <TimeBasedTriggeringPolicy/>: 시간 기반으로도 파일을 롤오버할 수 있도록 한다.
<Appenders>
<Policies>
<SizeBasedTriggeringPolicy size="5MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</Appenders>
  • <DefaultRolloverStrategy>: rolling file이 일어날 때 사용할 기본 롤오버 전략을 정의한다.
  • <Delete basePath="${logPath}" maxDepth="1">: 일정 조건에 따라 로그 파일을 삭제하는 전략을 설정한다.
  • <IfFileName glob="${serviceName}.*.log"/>: 특정 파일명 패턴에 따라 파일을 삭제한다.
  • <IfLastModified age="15d"/>: 파일의 마지막 수정일로부터 15일이 경과한 파일을 삭제한다.
<Appenders>
<DefaultRolloverStrategy>
<Delete basePath="${logPath}" maxDepth="1">
<IfFileName glob="${serviceName}.*.log"/>
<IfLastModified age="15d"/>
</Delete>
</DefaultRolloverStrategy>
</Appenders>
  • <Loggers>: 로그 레벨과 로깅 이벤트를 특정 appender에 연결하는 것을 설정한다.
  • name="consolelog": 로거가 적용되는 패키지 이름을 지정한다.
  • level="info": 이 로거의 로그 레벨을 "info"로 설정한다.
  • additivity="false": 부모 로거로 이벤트 전파를 중단한다.
  • <AppenderRef ref="console"/>: 이 로거에 콘솔 appender를 참조한다. 즉, 이 패키지의 로그는 콘솔에 출력된다.
  • <AppenderRef ref="file"/>: 이 로거에 파일 appender를 참조한다. 따라서 이 패키지의 로그는 파일에도 기록된다.
<Loggers>
<Logger name="consolelog" level="info" additivity="false">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</Logger>
</Loggers>

Log4j2 사용하기

spring boot project 내에서 다음과 같이 @Slf4j2 @Log4j2 어노테이션을 사용하여, logging 할 수 있다. 객체 log 가 자동 생성되어 별도의 선언 없이 logger를 사용할 수 있다.

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
final ErrorCode errorCode = e.getErrorCode();
final ErrorResponse response = new ErrorResponse(errorCode);

// [ Error Level ]
// trace < debug < info < warn < error
// 현재 logging level 은 info 로 설정되어, info, warn, error 만 로그에 남는다

log.trace(e.toString());
log.debug(e.toString());
log.info(e.toString());
log.warn(e.toString());
log.error(e.toString());

return ResponseEntity.status(errorCode.getStatus()).body(response);
}
}

그리고 error 가 발생 시, 다음과 같이 지정 경로 폴더 밑에 log 가 생성되는 것을 확인 할 수 있다.

[ERROR] 2023-12-09 12:42:15 [http-nio-8080-exec-1] GlobalExceptionHandler - G001 : 예상치 못한 서버 내부 오류[consolelog.global.support.AuthInterceptor.preHandle(AuthInterceptor.java:45), org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:146), org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1076), org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974), org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011), org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590), org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167), org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90), org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482), org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115), org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93), org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74), org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341), org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391), org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63), org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894), org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740), org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52), org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191), org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659), org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61), java.base/java.lang.Thread.run(Thread.java:833)]
[ERROR] 2023-12-09 12:42:18 [http-nio-8080-exec-2] GlobalExceptionHandler - G001 : 예상치 못한 서버 내부 오류[consolelog.global.support.AuthInterceptor.preHandle(AuthInterceptor.java:45), org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:146), org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1076), org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974), org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011), org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590), org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167), org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90), org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482), org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115), org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93), org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74), org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341), org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391), org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63), org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894), org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740), org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52), org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191), org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659), org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61), java.base/java.lang.Thread.run(Thread.java:833)]
[ERROR] 2023-12-09 12:42:44 [http-nio-8080-exec-4] GlobalExceptionHandler - M005 : 비정상적인 회원가입 절차입니다.,[consolelog.member.service.MemberService.validateUniqueNickname(MemberService.java:78), consolelog.member.service.MemberService.validate(MemberService.java:61), consolelog.member.service.MemberService.signUp(MemberService.java:37), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77), java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43), java.base/java.lang.reflect.Method.invoke(Method.java:568), org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343), org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196), org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163), org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751), org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123), org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391), org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119), org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184), org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751), org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703), consolelog.member.service.MemberService$$SpringCGLIB$$0.signUp(<generated>), consolelog.member.controller.MemberController.signUp(MemberController.java:35), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77), java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43), java.base/java.lang.reflect.Method.invoke(Method.java:568), org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205), org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150), org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118), org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884), org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797), org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87), org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081), org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974), org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011), org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590), org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885), jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201), org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116), org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174), org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149), org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167), org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90), org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482), org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115), org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93), org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74), org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341), org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391), org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63), org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894), org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740), org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52), org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191), org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659), org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61), java.base/java.lang.Thread.run(Thread.java:833)]

설정 과정에서 착각했던 점

log 파일은 직접 열어서 하나씩 확인하는 용도가 아니다. 별도의 모니터링 툴을 이용하여 log 를 분석하고, 어떤 오류가 발생했는 지 확인해야 한다.

그런데, logging 하는 과정에서 차후에 log 파일을 직접 열어 error를 확인 할 것이라 착각하고, 각 log에 ‘\n’ 을 추가하여 보기 좋게 하는 작업을 시도 했었다. ‘\n’이 들어가면 오히려 모니터링 툴에서는 하나의 log 가 여러개의 log 로 인식하여 문제가 발생한다. 때문에, 별도의 라인으로 분리할 필요가 없다.

또한, Exception 별로 log 파일을 분리하여 관리할까 생각도 했었는데, 어차피 툴을 이용하여 log 파일별로 filtering 해서 관리할 수 있다. 때문에, 파일을 분리하면 단점만 발생한다.

--

--