본문 바로가기
공부 기록

[Spring Boot] LogBack.xml to Java Config Bean

by 타태 2022. 3. 23.

 

 

2022.03.21 - [문제 해결 기록] - [Spring boot X Spring Security] CORS 설정하기

 

[Spring boot X Spring Security] CORS 설정하기

2022.03.14 - [실전 공부/Java&Spring&SpringBoot] - [REACT x SPRING BOOT] Axios + multipart/form-data 깔끔하게 받기 (파일 + 게시물 동시에 받기) [REACT x SPRING BOOT] Axios + multipart/form-data 깔끔..

ktae23.tistory.com

 

 

최근 XML로 작성한 설정 정보를 Java Class 기반으로 변경하는 작업을 진행했다.

이때 EhCache같은 건 예제가 많은데 LogBack은 아무리 찾아도 예제가 스택오버플로우에 한두개 겨우 있었다.

 

그래서 직접 적용해서 설정한 후 코드를 공유한다.

로그백 설명과 설정 방법은 너무나 많은 분들이 공유하고 있기 때문에 실제 사용 가능 코드만 공유하니 상황에 맞게 수정하여 사용하기를 바란다.

 

 application.yml

logs:
  config:
    filePath: C:\Users\userPath\logs
    fileName: ssr_lc

 

 LogBackConfig

package com.openeg.openegscts.utils;

