package com.codahale.dropwizard.logging;

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.spi.FilterAttachable;
import com.codahale.dropwizard.validation.ValidationMethod;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Strings;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.TimeZone;

/**
 * A base implementation of {@link AppenderFactory}.
 */
public abstract class AbstractAppenderFactory implements AppenderFactory {
    @NotNull
    protected Level threshold = Level.ALL;

    protected boolean async = false;

    protected boolean calleeData = false;

    protected boolean discarding = false;

    @Min(1)
    @Max(Integer.MAX_VALUE)
    protected int asyncQueueLength = 10000;

    protected String logFormat;

    /**
     * Returns the lowest level of events to print to the console.
     */
    @JsonProperty
    public Level getThreshold() {
        return threshold;
    }

    /**
     * Sets the lowest level of events to print to the console.
     */
    @JsonProperty
    public void setThreshold(Level threshold) {
        this.threshold = threshold;
    }

    /**
     * Returns the Logback pattern with which events will be formatted.
     */
    @JsonProperty
    public String getLogFormat() {
        return logFormat;
    }

    /**
     * Sets the Logback pattern with which events will be formatted.
     *
     * @see <a href="http://logback.qos.ch/manual/layouts.html#conversionWord">the Logback documentation</a>
     */
    @JsonProperty
    public void setLogFormat(String logFormat) {
        this.logFormat = logFormat;
    }

    /**
     * Returns true if the appender is asynchronous
     */
    public boolean isAsync() {
        return async;
    }

    /**
     * Sets if the appender will be created as an async appender.
     */
    @JsonProperty
    public void setAsync(boolean async) {
        this.async = async;
    }

    /**
     * Returns true if the async appender retains full callee data
     */
    public boolean isCalleeData() {
        return calleeData;
    }

    /**
     * Sets if the appender retains full callee data, this can be expensive
     *
     * @see <a href="http://logback.qos.ch/manual/appenders.html#asyncIncludeCallerData">the Logback documentation</a>
     */
    @JsonProperty
    public void setCalleeData(boolean calleeData) {
        this.calleeData = calleeData;
    }

    /**
     * Returns true if the appender is asynchronous and, in cases where the log queue is overflowing starts dropping new
     * events of level TRACE, DEBUG and INFO
     */
    public boolean isDiscarding() {
        return discarding;
    }

    /**
     * Sets the behaviour of the appender to be asynchronous and, in cases where the log queue
     * is overflowing starts dropping new events of level TRACE, DEBUG and INFO
     */
    @JsonProperty
    public void setDiscarding(boolean discarding) {
        this.discarding = discarding;
    }

    /**
     * If the appender is an async appender, return the queue length, if not return -1
     */
    public int getAsyncQueueLength() {
        return this.async ? asyncQueueLength : -1;
    }

    /**
     * Sets the queue length for async loggers
     */
    @JsonProperty
    public void setAsyncQueueLength(int asyncQueueLength) {
        this.asyncQueueLength = asyncQueueLength;
    }

    protected Appender<ILoggingEvent> wrapAppenderAsAsyncIfNecessary(Appender<ILoggingEvent> delegate) {
        if (this.async) {
            AsyncAppender asyncAppender = new AsyncAppender();
            asyncAppender.addAppender(delegate);
            asyncAppender.setQueueSize(this.asyncQueueLength);
            asyncAppender.setDiscardingThreshold(this.discarding ? 5 : 0);
            asyncAppender.setIncludeCallerData(this.calleeData);
            return asyncAppender;
        } else {
            return delegate;
        }
    }

    protected void addThresholdFilter(FilterAttachable<ILoggingEvent> appender, Level threshold) {
        final ThresholdFilter filter = new ThresholdFilter();
        filter.setLevel(threshold.toString());
        filter.start();
        appender.addFilter(filter);
    }

    protected DropwizardLayout buildLayout(LoggerContext context, TimeZone timeZone) {
        final DropwizardLayout formatter = new DropwizardLayout(context, timeZone);
        if (!Strings.isNullOrEmpty(logFormat)) {
            formatter.setPattern(logFormat);
        }
        formatter.start();
        return formatter;
    }

    @ValidationMethod(message="Async logging parameters specified for appender, but appender not configured to be async!")
    @SuppressWarnings("UnusedDeclaration")
    private boolean isValidConfiguration() {
        if (isDiscarding() || isCalleeData()) {
            return isAsync();
        } else {
            return true;
        }
    }

}

@@ -796,16 +796,336 @@
 ctory %7B%0A
+%0A    // The ratio in logback at which a discarding appender starts dropping messages%0A    // in essence it aims to keep 20%25 of the queue for growth for WARN and ERROR messages%0A    private static final int DISCARD_20_PCT_HEADROOM = 5;%0A%0A    // Dont discard any messages ever%0A    private static final int DONT_DISCARD = 0;%0A%0A
     @Not
@@ -2317,32 +2317,50 @@
 hronous%0A     */%0A
+    @JsonProperty%0A
     public boole
@@ -2655,32 +2655,50 @@
 ee data%0A     */%0A
+    @JsonProperty%0A
     public boole
@@ -3258,32 +3258,50 @@
 nd INFO%0A     */%0A
+    @JsonProperty%0A
     public boole
@@ -3759,24 +3759,42 @@
  -1%0A     */%0A
+    @JsonProperty%0A
     public i
@@ -4453,13 +4453,46 @@
 g ? 
-5 : 0
+DISCARD_20_PCT_HEADROOM : DONT_DISCARD
 );%0A 
@@ -5481,19 +5481,24 @@
         
-if 
+return !
 (isDisca
@@ -5528,91 +5528,21 @@
 ()) 
-%7B%0A            return isAsync();%0A        %7D else %7B%0A            return true;%0A        %7D
+%7C%7C isAsync();
 %0A   

Change the async logging to accomodate code review comments