【问题标题】:How to roll log file on app shutdown in logback?如何在 logback 中的应用程序关闭时滚动日志文件?
【发布时间】:2020-05-14 00:14:28
【问题描述】:

我有一个每天运行并在晚上自行关闭的市场数据 Spring Boot 应用程序。应用程序关闭后,我想处理记录的市场数据消息。为了做到这一点,我一直在寻找一种能够在关机而不是启动时翻转日志文件的方法,以便我的后处理应用程序具有读取文件的一致方式。

目前,我创建了一个自定义触发策略,并使用以下 logback 配置获取市场数据相关消息

@NoAutoStart
public class CustomTriggerPolicy<E> extends SizeAndTimeBasedFNATP<E> {
    @Override
    public boolean isTriggeringEvent(File activeFile, E event) {
        boolean triggeringEvent = super.isTriggeringEvent(activeFile, event);
        if(!triggeringEvent) {
            elapsedPeriodsFileName = getCurrentPeriodsFileNameWithoutCompressionSuffix();
        }
        return triggeringEvent;
    }
}

logback.xml

<configuration>
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook">
        <delay>60000</delay>
    </shutdownHook>
    <appender name="MKT-DATA-FIX-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/mkt-data-fix.log</file>
        <append>false</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%m%n</Pattern>
        </encoder>

        <!-- rollover daily -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/archived/foo/mkt-data-fix.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="com.mkt.data.CustomTriggerPolicy">
                <maxFileSize>100GB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>
</configuration>

SpringBootApplication.class(主类)

@PreDestroy
    public void shutdown() {
        manualLogRollover();
    }

    public void manualLogRollover() {
        try {
            Set<Appender<ILoggingEvent>> loggingAppenders = AppenderUtils.getLoggingAppenders();
            AppenderUtils.rolloverAppenders(loggingAppenders);
        } catch (Exception e) {
            log.warn("Log rollover failed", e);
        }
    }

AppenderUtils.java

public class AppenderUtils {
    public static Set<Appender<ILoggingEvent>> getLoggingAppenders() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        Set<Appender<ILoggingEvent>> fileLogAppenders = new HashSet<>();
        for(Logger logger : context.getLoggerList()) {
            for(Iterator<Appender<ILoggingEvent>> index = logger.iteratorForAppenders(); index.hasNext();) {
                Appender<ILoggingEvent> appender = index.next();
                if(appender instanceof RollingFileAppender) {
                    if(appender.getName().equals("MKT-DATA-FIX-LOG") ||
                            appender.getName().equals("MKT-DATA-FIX-EVENT-LOG")) {
                        fileLogAppenders.add(appender);
                    }
                }
            }
        }
        return fileLogAppenders;
    }

    public static void rolloverAppenders(Set<Appender<ILoggingEvent>> appenders) {
        for (Appender<ILoggingEvent> appender : appenders) {
            if (appender instanceof RollingFileAppender) {
                ((RollingFileAppender<ILoggingEvent>) appender).rollover();
            }
        }
    }
}

到目前为止,我已经做了以下事情;

  1. 尝试在没有使用 DelayingShutdownHook 的情况下在关机时手动翻转,这最终会创建一个 .tmp 文件并且没有完全完成翻转过程。
  2. 添加一个延迟 1 分钟的延迟关闭挂钩,但似乎 logback Compressor 的 asyncCompress 方法返回了一个 Future,它在 30 秒后超时,因此我收到以下错误
18:16:58,872 |-ERROR in c.q.l.core.rolling.TimeBasedRollingPolicy@398887205 - Timeout while waiting for compression job to finish java.util.concurrent.TimeoutException
        at java.util.concurrent.TimeoutException
        at      at java.util.concurrent.FutureTask.get(FutureTask.java:205)
        at      at ch.qos.logback.core.rolling.TimeBasedRollingPolicy.waitForAsynchronousJobToStop(TimeBasedRollingPolicy.java:137)
        at      at ch.qos.logback.core.rolling.TimeBasedRollingPolicy.stop(TimeBasedRollingPolicy.java:129)
        at      at ch.qos.logback.core.rolling.RollingFileAppender.stop(RollingFileAppender.java:152)
        at      at ch.qos.logback.core.spi.AppenderAttachableImpl.detachAndStopAllAppenders(AppenderAttachableImpl.java:107)
        at      at ch.qos.logback.classic.Logger.detachAndStopAllAppenders(Logger.java:206)
        at      at ch.qos.logback.classic.Logger.recursiveReset(Logger.java:331)iam
        at      at ch.qos.logback.classic.Logger.recursiveReset(Logger.java:338)
        at      at ch.qos.logback.classic.Logger.recursiveReset(Logger.java:338)
        at      at ch.qos.logback.classic.Logger.recursiveReset(Logger.java:338)
        at      at ch.qos.logback.classic.Logger.recursiveReset(Logger.java:338)
        at      at ch.qos.logback.classic.LoggerContext.reset(LoggerContext.java:223)
        at      at ch.qos.logback.classic.LoggerContext.stop(LoggerContext.java:348)
        at      at ch.qos.logback.core.hook.ShutdownHookBase.stop(ShutdownHookBase.java:39)
        at      at ch.qos.logback.core.hook.DelayingShutdownHook.run(DelayingShutdownHook.java:57)
        at      at java.lang.Thread.run(Thread.java:748)

