Reputation: 125
I'm trying to make a Custom Filter work in the custom-wide level as per the documentation.
I'm starting to think there's an issue with custom filters when used this way, given the default filters are being called correctly and the custom ones aren't. What I have is:
ThresholdFilterCustom.java (the same as ThresholdFilter, created to debug only):
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
@Plugin(name = "ThresholdFilterCustom", category = "Core", elementType = "filter", printObject = true)
public final class ThresholdFilterCustom extends AbstractFilter {
private final Level level;
private ThresholdFilterCustom(Level level, Result onMatch, Result onMismatch) {
super(onMatch, onMismatch);
this.level = level;
}
public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
return filter(level);
}
public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
return filter(level);
}
public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
return filter(level);
}
@Override
public Result filter(LogEvent event) {
return filter(event.getLevel());
}
private Result filter(Level level) {
// Look here! It should always match! not even filtering!
return onMatch;
}
@Override
public String toString() {
return level.toString();
}
/**
* Create a ThresholdFilterCustom.
* @param loggerLevel The log Level.
* @param match The action to take on a match.
* @param mismatch The action to take on a mismatch.
* @return The created ThresholdFilterCustom.
*/
@PluginFactory
public static ThresholdFilterCustom createFilter(@PluginAttribute(value = "level", defaultString = "ERROR") Level level,
@PluginAttribute(value = "onMatch", defaultString = "NEUTRAL") Result onMatch,
@PluginAttribute(value = "onMismatch", defaultString = "DENY") Result onMismatch) {
return new ThresholdFilterCustom(level, onMatch, onMismatch);
}
}
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Filters>
<ThresholdFilterCustom level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />
<!-- <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />-->
</Filters>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
If I uncomment
<!-- <ThresholdFilterCustom level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />-->
I don't see the message logged by logger.debug("logmessage") in the console
But if I uncomment
<!-- <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />-->
I do see the messages logged by logger.debug("logmessage"), even though the behavior between them should be identical. Why is this happening? Is it possible this is a log4j2 issue?
Upon using the debugger, I see the filter functions are not even called in the custom filter, but the custom filter is getting instantiated.
They are working correctly if I place them inside a logger. But they aren't working when placing them context-wide.
Upvotes: 1
Views: 2493
Reputation: 125
Answer is here.
For people that land in here: This got fixed here. In short, it seems that the javadoc of the methods is mixed up. To implement context-wide filters, the other methods should be overriden, the ones that are not filter(LogEvent event).
As per Ralph Goers comments:
The plugin is being properly constructed and is being called. However, you did not override all the filter methods. Most of them default to return NEUTRAL, which is what the method did in this case. It probably would make more sense to have AbstractFilter default to calling one of the other methods so that no so many methods need to be overridden.
His other answer:
When the global Filters are evaluated a LogEvent has not been constructed. So if you want to only support the global filter then you need to implement all the methods that don't take a LogEvent. Likewise, to use the Filter in other places you need to only implement the method that accepts a LogEvent. Yes, I think the Javadoc has it backwards.
What I mean with regard to the default methods is that there are 3 filter methods that explicitly return NEUTRAL.
public Result filter(final Logger logger, final Level level, final
Marker marker, final Message msg,final Throwable t);
public Result filter(final Logger logger, final Level level final Marker
marker, final Object msg,final Throwable t);
public Result filter(final Logger logger, final Level level, final Marker
marker, final String msg, final Object... params);
So all 3 of these need to be implemented In a Filter such as the one you created. All the other methods that accept a Logger end up calling the method above that accepts the varargs Object (i.e. an Object array) as the last parameter. It would make sense to me that the first 2 methods should also call the third method as the default instead of returning NEUTRAL. However, that would mean losing the explicit knowledge that one of the parameters was a Throwable, which I believe is why it is implemented as it is. I didn't mean that you need to do anything about this, except that right now you must implement all 3 methods above.
Upvotes: 4