package io.dropwizard.logging;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AsyncAppenderBase;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.LayoutBase;
import ch.qos.logback.core.pattern.PatternLayoutBase;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.logback.ThrottlingAppenderWrapper;
import io.dropwizard.logging.async.AsyncAppenderFactory;
import io.dropwizard.logging.filter.FilterFactory;
import io.dropwizard.logging.layout.DiscoverableLayoutFactory;
import io.dropwizard.logging.layout.LayoutFactory;
import io.dropwizard.util.Strings;
import io.dropwizard.util.Duration;
import io.dropwizard.validation.MaxDuration;
import io.dropwizard.validation.MinDuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
public abstract class AbstractAppenderFactory<E extends DeferredProcessingAware> implements AppenderFactory<E> {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAppenderFactory.class);
@NotNull
protected Level threshold = Level.ALL;
@Nullable
protected String logFormat;
@Nullable
protected DiscoverableLayoutFactory<E> layout;
@NotNull
protected TimeZone timeZone = TimeZone.getTimeZone("UTC");
@Min(1)
@Max(Integer.MAX_VALUE)
private int queueSize = AsyncAppenderBase.DEFAULT_QUEUE_SIZE;
private int discardingThreshold = -1;
@Nullable
@MinDuration(value = 0, unit = TimeUnit.SECONDS, inclusive = false)
@MaxDuration(value = 1, unit = TimeUnit.MINUTES)
private Duration messageRate;
private boolean includeCallerData = false;
private List<FilterFactory<E>> filterFactories = Collections.emptyList();
private boolean neverBlock = false;
@JsonProperty
public int getQueueSize() {
return queueSize;
}
@JsonProperty
public void setQueueSize(int queueSize) {
this.queueSize = queueSize;
}
@JsonProperty
public int getDiscardingThreshold() {
return discardingThreshold;
}
@JsonProperty
public void setDiscardingThreshold(int discardingThreshold) {
this.discardingThreshold = discardingThreshold;
}
@JsonProperty
@Nullable
public Duration getMessageRate() {
return messageRate;
}
@JsonProperty
public void setMessageRate(Duration messageRate) {
this.messageRate = messageRate;
}
@JsonProperty
public String getThreshold() {
return threshold.toString();
}
@JsonProperty
public void setThreshold(String threshold) {
this.threshold = DefaultLoggingFactory.toLevel(threshold);
}
@JsonProperty
@Nullable
public String getLogFormat() {
return logFormat;
}
@JsonProperty
public void setLogFormat(@Nullable String logFormat) {
this.logFormat = logFormat;
}
@JsonProperty
public TimeZone getTimeZone() {
return timeZone;
}
@JsonProperty
public void setTimeZone(String zoneId) {
this.timeZone = Strings.nullToEmpty(zoneId).equalsIgnoreCase("system") ? TimeZone.getDefault() :
TimeZone.getTimeZone(zoneId);
}
@JsonProperty
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
@JsonProperty
public boolean isIncludeCallerData() {
return includeCallerData;
}
@JsonProperty
public void setIncludeCallerData(boolean includeCallerData) {
this.includeCallerData = includeCallerData;
}
@JsonProperty
public List<FilterFactory<E>> getFilterFactories() {
return filterFactories;
}
@JsonProperty
public void setFilterFactories(List<FilterFactory<E>> appenders) {
this.filterFactories = new ArrayList<>(appenders);
}
@JsonProperty
public void setNeverBlock(boolean neverBlock) {
this.neverBlock = neverBlock;
}
@Nullable
public DiscoverableLayoutFactory<?> getLayout() {
return layout;
}
public void setLayout(@Nullable DiscoverableLayoutFactory<E> layout) {
this.layout = layout;
}
protected Appender<E> wrapAsync(Appender<E> appender, AsyncAppenderFactory<E> asyncAppenderFactory) {
return wrapAsync(appender, asyncAppenderFactory, appender.getContext());
}
protected Appender<E> wrapAsync(Appender<E> appender, AsyncAppenderFactory<E> asyncAppenderFactory, Context context) {
final AsyncAppenderBase<E> asyncAppender = asyncAppenderFactory.build();
if (asyncAppender instanceof AsyncAppender) {
((AsyncAppender) asyncAppender).setIncludeCallerData(includeCallerData);
}
asyncAppender.setQueueSize(queueSize);
asyncAppender.setDiscardingThreshold(discardingThreshold);
asyncAppender.setContext(context);
asyncAppender.setName("async-" + appender.getName());
asyncAppender.addAppender(appender);
asyncAppender.setNeverBlock(neverBlock);
asyncAppender.start();
if (messageRate == null) {
return asyncAppender;
} else {
return new ThrottlingAppenderWrapper<>(asyncAppender, messageRate.getQuantity(), messageRate.getUnit());
}
}
protected LayoutBase<E> buildLayout(LoggerContext context, LayoutFactory<E> defaultLayoutFactory) {
final LayoutBase<E> layoutBase;
if (layout == null) {
layoutBase = defaultLayoutFactory.build(context, timeZone);
} else {
layoutBase = layout.build(context, timeZone);
}
if (!Strings.isNullOrEmpty(logFormat)) {
if (layoutBase instanceof PatternLayoutBase) {
@SuppressWarnings("NullAway")
String logFormatWithTimeZone = logFormat.replace("%dwTimeZone", timeZone.getID());
((PatternLayoutBase<E>)layoutBase).setPattern(logFormatWithTimeZone);
} else {
LOGGER.warn("Ignoring 'logFormat', because 'layout' does not extend PatternLayoutBase");
}
}
layoutBase.start();
return layoutBase;
}
}