import static ch.qos.logback.classic.Level.*;
import static java.io.File.separator;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.LevelFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.RollingPolicy;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.util.FileSize;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LogBackConfig {

    @Value("${logs.config.filePath}")
    private String filePath;
    @Value("${logs.config.fileName}")
    private String fileName;

    // 공통 필드, 어펜더 별 설정을 달리 할 경우 지역변수로 변경
    // 또는 파일 경로 및 이름처럼 프로퍼티 주입으로 변경 가능
    private final LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory();
    private final String pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n";
    private final String fileNamePattern = ".%d{yyyy-MM-dd}_%i";
    private final String ext = ".log";
    private final String maxFileSize = "10MB";
    private final int maxHistory = 30;



    // 어펜더 목록, 다른 어펜더가 필요할 경우 추가
    private ConsoleAppender<ILoggingEvent> consoleAppender;
    private RollingFileAppender<ILoggingEvent> fileAppender;
    private RollingFileAppender<ILoggingEvent> filterAppender;


    @Bean
    public void logConfig() {
        consoleAppender = getLogConsoleAppender();
        fileAppender = getLogFileAppender();
        filterAppender = getFilterLogFileAppender();
        createLoggers();
    }

    private void createLoggers() {
        // 로거 이름, 로깅 레벨, 상위 로깅 설정 상속 여부
        createLogger("root", INFO, true);
        createLogger("jdbc", OFF, false);
        createLogger("jdbc.sqlonly", DEBUG, false);
        createLogger("jdbc.sqltiming", DEBUG, false);
        createLogger("com.openeg.openegscts", INFO, false);
        createLogger("com.openeg.openegscts.*.controller", DEBUG, false);
        createLogger("com.openeg.openegscts.*.service", WARN, false);
        createLogger("com.openeg.openegscts.*.repository", INFO, false);
        createLogger("com.openeg.openegscts.*.security", DEBUG, false);
    }

    // 어펜더 추가 시 로거 등록 필요
    private void createLogger(String loggerName, Level logLevel, Boolean additive) {
        Logger logger = logCtx.getLogger(loggerName);
        logger.setAdditive(additive);
        logger.setLevel(logLevel);
        logger.addAppender(consoleAppender);
        logger.addAppender(fileAppender);
        logger.addAppender(filterAppender);
    }

    // 콘솔 로그 어펜더 생성
    private ConsoleAppender<ILoggingEvent> getLogConsoleAppender() {
        final String appenderName = "STDOUT";

        PatternLayoutEncoder consoleLogEncoder = createLogEncoder(pattern);
        return createLogConsoleAppender(appenderName, consoleLogEncoder);
    }

    // 롤링 파일 어펜더 생성
    private RollingFileAppender<ILoggingEvent> getLogFileAppender() {
        final String appenderName = "LOGS";

        final String logFilePath = filePath + separator + fileName;
        final String archiveLogFile = filePath + separator + appenderName  + separator + fileName + fileNamePattern;

        PatternLayoutEncoder fileLogEncoder = createLogEncoder(pattern);
        RollingFileAppender<ILoggingEvent> logFileAppender = createLogFileAppender(appenderName, logFilePath, fileLogEncoder);
        SizeAndTimeBasedRollingPolicy<RollingPolicy> logFilePolicy = createLogFilePolicy(maxFileSize, maxHistory, archiveLogFile, logFileAppender);

        logFileAppender.setRollingPolicy(logFilePolicy);
        logFileAppender.start();

        return logFileAppender;
    }

    // 롤링 파일 어펜더 생성 (필터 적용)
    private RollingFileAppender<ILoggingEvent> getFilterLogFileAppender() {
        final String appenderName = "ERROR";

        final String errorLogFilePath = filePath + separator + appenderName  + separator + fileName;
        final String errorLogFile = errorLogFilePath + fileNamePattern;

        PatternLayoutEncoder fileLogEncoder = createLogEncoder(pattern);
        RollingFileAppender<ILoggingEvent> logFileAppender = createLogFileAppender(appenderName, errorLogFilePath, fileLogEncoder);
        SizeAndTimeBasedRollingPolicy<RollingPolicy> logFilePolicy = createLogFilePolicy(maxFileSize, maxHistory, errorLogFile, logFileAppender);
        LevelFilter levelFilter = createLevelFilter(ERROR);

        logFileAppender.setRollingPolicy(logFilePolicy);
        logFileAppender.addFilter(levelFilter);
        logFileAppender.start();

        return logFileAppender;
    }

    private PatternLayoutEncoder createLogEncoder(String pattern) {
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(logCtx);
        encoder.setPattern(pattern);
        encoder.start();
        return encoder;
    }

    private ConsoleAppender<ILoggingEvent> createLogConsoleAppender(String appenderName, PatternLayoutEncoder consoleLogEncoder) {
        ConsoleAppender<ILoggingEvent> logConsoleAppender = new ConsoleAppender<>();
        logConsoleAppender.setName(appenderName);
        logConsoleAppender.setContext(logCtx);
        logConsoleAppender.setEncoder(consoleLogEncoder);
        logConsoleAppender.start();
        return logConsoleAppender;
    }

    private RollingFileAppender<ILoggingEvent> createLogFileAppender(String appenderName, String logFilePath, PatternLayoutEncoder logEncoder) {
        RollingFileAppender<ILoggingEvent> logFileAppender = new RollingFileAppender<>();
        logFileAppender.setName(appenderName);
        logFileAppender.setContext(logCtx);
        logFileAppender.setEncoder(logEncoder);
        logFileAppender.setAppend(true);
        logFileAppender.setFile(logFilePath + ext);
        return logFileAppender;
    }

    private SizeAndTimeBasedRollingPolicy<RollingPolicy> createLogFilePolicy(String maxFileSize, int maxHistory, String fileNamePattern, RollingFileAppender<ILoggingEvent> logFileAppender) {
        SizeAndTimeBasedRollingPolicy<RollingPolicy> logFilePolicy = new SizeAndTimeBasedRollingPolicy<>();
        logFilePolicy.setContext(logCtx);
        logFilePolicy.setParent(logFileAppender);
        logFilePolicy.setFileNamePattern(fileNamePattern + ext);
        logFilePolicy.setMaxHistory(maxHistory);
        logFilePolicy.setMaxFileSize(FileSize.valueOf(maxFileSize));
        logFilePolicy.start();
        return logFilePolicy;
    }

    private LevelFilter createLevelFilter(Level level) {
        LevelFilter levelFilter = new LevelFilter();
        levelFilter.setLevel(level);
        levelFilter.setOnMatch(FilterReply.ACCEPT);
        levelFilter.setOnMismatch(FilterReply.DENY);
        levelFilter.start();
        return levelFilter;
    }

}

 

 

예제를 찾고 상황에 맞춰 이렇게 저렇게 수정하고 메서드를 구분하고 작동 확인까지 한 뒤 커밋을하려니 굉장히 많은 경고 메시지를 보게 되었다.

 

 

 

찾아보니 제네릭에 들어갈 타입을 명시해주지 않아서 생기는 경고였다.

소나린트에서 알려주듯이 List<String>으로 명시해주지 않고 List라고 쓴것과 같은 상황이다.

이는 "컴파일러에 형식 안전성을 보장하는 데 필요한 모든 형식 검사를 수행하기에 충분한 형식 정보가 없음을 의미"한다

by 공식문서

 

그런데 XML만 봤지 Java 클래스로 설정한 예제를 찾지 못해서 타입으로 어떤걸 넣어줘야 할지 모르겠더라.

그래서 일단 코드를 까봤다.

음, 상속한 부모 객체를 넣으면 타입 문제는 괜찮겠지?

응 아니야~~

친절하게 ILoggingEvent를 넣으라고 알려준다.

그래도 롤링 정책 객체는 부모 객체로 지정해주니 문제 없이 된다.

 

실행한 결과로 로그 파일이 생성되고 에러는 에러끼리 잘 구분해서 저장되고 있다.

반응형

댓글