programing

LogBack Appender를 프로그래밍 방식으로 구성합니다.

golfzon 2023. 3. 20. 23:46
반응형

LogBack Appender를 프로그래밍 방식으로 구성합니다.

logback.xml에 정의된 logback appender가 있는데, DB appender인데 bean으로 정의된 자신의 접속 풀을 사용하여 java에서 appender를 구성하는 방법이 있는지 궁금합니다.

비슷한 걸 찾긴 하지만 실제 답은 절대 못 찾아요.

이 예에서는 FileAppender를 사용하고 있습니다.

import org.slf4j.LoggerFactory;

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.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;

public class Loggerutils {

    public static void main(String[] args) {
          Logger foo = createLoggerFor("foo", "foo.log");
          Logger bar = createLoggerFor("bar", "bar.log");
          foo.info("test");
          bar.info("bar");
    }

    private static Logger createLoggerFor(String string, String file) {
          LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
          PatternLayoutEncoder ple = new PatternLayoutEncoder();

          ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
          ple.setContext(lc);
          ple.start();
          FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
          fileAppender.setFile(file);
          fileAppender.setEncoder(ple);
          fileAppender.setContext(lc);
          fileAppender.start();

          Logger logger = (Logger) LoggerFactory.getLogger(string);
          logger.addAppender(fileAppender);
          logger.setLevel(Level.DEBUG);
          logger.setAdditive(false); /* set to true if root should log too */

          return logger;
    }

}

부록을 프로그래밍 방식으로 구성할 수 있습니다.거의 모든 부록은 프로그램 구성을 사용하여 테스트됩니다.따라서 로그백프로젝트 소스 코드에는 프로그램적인 Appender 설정 예가 많이 있습니다. logback-core appender 를 확인합니다.logback-core/src/test/java는, 「」아래를 해 주세요logback-classic/src/test/java.

참고로 로거 작성을 담당하는 코드를 수정하려고 할 때 로거가 동작하기 위해서는 충족해야 할 규칙이 많이 있습니다.

이러한 규칙은 매우 유용한 기사 "Slf4j/logback의 프로그래밍 구성:

slf4j/logback의 프로그래밍 구성 경험이 있습니다.

작업

프로그램은 처리된 각 입력 파일에 대해 별도의 로그 파일을 열어야 합니다.

태스크용 솔루션

xml 경유로 로그백을 설정하는 것이 아니라 인코더, 추가 프로그램 및 로거를 "수동으로" 인스턴스화한 후 이들을 구성하고 링크해야 합니다.

경고 1

부가 프로그램 간에 인코더(PatternLayoutEncoder 등)를 공유하려고 하면 로그백이 미쳐버립니다.

경고 1의 해결책

각 Appender에 대해 개별 인코더를 만듭니다.

경고 2

인코더 및 추가 프로그램이 로깅 컨텍스트와 연결되어 있지 않은 경우 로그백은 아무것도 로깅하지 않습니다.

경고 2의 해결책

파라미터로서 LoggerFactory를 전달하여 각 인코더 및 Appender에서 setContext를 호출합니다.

경고 3

인코더 및 추가 프로그램이 시작되지 않은 경우 로그백은 어떤 것도 기록하기를 거부합니다.

경고 3을 위한 솔루션

인코더와 부록은 올바른 순서로 시작해야 합니다. 즉, 먼저 인코더와 부록이 차례로 시작됩니다.

경고 4

RollingPolicy 객체(예:TimeBasedRollingPolicy)는 Appender와 동일한 컨텍스트에 연결되어 있지 않은 경우 "date format not recognized" 등의 이상한 오류 메시지를 생성합니다.

경고 4의 해결책

RollingPolicy에서 setContext를 호출하는 방법은 인코더 및 부록과 동일합니다.

다음으로 "수동" 로그백 설정의 작업 예를 나타냅니다.

package testpackage

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.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy

import org.slf4j.LoggerFactory

class TestLogConfig {

  public static void main(String[] args) {
    LoggerContext logCtx = LoggerFactory.getILoggerFactory();

    PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    ConsoleAppender logConsoleAppender = new ConsoleAppender();
    logConsoleAppender.setContext(logCtx);
    logConsoleAppender.setName("console");
    logConsoleAppender.setEncoder(logEncoder);
    logConsoleAppender.start();

    logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    RollingFileAppender logFileAppender = new RollingFileAppender();
    logFileAppender.setContext(logCtx);
    logFileAppender.setName("logFile");
    logFileAppender.setEncoder(logEncoder);
    logFileAppender.setAppend(true);
    logFileAppender.setFile("logs/logfile.log");

    TimeBasedRollingPolicy logFilePolicy = new TimeBasedRollingPolicy();
    logFilePolicy.setContext(logCtx);
    logFilePolicy.setParent(logFileAppender);
    logFilePolicy.setFileNamePattern("logs/logfile-%d{yyyy-MM-dd_HH}.log");
    logFilePolicy.setMaxHistory(7);
    logFilePolicy.start();

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

    Logger log = logCtx.getLogger("Main");
    log.setAdditive(false);
    log.setLevel(Level.INFO);
    log.addAppender(logConsoleAppender);
    log.addAppender(logFileAppender);
  }
}

