//Dstl (c) Crown Copyright 2017 package uk.gov.dstl.baleen.core.logging.builders; import java.util.List; import java.util.Optional; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; import ch.qos.logback.core.util.FileSize; /** * Creates a logger which will output to a file, either a single file or one which is rotated based on size or time. * * */ public class BaleenFileLoggerBuilder extends AbstractBaleenLoggerBuilder { private Optional<Integer> maxNumberLogs; private Optional<Integer> maxSize; private String file; private boolean dailyLogFiles; /** * Create a new instance of the BaleenFileLoggerBuilder * * @param name The name of the logger * @param pattern The logging pattern, even though it isn't set explicitly by this builder it is passed to the super() which might set it * @param file The file to write to (absolute or relative path) * @param filter A filter, for example a MinMaxFilter, to apply to logging events * @param dailyLogFiles Should the log files be rotated each day? * @param maxSize What is the max size of file (in MB) before rotating? Defaults to no maximum * @param maxNumberLogs The max number of log files to keep. Defaults to 1 */ public BaleenFileLoggerBuilder(String name, String pattern, String file, Filter<ILoggingEvent> filter, boolean dailyLogFiles, Optional<Integer> maxSize, Optional<Integer> maxNumberLogs) { super(name, pattern, filter); this.file = file; this.dailyLogFiles = dailyLogFiles; this.maxSize = maxSize; this.maxNumberLogs = maxNumberLogs; } /** * Create a new instance of the BaleenFileLoggerBuilder * * @param name The name of the logger * @param pattern The logging pattern, even though it isn't set explicitly by this builder it is passed to the super() which might set it * @param file The file to write to (absolute or relative path) * @param filters A list of filters to apply to logging events * @param dailyLogFiles Should the log files be rotated each day? * @param maxSize What is the max size of file (in kb) before rotating? Defaults to no maximum * @param maxNumberLogs The max number of log files to keep. Defaults to 1 */ public BaleenFileLoggerBuilder(String name, String pattern, String file, List<Filter<ILoggingEvent>> filters, boolean dailyLogFiles, Optional<Integer> maxSize, Optional<Integer> maxNumberLogs) { super(name, pattern, filters); this.file = file; this.dailyLogFiles = dailyLogFiles; this.maxSize = maxSize; this.maxNumberLogs = maxNumberLogs; } private boolean isRolling() { return dailyLogFiles || divideBasedOnSize(); } private boolean divideBasedOnSize() { return maxSize.isPresent() && maxSize.get() >= 0; } private String getFileWithPattern(String pattern) { if (file.contains(pattern)) { return file; } else { return file + "." + pattern; } } private FileSize getMaxFileSize() { return FileSize.valueOf(maxSize.get() + "kb"); } /** * Build a new appender that will log to file for the specified context and encoder (where required). * * @param context The logger context * @param encoder The encoder to use (if that is possible for the specific appender) * @return The new file-based log appender */ @Override protected Appender<ILoggingEvent> createAppender(LoggerContext context, Encoder<ILoggingEvent> encoder) { if (!isRolling()) { //If the logging isn't configured to roll - i.e. we're not producing daily log files and there is no maximum size set FileAppender<ILoggingEvent> appender = new FileAppender<ILoggingEvent>(); appender.setFile(file); appender.setEncoder(encoder); return appender; } else { //If the logging is configured to roll RollingFileAppender<ILoggingEvent> appender; if (dailyLogFiles) { //If we're creating daily log files... appender = createDailyLogAppender(context, encoder); } else { //If it's not daily log files, then our rolling must be size based appender = createLogAppender(context, encoder); } return appender; } } /** * Create an appender that will create a new log each day * * @param context * @param encoder * @return An appender that matches the set up of the logger builder */ private RollingFileAppender<ILoggingEvent> createDailyLogAppender(LoggerContext context, Encoder<ILoggingEvent> encoder){ RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>(); appender.setEncoder(encoder); appender.setFile(file); TimeBasedRollingPolicy<ILoggingEvent> rolling = new TimeBasedRollingPolicy<>(); rolling.setContext(context); rolling.setParent(appender); rolling.setFileNamePattern(getFileWithPattern("%d")); //Set the maximum number of logs, either to the user specified setting or default to 1 if (maxNumberLogs.isPresent() && maxNumberLogs.get() >= 0) { rolling.setMaxHistory(maxNumberLogs.get()); } else { rolling.setMaxHistory(1); } //Do we need to also split files by size? if (divideBasedOnSize()) { SizeAndTimeBasedFNATP<ILoggingEvent> sizeBased = new SizeAndTimeBasedFNATP<>(); sizeBased.setContext(context); sizeBased.setMaxFileSize(getMaxFileSize()); sizeBased.setTimeBasedRollingPolicy(rolling); rolling.setTimeBasedFileNamingAndTriggeringPolicy(sizeBased); } rolling.start(); if(rolling.getTimeBasedFileNamingAndTriggeringPolicy() != null){ rolling.getTimeBasedFileNamingAndTriggeringPolicy().start(); } appender.setRollingPolicy(rolling); return appender; } /** * Create an appender that will roll over based on size * * @param context * @param encoder * @return An appender that matches the set up of the logger builder */ private RollingFileAppender<ILoggingEvent> createLogAppender(LoggerContext context, Encoder<ILoggingEvent> encoder){ RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>(); appender.setEncoder(encoder); appender.setFile(file); FixedWindowRollingPolicy rolling = new FixedWindowRollingPolicy(); rolling.setParent(appender); rolling.setContext(context); //Set the maximum number of logs, either to the user specified setting or default to 1 rolling.setMinIndex(1); if (maxNumberLogs.isPresent() && maxNumberLogs.get() >= 0) { rolling.setMaxIndex(maxNumberLogs.get()); } else { rolling.setMaxIndex(1); } rolling.setFileNamePattern(getFileWithPattern("%i")); //Configure size based rolling SizeBasedTriggeringPolicy<ILoggingEvent> trigger = new SizeBasedTriggeringPolicy<>(); trigger.setMaxFileSize(getMaxFileSize()); trigger.setContext(context); rolling.start(); trigger.start(); appender.setRollingPolicy(rolling); appender.setTriggeringPolicy(trigger); return appender; } }