Comprehensive logging framework module for Dropwizard applications providing configurable appenders, formatters, and logback integration
—
Asynchronous logging capabilities for high-throughput applications with configurable queue sizes and discard policies. Async logging improves application performance by moving log I/O operations to background threads.
Base interface for creating asynchronous appender wrappers that can wrap any synchronous appender in an async wrapper.
/**
* Interface for creating async appenders
*/
public interface AsyncAppenderFactory<E> {
/**
* Build an async appender wrapper
* @return configured AsyncAppenderBase instance
*/
AsyncAppenderBase<E> build();
}Default implementation that creates AsyncAppender instances specifically for ILoggingEvent objects.
/**
* Creates AsyncAppender instances for ILoggingEvent
*/
public class AsyncLoggingEventAppenderFactory implements AsyncAppenderFactory<ILoggingEvent> {
/**
* Build an AsyncAppender for logging events
* @return configured AsyncAppender instance
*/
@Override
public AsyncAppenderBase<ILoggingEvent> build();
}All appender factories support async configuration through the AbstractAppenderFactory base class:
public abstract class AbstractAppenderFactory<E> implements AppenderFactory<E> {
protected int queueSize = 256;
protected int discardingThreshold = -1;
protected boolean includeCallerData = false;
protected boolean neverBlock = false;
/**
* Set the async queue size
* @param queueSize the size of the async queue (default: 256)
*/
public void setQueueSize(int queueSize);
/**
* Get the async queue size
* @return the current queue size
*/
public int getQueueSize();
/**
* Set the async discarding threshold
* @param discardingThreshold events below this level may be discarded when queue is full
* (-1 disables discarding, default: -1)
*/
public void setDiscardingThreshold(int discardingThreshold);
/**
* Get the async discarding threshold
* @return the current discarding threshold
*/
public int getDiscardingThreshold();
/**
* Include caller data in async logging
* @param includeCallerData true to include caller information (impacts performance)
*/
public void setIncludeCallerData(boolean includeCallerData);
/**
* Check if caller data is included
* @return true if caller data is included
*/
public boolean isIncludeCallerData();
/**
* Set non-blocking behavior for async appenders
* @param neverBlock true to never block on full queue (may lose log events)
*/
public void setNeverBlock(boolean neverBlock);
/**
* Check if async appender never blocks
* @return true if configured to never block
*/
public boolean isNeverBlock();
/**
* Wrap appender in async wrapper if needed
* @param context the logger context
* @param asyncAppenderFactory factory for async appenders
* @param appender the appender to wrap
* @return the wrapped appender (or original if async not configured)
*/
protected Appender<E> wrapAsync(LoggerContext context,
AsyncAppenderFactory<E> asyncAppenderFactory,
Appender<E> appender);
}FileAppenderFactory<ILoggingEvent> fileAppender = new FileAppenderFactory<>();
fileAppender.setCurrentLogFilename("./logs/application.log");
// Configure async settings
fileAppender.setQueueSize(512); // Larger queue for high throughput
fileAppender.setIncludeCallerData(false); // Disable for better performance
fileAppender.setNeverBlock(false); // Block when queue is full (prevents loss)
fileAppender.setDiscardingThreshold(-1); // Never discard events
// Build with async wrapper
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
AsyncLoggingEventAppenderFactory asyncFactory = new AsyncLoggingEventAppenderFactory();
Appender<ILoggingEvent> appender = fileAppender.build(context, "MyApp",
new DropwizardLayoutFactory(), new ThresholdLevelFilterFactory(), asyncFactory);ConsoleAppenderFactory<ILoggingEvent> consoleAppender = new ConsoleAppenderFactory<>();
// High-throughput configuration
consoleAppender.setQueueSize(2048); // Very large queue
consoleAppender.setDiscardingThreshold(20); // Discard INFO and below when queue is 80% full
consoleAppender.setIncludeCallerData(false); // Disable caller data for performance
consoleAppender.setNeverBlock(true); // Never block application threads
// This configuration prioritizes application performance over log completenessFileAppenderFactory<ILoggingEvent> fileAppender = new FileAppenderFactory<>();
fileAppender.setCurrentLogFilename("./logs/application.log");
// Balanced configuration
fileAppender.setQueueSize(1024); // Reasonable queue size
fileAppender.setDiscardingThreshold(0); // Discard TRACE events when queue is full
fileAppender.setIncludeCallerData(true); // Include caller data for debugging
fileAppender.setNeverBlock(false); // Block to ensure important events are logged
// This configuration balances performance with log completenessThe async appender uses a bounded queue to buffer log events:
neverBlock setting
neverBlock = false: Application thread blocks until queue space is availableneverBlock = true: Events may be dropped when queue is fullWhen the queue reaches the discarding threshold percentage:
(queueSize * discardingThreshold) / 100-1 to disable discarding entirelyDiscarding Level Mapping:
// Default discarding behavior by level
if (discardingThreshold >= 0) {
// Events at TRACE, DEBUG levels may be discarded
// INFO, WARN, ERROR levels are preserved
}Including caller data has significant performance implications:
// High performance (recommended for production)
appender.setIncludeCallerData(false);
// Lower performance but includes method/line information
appender.setIncludeCallerData(true);When includeCallerData = true, each log event captures:
This information is expensive to obtain and should be disabled in high-throughput scenarios.
Async appenders must be properly shut down to flush remaining events:
// Proper shutdown
LoggingFactory loggingFactory = // ... your logging factory
loggingFactory.stop(); // Flushes async queues and stops background threads
// Or manually stop async appenders
AsyncAppenderBase<ILoggingEvent> asyncAppender = // ... your async appender
asyncAppender.stop(); // Waits for queue to drain and stops worker threadYou can monitor async appender performance through JMX or programmatically:
AsyncAppenderBase<ILoggingEvent> asyncAppender = // ... your async appender
// Check queue status
int queueSize = asyncAppender.getQueueSize();
int remainingCapacity = asyncAppender.getRemainingCapacity();
int numberOfElementsInQueue = queueSize - remainingCapacity;
// Monitor discard counts (if supported by implementation)
// Note: Dropwizard may expose these through metricsDropwizard Logging integrates with Dropwizard Metrics to provide async appender monitoring:
MetricRegistry metrics = new MetricRegistry();
loggingFactory.configure(metrics, "MyApplication");
// Metrics may include:
// - Queue depth
// - Discard counts
// - Processing rates
// - Error countsLog Events Not Appearing:
Application Blocking:
neverBlock settingMissing Caller Information:
includeCallerData is set to trueMemory Usage:
// Enable debug logging for async appenders
Logger asyncLogger = LoggerFactory.getLogger(AsyncAppender.class);
asyncLogger.setLevel(Level.DEBUG);
// This will show queue statistics and discard informationInstall with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-logging