我没有选择。有谁知道如何实现我在这里寻找的东西?

【问题讨论】:

    标签: java logback shutdown appender


    【解决方案1】:

    我想我明白了。我必须定义自己的触发和滚动策略来滚动附加程序并同步压缩文件。

    我不需要在我的主类中将翻转作为 @PreDestroy 的一部分。

    @NoAutoStart
    public class CustomTriggerPolicy<E> extends SizeAndTimeBasedFNATP<E> {
        @Override
        public boolean isTriggeringEvent(File activeFile, E event) {
            boolean triggeringEvent = super.isTriggeringEvent(activeFile, event);
            if(!triggeringEvent) {
                elapsedPeriodsFileName = getCurrentPeriodsFileNameWithoutCompressionSuffix();
            }
            return triggeringEvent;
        }
    }
    
    import ch.qos.logback.core.rolling.RolloverFailure;
    import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
    import ch.qos.logback.core.rolling.helper.*;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    
    /**
     * Custom rolling policy where we trigger rollback at shutdown.
     */
    public class MktDataRollingPolicy<E> extends TimeBasedRollingPolicy<E> {
    
        private static final String LOGBACK_STATUS_FILE_NAME = "logback-status.log";
        private Compressor compressor;
        private RenameUtil renameUtil = new RenameUtil();
        private String basePath;
        private String logbackStatusFilePath;
    
        FileNamePattern zipEntryFileNamePattern;
    
        @Override
        public void start() {
            basePath = System.getProperty("logback.statusLog", "/home/master/logs/mkt-data-log");
            logbackStatusFilePath = basePath + File.separator + LOGBACK_STATUS_FILE_NAME;
            super.start();
            // set the LR for our utility object
            renameUtil.setContext(this.context);
    
            compressor = new Compressor(compressionMode);
            compressor.setContext(context);
    
            if (compressionMode == CompressionMode.ZIP) {
                String zipEntryFileNamePatternStr = transformFileNamePattern2ZipEntry(fileNamePatternStr);
                zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);
            }
            Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHookRunnable()));
        }
    
        @Override
        public void rollover() throws RolloverFailure {
            String elapsedPeriodsFileName = getTimeBasedFileNamingAndTriggeringPolicy().getElapsedPeriodsFileName();
            if (elapsedPeriodsFileName != null) {
                if (compressionMode == CompressionMode.NONE) {
                    if (getParentsRawFileProperty() != null) {
                        renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
                    } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null }
                } else {
                    String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
                    if (getParentsRawFileProperty() == null) {
                        compressor.compress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem);
                    } else {
                        if (isLogFileEmpty()) {
                            addInfo("Skipping compression for empty file: " + getParentsRawFileProperty());
                            return;
                        }
                        renameRawAndCompress(elapsedPeriodsFileName, elapsedPeriodStem);
                    }
                }
            } else {
                String msg = String.format("ElapsedPeriodsFileName is null. Skipping rollover for file: %s",
                        getParentsRawFileProperty());
                addWarn(msg);
                File statusFile = new File(logbackStatusFilePath);
                try (BufferedWriter bw = new BufferedWriter(new FileWriter(statusFile, true))) {
                    bw.append(msg);
                    bw.append("\n");
                } catch (IOException e) {
                    System.out.println("Failed to write to status file: " + logbackStatusFilePath);
                    e.printStackTrace();
                }
            }
        }
    
    
        private boolean isLogFileEmpty() {
            File f = new File(getParentsRawFileProperty());
            return f.exists() && f.length() == 0;
        }
    
        private String transformFileNamePattern2ZipEntry(String fileNamePatternStr) {
            String slashified = FileFilterUtil.slashify(fileNamePatternStr);
            return FileFilterUtil.afterLastSlash(slashified);
        }
    
        void renameRawAndCompress(String nameOfCompressedFile, String innerEntryName) throws RolloverFailure {
            String parentsRawFile = getParentsRawFileProperty();
            String tmpTarget = nameOfCompressedFile + System.nanoTime() + ".tmp";
            renameUtil.rename(parentsRawFile, tmpTarget);
            compressor.compress(tmpTarget, nameOfCompressedFile, innerEntryName);
        }
    
        class ShutdownHookRunnable implements Runnable {
            @Override
            public void run() {
                try {
                    addInfo("Calling rollover inside shutdown hook");
                    rollover();
                } catch (Exception e) {
                    addError("Failed to rollover", e);
                }
            }
        }
    }
    

    logback.xml

    <configuration>
        <appender name="MKT-DATA-FIX-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_HOME}/mkt-data-fix.log</file>
            <append>false</append>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <Pattern>%m%n</Pattern>
            </encoder>
    
            <!-- rollover daily -->
            <rollingPolicy class="com.myapp.logback.MktDataRollingPolicy">
                <fileNamePattern>${LOG_HOME}/archived/foo/mkt-data-fix.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="com.mkt.data.CustomTriggerPolicy">
                    <maxFileSize>100GB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
        </appender>
    </configuration>
    

    【讨论】:

      猜你喜欢
      • 2011-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      • 2011-02-05
      • 1970-01-01
      • 2018-09-19
      • 2017-10-24
      相关资源
      最近更新 更多