io.dropwizard.logging.FileAppenderFactory Maven / Gradle / Ivy
package io.dropwizard.logging;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
import ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.dropwizard.logging.async.AsyncAppenderFactory;
import io.dropwizard.logging.filter.LevelFilterFactory;
import io.dropwizard.logging.layout.LayoutFactory;
import io.dropwizard.util.Size;
import io.dropwizard.validation.ValidationMethod;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**
* An {@link AppenderFactory} implementation which provides an appender that writes events to a file, archiving older
* files as it goes.
*
* Configuration Parameters:
*
*
* Name
* Default
* Description
*
*
* {@code type}
* REQUIRED
* The appender type. Must be {@code file}.
*
*
* {@code threshold}
* {@code ALL}
* The lowest level of events to write to the file.
*
*
* {@code currentLogFilename}
* REQUIRED
* The filename where current events are logged.
*
*
* {@code archive}
* {@code true}
* Whether or not to archive old events in separate files.
*
*
* {@code archivedLogFilenamePattern}
* REQUIRED if {@code archive} is {@code true}.
*
* The filename pattern for archived files. {@code %d} is replaced with the date in {@code yyyy-MM-dd} form,
* and the fact that it ends with {@code .gz} indicates the file will be gzipped as it's archived. Likewise,
* filename patterns which end in {@code .zip} will be filled as they are archived.
*
*
*
* {@code archivedFileCount}
* {@code 5}
*
* The number of archived files to keep. Must be greater than {@code 0}.
*
*
*
* {@code maxFileSize}
* (unlimited)
*
* The maximum size of the currently active file before a rollover is triggered. The value can be expressed
* in bytes, kilobytes, megabytes, gigabytes, and terabytes by appending B, K, MB, GB, or TB to the
* numeric value. Examples include 100MB, 1GB, 1TB. Sizes can also be spelled out, such as 100 megabytes,
* 1 gigabyte, 1 terabyte.
*
*
*
* {@code timeZone}
* {@code UTC}
* The time zone to which event timestamps will be converted.
*
*
* {@code logFormat}
* the default format
*
* The Logback pattern with which events will be formatted. See
* the Logback documentation
* for details.
*
*
*
*
* @see AbstractAppenderFactory
*/
@JsonTypeName("file")
public class FileAppenderFactory extends AbstractAppenderFactory {
@NotNull
private String currentLogFilename;
private boolean archive = true;
private String archivedLogFilenamePattern;
@Min(1)
private int archivedFileCount = 5;
private Size maxFileSize;
@JsonProperty
public String getCurrentLogFilename() {
return currentLogFilename;
}
@JsonProperty
public void setCurrentLogFilename(String currentLogFilename) {
this.currentLogFilename = currentLogFilename;
}
@JsonProperty
public boolean isArchive() {
return archive;
}
@JsonProperty
public void setArchive(boolean archive) {
this.archive = archive;
}
@JsonProperty
public String getArchivedLogFilenamePattern() {
return archivedLogFilenamePattern;
}
@JsonProperty
public void setArchivedLogFilenamePattern(String archivedLogFilenamePattern) {
this.archivedLogFilenamePattern = archivedLogFilenamePattern;
}
@JsonProperty
public int getArchivedFileCount() {
return archivedFileCount;
}
@JsonProperty
public void setArchivedFileCount(int archivedFileCount) {
this.archivedFileCount = archivedFileCount;
}
@JsonProperty
public Size getMaxFileSize() {
return maxFileSize;
}
@JsonProperty
public void setMaxFileSize(Size maxFileSize) {
this.maxFileSize = maxFileSize;
}
@JsonIgnore
@ValidationMethod(message = "must have archivedLogFilenamePattern if archive is true")
public boolean isValidArchiveConfiguration() {
return !archive || (archivedLogFilenamePattern != null);
}
@JsonIgnore
@ValidationMethod(message = "when specifying maxFileSize, archivedLogFilenamePattern must contain %i")
public boolean isValidForMaxFileSizeSetting() {
return !archive || maxFileSize == null ||
(archivedLogFilenamePattern != null && archivedLogFilenamePattern.contains("%i"));
}
@JsonIgnore
@ValidationMethod(message = "when archivedLogFilenamePattern contains %i, maxFileSize must be specified")
public boolean isMaxFileSizeSettingSpecified() {
return !archive || !(archivedLogFilenamePattern != null && archivedLogFilenamePattern.contains("%i")) ||
maxFileSize != null;
}
@Override
public Appender build(LoggerContext context, String applicationName, LayoutFactory layoutFactory,
LevelFilterFactory levelFilterFactory, AsyncAppenderFactory asyncAppenderFactory) {
final FileAppender appender = buildAppender(context);
appender.setName("file-appender");
appender.setAppend(true);
appender.setContext(context);
final LayoutWrappingEncoder layoutEncoder = new LayoutWrappingEncoder<>();
layoutEncoder.setLayout(buildLayout(context, layoutFactory));
appender.setEncoder(layoutEncoder);
appender.setPrudent(false);
appender.addFilter(levelFilterFactory.build(threshold));
getFilterFactories().stream().forEach(f -> appender.addFilter(f.build()));
appender.stop();
appender.start();
return wrapAsync(appender, asyncAppenderFactory);
}
protected FileAppender buildAppender(LoggerContext context) {
if (archive) {
final RollingFileAppender appender = new RollingFileAppender<>();
appender.setFile(currentLogFilename);
if (maxFileSize != null && !archivedLogFilenamePattern.contains("%d")) {
final FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
final SizeBasedTriggeringPolicy triggeringPolicy = new SizeBasedTriggeringPolicy<>();
triggeringPolicy.setMaxFileSize(String.valueOf(maxFileSize.toBytes()));
triggeringPolicy.setContext(context);
rollingPolicy.setContext(context);
rollingPolicy.setMaxIndex(getArchivedFileCount());
rollingPolicy.setFileNamePattern(getArchivedLogFilenamePattern());
appender.setRollingPolicy(rollingPolicy);
appender.setTriggeringPolicy(triggeringPolicy);
rollingPolicy.setParent(appender);
rollingPolicy.start();
return appender;
} else {
final TimeBasedFileNamingAndTriggeringPolicy triggeringPolicy;
if (maxFileSize == null) {
triggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy<>();
} else {
final SizeAndTimeBasedFNATP maxFileSizeTriggeringPolicy = new SizeAndTimeBasedFNATP<>();
maxFileSizeTriggeringPolicy.setMaxFileSize(String.valueOf(maxFileSize.toBytes()));
triggeringPolicy = maxFileSizeTriggeringPolicy;
}
triggeringPolicy.setContext(context);
final TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(context);
rollingPolicy.setFileNamePattern(archivedLogFilenamePattern);
rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(
triggeringPolicy);
triggeringPolicy.setTimeBasedRollingPolicy(rollingPolicy);
rollingPolicy.setMaxHistory(archivedFileCount);
appender.setRollingPolicy(rollingPolicy);
appender.setTriggeringPolicy(triggeringPolicy);
rollingPolicy.setParent(appender);
rollingPolicy.start();
return appender;
}
}
final FileAppender appender = new FileAppender<>();
appender.setFile(currentLogFilename);
return appender;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy