I work with LogBack and I have several Loggers. I have created a Custom Appender:
public class LogListenerAppender extends AppenderBase<ILoggingEvent> {
private List<LogListener> listeners;
public LogListenerAppender() {
listeners = new ArrayList<>();
}
public void addListener(LogListener listener){
listeners.add(listener);
System.out.println("Current listener: " + listeners.size());
}
/**
* Send the LogEvent to all Listeners
* @param eventObject the LogEventObject
*/
@Override
protected void append(ILoggingEvent eventObject) {
for(LogListener listener : listeners){
listener.receiveLogMessage(eventObject);
}
}
}
This appender is to add listeners to the Logger to catch Log messages.
Now in my logback.xml file, I have created the Appender as LISTENER
<!--Custom Listener Appender-->
<appender name="LISTENER" class="path.to.LogListenerAppender"/>
And some Logger:
<logger name="TestLogger">
<appender-ref ref="LISTENER" />
</logger>
<logger name="MainLogger">
<appender-ref ref="LISTENER" />
</logger>
In the code I add LogListener to the Logger:
public static void main(String[] args){
Logger testLogger = LoggerFactory.getLogger("TestLogger");
Logger mainLogger = LoggerFactory.getLogger("MainLogger");
addListenerToLogger(testLogger, new LogListener(Level.TRACE) {
@Override
public void log(String message, long timestamp) {
System.out.println("TEST LOG: " + message);
}
});
addListenerToLogger(mainLogger, new LogListener(Level.TRACE) {
@Override
public void log(String message, long timestamp) {
System.out.println("MAIN LOG: " + message);
}
});
testLogger.info("Hello");
}
private static void addListenerToLogger(Logger logger, LogListener loglistener){
ch.qos.logback.classic.Logger log = (ch.qos.logback.classic.Logger) logger;
LogListenerAppender appender = (LogListenerAppender)log.getAppender("LISTENER");
appender.addListener(loglistener);
}
The desired output is:
TEST LOG: Hello
But the output is:
TEST LOG: Hello
MAIN LOG: Hello
And the System.out.println("Current listener: " + listeners.size());
in the LogListenerAppender prints 2.
My problem now is that Logback uses the same instance of LogListenerAppender for all Logger who uses <appender-ref ref="LISTENER"/>
.
But I need for every Logger a new LogListenerAppender. How can I configure logBack that he creates every time a new Instance?
My idea is to create appender for every logger like:
<appender name="LISTENER1" class="path.to.LogListenerAppender"/>
<appender name="LISTENER2" class="path.to.LogListenerAppender"/>
//etc...
<logger name="TestLogger">
<appender-ref ref="LISTENER1" />
</logger>
<logger name="MainLogger">
<appender-ref ref="LISTENER2" />
</logger>
//etc...
But I hope it exists an easier way
You can see why this is occuring in your code here:
LogListenerAppender appender = (LogListenerAppender)log.getAppender("LISTENER");
Your code just gets whatever appender you've created in memory tagged with reference "LISTENER". Everything in that list of listeners will listen for any event from an appender tagged with listener.
Perhaps try adding a String to your add listener method like so:
private static void addListenerToLogger(Logger logger, LogListener loglistener, String appenderRef){
ch.qos.logback.classic.Logger log = (ch.qos.logback.classic.Logger) logger;
LogListenerAppender appender = (LogListenerAppender)log.getAppender(appenderRef);
appender.addListener(loglistener);
}
This way you can pass the appropriate appender ref (i.e. "LISTENER1") to the method to retrieve the appropriate appenders.
It would be worthwhile to chose appropriate refs as well i.e.;
<logger name="TestLogger">
<appender-ref ref="TEST" />
</logger>
<logger name="MainLogger">
<appender-ref ref="MAIN" />
</logger>
For the sake of readability and maintainability, but that's a style choice more than a technical decision