package io.dropwizard.logging;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Logger;
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.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.util.FileSize;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import io.dropwizard.jackson.DiscoverableSubtypeResolver;
import io.dropwizard.logging.async.AsyncLoggingEventAppenderFactory;
import io.dropwizard.logging.filter.NullLevelFilterFactory;
import io.dropwizard.logging.layout.DropwizardLayoutFactory;
import io.dropwizard.util.Size;
import io.dropwizard.validation.BaseValidator;
import io.dropwizard.validation.ConstraintViolations;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.LoggerFactory;
import javax.validation.Validator;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import static org.assertj.core.api.Assertions.assertThat;
public class FileAppenderFactoryTest {
static {
BootstrapLogging.bootstrap();
}
private final Validator validator = BaseValidator.newValidator();
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void isDiscoverable() throws Exception {
assertThat(new DiscoverableSubtypeResolver().getDiscoveredSubtypes())
.contains(FileAppenderFactory.class);
}
@Test
public void includesCallerData() {
FileAppenderFactory<ILoggingEvent> fileAppenderFactory = new FileAppenderFactory<>();
fileAppenderFactory.setArchive(false);
AsyncAppender asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(asyncAppender.isIncludeCallerData()).isFalse();
fileAppenderFactory.setIncludeCallerData(true);
asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(asyncAppender.isIncludeCallerData()).isTrue();
}
@Test
public void isRolling() throws Exception {
// the method we want to test is protected, so we need to override it so we can see it
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory<ILoggingEvent>() {
@Override
public FileAppender<ILoggingEvent> buildAppender(LoggerContext context) {
return super.buildAppender(context);
}
};
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setArchive(true);
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d.log.gz").toString());
assertThat(fileAppenderFactory.buildAppender(new LoggerContext())).isInstanceOf(RollingFileAppender.class);
}
@Test
public void hasArchivedLogFilenamePattern() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors)
.containsOnly("must have archivedLogFilenamePattern if archive is true");
fileAppenderFactory.setArchive(false);
errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
}
@Test
public void isValidForInfiniteRolledFiles() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setArchivedFileCount(0);
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d.log.gz").toString());
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
assertThat(fileAppenderFactory.buildAppender(new LoggerContext())).isNotNull();
}
@Test
public void isValidForMaxFileSize() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setMaxFileSize(Size.kilobytes(1));
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d.log.gz").toString());
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors)
.containsOnly("when specifying maxFileSize, archivedLogFilenamePattern must contain %i");
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d-%i.log.gz").toString());
errors = ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
}
@Test
public void hasMaxFileSizeValidation() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%i.log.gz").toString());
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors)
.containsOnly("when archivedLogFilenamePattern contains %i, maxFileSize must be specified");
fileAppenderFactory.setMaxFileSize(Size.kilobytes(1));
errors = ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
}
@Test
public void testCurrentFileNameErrorWhenArchiveIsNotEnabled() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setArchive(false);
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors)
.containsOnly("currentLogFilename can only be null when archiving is enabled");
fileAppenderFactory.setCurrentLogFilename("test");
errors = ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
}
@Test
public void testCurrentFileNameCanBeNullWhenArchiveIsEnabled() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setArchive(true);
fileAppenderFactory.setArchivedLogFilenamePattern("name-to-be-used");
fileAppenderFactory.setCurrentLogFilename(null);
ImmutableList<String> errors =
ConstraintViolations.format(validator.validate(fileAppenderFactory));
assertThat(errors).isEmpty();
}
@Test
public void testCurrentLogFileNameIsEmptyAndAppenderUsesArchivedNameInstead() throws Exception {
final FileAppenderFactory<ILoggingEvent> appenderFactory = new FileAppenderFactory<>();
appenderFactory.setArchivedLogFilenamePattern(folder.newFile("test-archived-name-%d.log").toString());
final FileAppender<ILoggingEvent> rollingAppender = appenderFactory.buildAppender(new LoggerContext());
final String file = rollingAppender.getFile();
final String dateSuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYY-MM-dd"));
final String name = Files.getNameWithoutExtension(file);
Assert.assertEquals("test-archived-name-" + dateSuffix, name);
}
@Test
public void hasMaxFileSize() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setArchive(true);
fileAppenderFactory.setMaxFileSize(Size.kilobytes(1));
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d-%i.log.gz").toString());
RollingFileAppender<ILoggingEvent> appender = (RollingFileAppender<ILoggingEvent>) fileAppenderFactory.buildAppender(new LoggerContext());
assertThat(appender.getTriggeringPolicy()).isInstanceOf(DefaultTimeBasedFileNamingAndTriggeringPolicy.class);
final Field maxFileSizeField = SizeAndTimeBasedRollingPolicy.class.getDeclaredField("maxFileSize");
maxFileSizeField.setAccessible(true);
final FileSize maxFileSize = (FileSize) maxFileSizeField.get(appender.getRollingPolicy());
assertThat(maxFileSize.getSize()).isEqualTo(1024L);
}
@Test
public void hasMaxFileSizeFixedWindow() throws Exception {
FileAppenderFactory fileAppenderFactory = new FileAppenderFactory();
fileAppenderFactory.setCurrentLogFilename(folder.newFile("logfile.log").toString());
fileAppenderFactory.setArchive(true);
fileAppenderFactory.setMaxFileSize(Size.kilobytes(1));
fileAppenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%i.log.gz").toString());
RollingFileAppender<ILoggingEvent> appender = (RollingFileAppender<ILoggingEvent>) fileAppenderFactory.buildAppender(new LoggerContext());
assertThat(appender.getRollingPolicy()).isInstanceOf(FixedWindowRollingPolicy.class);
assertThat(appender.getRollingPolicy().isStarted()).isTrue();
assertThat(appender.getTriggeringPolicy()).isInstanceOf(SizeBasedTriggeringPolicy.class);
assertThat(appender.getTriggeringPolicy().isStarted()).isTrue();
final Field maxFileSizeField = SizeBasedTriggeringPolicy.class.getDeclaredField("maxFileSize");
maxFileSizeField.setAccessible(true);
final FileSize maxFileSize = (FileSize) maxFileSizeField.get(appender.getTriggeringPolicy());
assertThat(maxFileSize.getSize()).isEqualTo(1024L);
}
@Test
public void appenderContextIsSet() throws Exception {
final Logger root = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
final FileAppenderFactory<ILoggingEvent> appenderFactory = new FileAppenderFactory<>();
appenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d.log.gz").toString());
final Appender<ILoggingEvent> appender = appenderFactory.build(root.getLoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(appender.getContext()).isEqualTo(root.getLoggerContext());
}
@Test
public void appenderNameIsSet() throws Exception {
final Logger root = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
final FileAppenderFactory<ILoggingEvent> appenderFactory = new FileAppenderFactory<>();
appenderFactory.setArchivedLogFilenamePattern(folder.newFile("example-%d.log.gz").toString());
final Appender<ILoggingEvent> appender = appenderFactory.build(root.getLoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(appender.getName()).isEqualTo("async-file-appender");
}
@Test
public void isNeverBlock() throws Exception {
FileAppenderFactory<ILoggingEvent> fileAppenderFactory = new FileAppenderFactory<>();
fileAppenderFactory.setArchive(false);
fileAppenderFactory.setNeverBlock(true);
AsyncAppender asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(asyncAppender.isNeverBlock()).isTrue();
}
@Test
public void isNotNeverBlock() throws Exception {
FileAppenderFactory<ILoggingEvent> fileAppenderFactory = new FileAppenderFactory<>();
fileAppenderFactory.setArchive(false);
fileAppenderFactory.setNeverBlock(false);
AsyncAppender asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(asyncAppender.isNeverBlock()).isFalse();
}
@Test
public void defaultIsNotNeverBlock() throws Exception {
FileAppenderFactory<ILoggingEvent> fileAppenderFactory = new FileAppenderFactory<>();
fileAppenderFactory.setArchive(false);
// default neverBlock
AsyncAppender asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
assertThat(asyncAppender.isNeverBlock()).isFalse();
}
@Test
public void overrideBufferSize() throws NoSuchFieldException, IllegalAccessException {
FileAppenderFactory<ILoggingEvent> fileAppenderFactory = new FileAppenderFactory<>();
fileAppenderFactory.setArchive(false);
fileAppenderFactory.setBufferSize(Size.kilobytes(256));
AsyncAppender asyncAppender = (AsyncAppender) fileAppenderFactory.build(new LoggerContext(), "test", new DropwizardLayoutFactory(), new NullLevelFilterFactory<>(), new AsyncLoggingEventAppenderFactory());
final Appender<ILoggingEvent> fileAppender = asyncAppender.getAppender("file-appender");
assertThat(fileAppender).isInstanceOf(FileAppender.class);
final Field bufferSizeField = FileAppender.class.getDeclaredField("bufferSize");
bufferSizeField.setAccessible(true);
FileSize bufferSizeFromAppender = (FileSize) bufferSizeField.get(fileAppender);
assertThat(bufferSizeFromAppender.getSize()).isEqualTo(fileAppenderFactory.getBufferSize().toBytes());
}
}