Comprehensive logging framework module for Dropwizard applications providing configurable appenders, formatters, and logback integration
—
Configurable filter system for controlling which log events are processed by appenders with threshold and custom filtering capabilities. The filter system provides both level-based filtering and extensible custom filter support.
Base SPI interface for creating custom Logback filters with Jackson-based polymorphic configuration.
/**
* SPI for creating Logback Filter instances
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface FilterFactory<E> extends Discoverable {
/**
* Build a filter instance
* @return configured Filter instance
*/
Filter<E> build();
}Interface for creating level-based filters that filter events based on logging levels.
/**
* Interface for creating level-based filters
*/
public interface LevelFilterFactory<E> {
/**
* Build a level filter instance
* @param threshold the minimum level threshold
* @return configured Filter instance
*/
Filter<E> build(Level threshold);
}Default implementation that creates threshold filters for filtering events below a minimum log level.
/**
* Creates threshold filters for minimum log levels
*/
public class ThresholdLevelFilterFactory implements LevelFilterFactory<ILoggingEvent> {
/**
* Build a threshold filter that accepts events at or above the threshold level
* @param threshold the minimum level threshold
* @return ThresholdFilter configured with the specified level
*/
@Override
public Filter<ILoggingEvent> build(Level threshold);
}Usage Example:
ThresholdLevelFilterFactory filterFactory = new ThresholdLevelFilterFactory();
Filter<ILoggingEvent> filter = filterFactory.build(Level.WARN);
// The filter will accept WARN, ERROR levels and reject DEBUG, INFO levels
FilterReply reply1 = filter.decide(infoEvent); // Returns DENY
FilterReply reply2 = filter.decide(warnEvent); // Returns NEUTRAL (accept)
FilterReply reply3 = filter.decide(errorEvent); // Returns NEUTRAL (accept)No-op filter factory that creates filters which always return NEUTRAL, effectively passing all events through.
/**
* Creates no-op filters that always return NEUTRAL
*/
public class NullLevelFilterFactory<E> implements LevelFilterFactory<E> {
/**
* Build a no-op filter that accepts all events
* @param threshold ignored parameter
* @return Filter that always returns NEUTRAL
*/
@Override
public Filter<E> build(Level threshold);
}Usage Example:
NullLevelFilterFactory<ILoggingEvent> filterFactory = new NullLevelFilterFactory<>();
Filter<ILoggingEvent> filter = filterFactory.build(Level.DEBUG); // threshold ignored
// The filter will accept all events regardless of level
FilterReply reply = filter.decide(anyEvent); // Always returns NEUTRALFilters can be configured on appenders to control which events are processed. The AbstractAppenderFactory provides support for multiple filter configurations.
public abstract class AbstractAppenderFactory<E> implements AppenderFactory<E> {
protected List<FilterFactory<E>> filterFactories = new ArrayList<>();
/**
* Set additional filter factories
* @param filterFactories list of filter factories to apply
*/
public void setFilterFactories(List<FilterFactory<E>> filterFactories);
/**
* Get the configured filter factories
* @return list of filter factories
*/
public List<FilterFactory<E>> getFilterFactories();
/**
* Add a single filter factory
* @param filterFactory the filter factory to add
*/
public void addFilterFactory(FilterFactory<E> filterFactory);
}Usage Example:
// Create a console appender with custom filtering
ConsoleAppenderFactory<ILoggingEvent> consoleAppender = new ConsoleAppenderFactory<>();
// Add a threshold filter to only show WARN and above
ThresholdLevelFilterFactory thresholdFilter = new ThresholdLevelFilterFactory();
List<FilterFactory<ILoggingEvent>> filters = new ArrayList<>();
filters.add(thresholdFilter);
consoleAppender.setFilterFactories(filters);
consoleAppender.setThreshold(Level.WARN); // This also applies threshold filtering
// Build the appender
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Appender<ILoggingEvent> appender = consoleAppender.build(context, "MyApp",
new DropwizardLayoutFactory(), thresholdFilter, new AsyncLoggingEventAppenderFactory());You can create custom filters by implementing the FilterFactory interface:
/**
* Example custom filter that filters by logger name pattern
*/
@JsonTypeName("logger-pattern")
public class LoggerPatternFilterFactory implements FilterFactory<ILoggingEvent> {
private String pattern;
private boolean include = true;
public void setPattern(String pattern) {
this.pattern = pattern;
}
public void setInclude(boolean include) {
this.include = include;
}
@Override
public Filter<ILoggingEvent> build() {
return new Filter<ILoggingEvent>() {
private Pattern compiledPattern = Pattern.compile(pattern);
@Override
public FilterReply decide(ILoggingEvent event) {
boolean matches = compiledPattern.matcher(event.getLoggerName()).matches();
if (include) {
return matches ? FilterReply.NEUTRAL : FilterReply.DENY;
} else {
return matches ? FilterReply.DENY : FilterReply.NEUTRAL;
}
}
};
}
}Usage Example:
// Filter to only include database-related loggers
LoggerPatternFilterFactory dbFilter = new LoggerPatternFilterFactory();
dbFilter.setPattern(".*\\.db\\..*");
dbFilter.setInclude(true);
// Filter to exclude noisy loggers
LoggerPatternFilterFactory noiseFilter = new LoggerPatternFilterFactory();
noiseFilter.setPattern("com\\.noisy\\..*");
noiseFilter.setInclude(false);
// Apply filters to an appender
FileAppenderFactory<ILoggingEvent> fileAppender = new FileAppenderFactory<>();
fileAppender.setFilterFactories(Arrays.asList(dbFilter, noiseFilter));When multiple filters are configured on an appender, they are applied in order with the following logic:
If all filters return NEUTRAL, the event is accepted and logged.
Filter Chain Example:
// Create multiple filters
List<FilterFactory<ILoggingEvent>> filterChain = Arrays.asList(
createThresholdFilter(Level.DEBUG), // First: must be DEBUG or above
createLoggerPatternFilter("com.app.*"), // Second: must match logger pattern
createCustomBusinessFilter() // Third: custom business logic
);
ConsoleAppenderFactory<ILoggingEvent> appender = new ConsoleAppenderFactory<>();
appender.setFilterFactories(filterChain);
// Event processing:
// 1. ThresholdFilter: if level < DEBUG -> DENY (stop)
// 2. LoggerPatternFilter: if logger doesn't match -> DENY (stop)
// 3. CustomBusinessFilter: apply business logic -> ACCEPT/DENY/NEUTRAL
// 4. If all return NEUTRAL -> event is loggedYou can also wrap standard Logback filters in FilterFactory implementations:
Example wrapping a RegexFilter:
@JsonTypeName("regex")
public class RegexFilterFactory implements FilterFactory<ILoggingEvent> {
private String regex;
private FilterReply onMatch = FilterReply.NEUTRAL;
private FilterReply onMismatch = FilterReply.DENY;
@Override
public Filter<ILoggingEvent> build() {
ch.qos.logback.core.filter.Filter<ILoggingEvent> regexFilter =
new ch.qos.logback.core.filter.Filter<ILoggingEvent>() {
private Pattern pattern = Pattern.compile(regex);
@Override
public FilterReply decide(ILoggingEvent event) {
String message = event.getFormattedMessage();
return pattern.matcher(message).find() ? onMatch : onMismatch;
}
};
return regexFilter;
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-logging