프로그램 구성의 구체적인 예를 찾는 사람이 있다면요

Console Appender의 문자 집합을 설정합니다.

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ConsoleAppender<ILoggingEvent> appender =
    (ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT");
LayoutWrappingEncoder<ILoggingEvent> enc = 
    (LayoutWrappingEncoder<ILoggingEvent>) appender.getEncoder();
enc.setCharset(Charset.forName("utf-8"));

my logback.xml:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <charset>866</charset>
        <pattern>[%level] %msg%n</pattern>
    </encoder>
</appender>

<logger name="appconsole">
    <appender-ref ref="STDOUT" />
</logger>

로거를 프로그래밍 방식으로 설정해야 하는 이유왜냐하면 앱(Spring Boot)을 jar 파일로 패키징하고 있기 때문입니다.따라서 Logback.xml 파일은 jar 안에 숨겨져 있는 것처럼 보입니다.단, 포장을 풀고 변경하는 것은 편리하지 않습니다.그리고 app.jar 옆에 logback.xml 파일은 필요 없습니다.앱의 모든 구성 속성이 포함된 app.yaml 파일만 가지고 있습니다.

현재 버전(logback=1.3.0-alpha16,slf4j=2.0.0-alpha7)에서는 SPI를 사용하여 구성을 프로그래밍 방식으로 구축할 수 있습니다.

다음과 같은 이름의 텍스트 파일이 항아리에 필요합니다.ch.qos.logback.classic.spi.Configurator위치META-INF/services로그백이 주울 수 있도록 항아리 안에 넣어두세요.파일에는 초기화를 수행하는 클래스의 완전 수식 클래스 이름이 포함되어 있어야 합니다.

그런 다음 다음과 같이 초기화를 수행하는 클래스를 만듭니다.

package example

import java.nio.charset.Charset;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.spi.ContextAwareBase;

public class ProgrammaticLoggingConfigurator extends ContextAwareBase implements Configurator {
  
  @Override
  public void configure(LoggerContext loggerContext) {

    PatternLayout pattern = new PatternLayout();
    pattern.setContext(loggerContext);
    pattern.setPattern("[%level] %msg%n");
    pattern.start();

    LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<ILoggingEvent>();
    encoder.setContext(loggerContext);
    encoder.setCharset(Charset.forName("utf-8"));
    encoder.setLayout(pattern);

    ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>();
    consoleAppender.setContext(loggerContext);
    consoleAppender.setName("console");
    consoleAppender.setEncoder(encoder);
    consoleAppender.start();

    Logger log = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
    log.setAdditive(false);
    log.setLevel(Level.DEBUG);
    log.addAppender(consoleAppender);
  }
}

따라서 이 예에서는 다음과 같은 파일이 필요합니다.ch.qos.logback.classic.spi.Configurator다음 내용을 포함:example.ProgrammaticLoggingConfigurator줍기 위해서요.

코멘트는 할 수 없습니다(아직은요?) 3가지 힌트를 추가하고 싶습니다.

  • 위의 경고에 대해 문제가 있는 경우 에 콜을 추가합니다.

    StatusPrinter.print(context);
    

    모든 것이 구성된 후, 즉 루트/"메인" appender를 추가한 후 문제가 무엇인지 알려줍니다.

  • 로그 레벨을 다른 파일로 구분하는 것을 매우 좋아합니다.오류를 찾을 때는 에러 파일을 참조해, 다음과 같이 셋업 합니다.

tot_[app name].log   : Level.INFO
deb_[app name].log   : Level.DEBUG
err_[app name].log   : Level.ERROR

다음과 같은 단순한 개인 필터 클래스를 통한 라우팅

    private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {

        private final Level level;

        private ThresholdLoggerFilter(Level level){
            this.level = level;
        }

        @Override
        public FilterReply decide(ILoggingEvent event) {
            if (event.getLevel().isGreaterOrEqual(level)) {
                return FilterReply.NEUTRAL;
            } else {
                return FilterReply.DENY;
            }
        }
    }

그리고 전화만 하면 돼myFilter.start()그리고.myAppender.addFilter(myFilter);.

  • 마지막으로 로그 레벨을 동적으로 변경하여 셋업으로 다음과 같은 간단한 인터페이스를 구현하고 싶습니다.

    public interface LoggingService {
        void setRootLogLevel(Level level);
    }
    

유효한 입력이 있을 때마다 이 서비스를 다음과 같이 구현할 수 있도록 모니터링되는 속성 파일에 루트 로깅 수준을 유지합니다.

    @Override
    public void setRootLogLevel(Level level) {
        if (context != null && context.isStarted()) {
        ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
        }
    }

새로운 root logger 레벨로.

여기서 주목해야 할 점은 ch.qos.logback.classic에 타이프캐스트해야 한다는 것입니다.로거 및 추가 프로그램을 설정할 수 있습니다.

    import ch.qos.logback.classic.Logger;
    import org.slf4j.LoggerFactory;
    Logger log = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
            log.addAppender(mockAppender);

언급URL : https://stackoverflow.com/questions/16910955/programmatically-configure-logback-appender

반응형