1. logback-spring.xml (/resources 내부 포함)
<?xml version="1.0" encoding="UTF-8" ?><configuration>
<property name="logPath" value="/root/logs" />
<property name="fileName" value="application" />
<property name="maxHistory" value="30" />
<property name="maxFileSize" value="10MB" />
<property name="totalSizeCap" value="1GB" />
<property name="consolePattern" value="[%level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
<property name="filePattern" value="[%level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"/>
<property name="slackPattern" value="%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n"/>
<property name="slackHookUri" value="%%%%%%%%%YOUR SLACK WEBHOOK URI%%%%%%%%%"/>
<!-- Log Appender Module --> <springProfile name="console-logging">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${consolePattern}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console"/>
</root>
</springProfile>
<!-- file Appender Module --> <springProfile name="file-logging">
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}//${fileName}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${logPath}//archive/%d{yyyy-MM-dd}_${fileName}_%i.log</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
<maxFileSize>${maxFileSize}</maxFileSize>
<totalSizeCap>${totalSizeCap}</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${filePattern}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="file"/>
</root>
</springProfile>
<!-- Message Appender Module --> <springProfile name="message-logging">
<appender name="slack" class="com.github.maricn.logback.SlackAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${slackPattern}</pattern>
</layout>
<webhookUri>${slackHookUri}</webhookUri>
<username>username</username>
<iconEmoji>:stuck_out_tongue_winking_eye:</iconEmoji>
<colorCoding>true</colorCoding>
</appender>
</springProfile>
<!-- Log Level Module --><!-- <springProfile name="high-level-logging">--><!-- <logger name="root" level="error" />--><!-- <logger name="org.springframework" level="error" />--><!-- <logger name="com.blog.tistory" level="warn" />--><!-- </springProfile>--><!-- <springProfile name="low-level-logging">--><!-- <logger name="root" level="info" />--><!-- <logger name="org.springframework" level="info" />--><!-- <logger name="com.blog.tistory" level="debug" />--><!-- </springProfile>--></configuration>
XML
복사
2. application.yml
# common settingspring: profiles: group: local: - console-logging dev: - file-logging prod: - file-logging---# localspring: config: activate: on-profile: local datasource: ...---# devspring: config: activate: on-profile: dev datasource: ...---# prodspring: config: activate: on-profile: prod datasource: ...
YAML
복사
3. GlobalLoggingFilter.java
package com.testapp.filter;import jakarta.servlet.*;import jakarta.servlet.annotation.WebFilter;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.web.util.ContentCachingRequestWrapper;import org.springframework.web.util.ContentCachingResponseWrapper;import java.io.IOException;import java.util.Collections;@Slf4j@WebFilter(filterName = "loggingFilter", urlPatterns = { "/*" })public class GlobalLoggingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //request&response caching ContentCachingRequestWrapper httpServletRequest = new ContentCachingRequestWrapper((HttpServletRequest) request); ContentCachingResponseWrapper httpServletResponse = new ContentCachingResponseWrapper((HttpServletResponse) response); //main logic chain.doFilter(httpServletRequest, httpServletResponse); String url = httpServletRequest.getRequestURI(); String reqContent = new String(httpServletRequest.getContentAsByteArray()); log.info("\n\n"); log.info(" ========== TEST-APP REQUEST ========== "); log.info("url : {}", url); Collections.list(httpServletRequest.getHeaderNames()).forEach(headerName -> { log.info("headerName {} : {}", headerName, httpServletRequest.getHeader(headerName)); }); log.info("requestBody : {}", reqContent); int httpStatusCode = httpServletResponse.getStatus(); String resContent = new String(httpServletResponse.getContentAsByteArray()); httpServletResponse.copyBodyToResponse(); //이 매서드를 호출해줘야 한번 읽은 response 가 클라이언트에게도 복사되어 응답될 수 있다. log.info("\n\n"); log.info(" ========== TEST-APP RESPONSE ========== "); log.info("httpStatusCode : {}", httpStatusCode); httpServletResponse.getHeaderNames().forEach(headerName -> { log.info("headerName {} : {}", headerName, httpServletResponse.getHeader(headerName)); }); log.info("responseBody : {}", resContent); log.info("\n\n"); }}
Java
복사
4. MainApplication
@SpringBootApplication@ServletComponentScanpublic class TestAppApplication { public static void main(String[] args) { SpringApplication.run(TestAppApplication.class, args); }}
Java
복사