ContextSelector and getLogger

Hello, I currently try to get SLF4J and Logback to work on IBM Lotus Domino Server 8.5. It's working. We need to do logging an a per Thread basis because we log into a Domino database. The Java object of a Domino database is based on a session object, and that can not exist over more then one thread. Okay, I created a simple ContextSelector: package de.pentos.domino.logging; import java.util.Arrays; import java.util.List; import ch.qos.logback.classic.LoggerContext; /** * @author Martin Burchard */ public class ContextSelector implements ch.qos.logback.classic.selector.ContextSelector { private final LoggerContext defaultContext; private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>(); public ContextSelector(final LoggerContext context) { defaultContext = context; } public LoggerContext detachLoggerContext(final String loggerContextName) { return getLoggerContext(); } public List<String> getContextNames() { return Arrays.asList(getLoggerContext().getName()); } public LoggerContext getDefaultLoggerContext() { return defaultContext; } public LoggerContext getLoggerContext() { LoggerContext lc = threadLocal.get(); if (lc == null) { threadLocal.set(new LoggerContext()); } return threadLocal.get(); } public LoggerContext getLoggerContext(final String name) { if (getLoggerContext().getName().equals(name)) { return getLoggerContext(); } return null; } } As long as all Loggers are used like this: private Logger log = LoggerFactory.getLogger(DemoWebService.class); everything works fine, I get my logging. But also this is a common way to get my Logger: private static final Logger log = LoggerFactory.getLogger(DemoWebService.class); Because for WebServices and Servlets the VM is not terminated and these Logger Instance will live a long time without ever get refreshed. After the first run I don't see logging any more. How can I deal with external JARs like Apache HTTP Client and other that instantiate the loggers in a static way? Regards, Martin -- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22352655.html Sent from the Logback User mailing list archive at Nabble.com.

Hello Martin, Could you please describe your use case in your terms without assuming the use of a ContextSelector? Martin Burchard wrote:
Hello,
I currently try to get SLF4J and Logback to work on IBM Lotus Domino Server 8.5. It's working. We need to do logging an a per Thread basis because we log into a Domino database. The Java object of a Domino database is based on a session object, and that can not exist over more then one thread. Okay, I created a simple ContextSelector:
package de.pentos.domino.logging;
import java.util.Arrays; import java.util.List;
import ch.qos.logback.classic.LoggerContext;
/** * @author Martin Burchard */ public class ContextSelector implements ch.qos.logback.classic.selector.ContextSelector {
private final LoggerContext defaultContext; private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>();
public ContextSelector(final LoggerContext context) { defaultContext = context; }
public LoggerContext detachLoggerContext(final String loggerContextName) { return getLoggerContext(); }
public List<String> getContextNames() { return Arrays.asList(getLoggerContext().getName()); }
public LoggerContext getDefaultLoggerContext() { return defaultContext; }
public LoggerContext getLoggerContext() { LoggerContext lc = threadLocal.get(); if (lc == null) { threadLocal.set(new LoggerContext()); } return threadLocal.get(); }
public LoggerContext getLoggerContext(final String name) { if (getLoggerContext().getName().equals(name)) { return getLoggerContext(); } return null; }
}
As long as all Loggers are used like this:
private Logger log = LoggerFactory.getLogger(DemoWebService.class);
everything works fine, I get my logging.
But also this is a common way to get my Logger:
private static final Logger log = LoggerFactory.getLogger(DemoWebService.class);
Because for WebServices and Servlets the VM is not terminated and these Logger Instance will live a long time without ever get refreshed. After the first run I don't see logging any more.
How can I deal with external JARs like Apache HTTP Client and other that instantiate the loggers in a static way?
Regards, Martin
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

Hello Ceki, Java on Domino means, you are working with 3 types of processes Agents: Every Agent has it's on VM, It has it's own Session, it could deal with static loggers. WebServices: Every Request has it's own Thread with it's own Session, but the VM remains open until the Server shuts down Servlets: Every Request has it's own Thread with it's own Session, but the VM remains open until the Server shuts down As I said we want to log into a database. To log into a database we need a session. A session is available for a Thread. This means for WebServices and Servlets, that we need a way to find the correct DatabaseAppender for the corresponding thread with the corresponding session from static Loggers. That's why I thought about the ContextSelector. _______________________________ Martin Burchard Tel: +49 89 54493730 Fax: +49 89 54493737 Mob: +49-(0)172-8520299 Skype: nabor_gilgalad martin.burchard@pentos.com _______________________________ _______________________________ Pentos AG Landsberger Straße 6 80339 München Tel: +49 89 54493730 Fax: +49 89 54493737 www.pentos.com _______________________________ From: Ceki Gulcu <ceki@qos.ch> To: logback users list <logback-user@qos.ch> Date: 05.03.2009 15:32 Subject: Re: [logback-user] ContextSelector and getLogger Sent by: logback-user-bounces@qos.ch Hello Martin, Could you please describe your use case in your terms without assuming the use of a ContextSelector? Martin Burchard wrote:
Hello,
I currently try to get SLF4J and Logback to work on IBM Lotus Domino Server 8.5. It's working. We need to do logging an a per Thread basis because we log into a Domino database. The Java object of a Domino database is based on a session object, and that can not exist over more then one thread. Okay, I created a simple ContextSelector:
package de.pentos.domino.logging;
import java.util.Arrays; import java.util.List;
import ch.qos.logback.classic.LoggerContext;
/** * @author Martin Burchard */ public class ContextSelector implements ch.qos.logback.classic.selector.ContextSelector {
private final LoggerContext defaultContext; private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>();
public ContextSelector(final LoggerContext context) { defaultContext = context; }
public LoggerContext detachLoggerContext(final String loggerContextName) { return getLoggerContext(); }
public List<String> getContextNames() { return Arrays.asList(getLoggerContext().getName()); }
public LoggerContext getDefaultLoggerContext() { return defaultContext; }
public LoggerContext getLoggerContext() { LoggerContext lc = threadLocal.get(); if (lc == null) { threadLocal.set(new LoggerContext()); } return threadLocal.get(); }
public LoggerContext getLoggerContext(final String name) { if (getLoggerContext().getName().equals(name)) { return getLoggerContext(); } return null; }
}
As long as all Loggers are used like this:
private Logger log = LoggerFactory.getLogger(DemoWebService.class);
everything works fine, I get my logging.
But also this is a common way to get my Logger:
private static final Logger log = LoggerFactory.getLogger(DemoWebService.class);
Because for WebServices and Servlets the VM is not terminated and these Logger Instance will live a long time without ever get refreshed. After the first run I don't see logging any more.
How can I deal with external JARs like Apache HTTP Client and other that instantiate the loggers in a static way?
Regards, Martin
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user

Have you considered not bothering with the thread local, logging in the normal static way and filtering the output on the basis of the thread name using a logback filter? Or even on the basis of the session's ID, made visible to logback via an MDC value? Then all 3rd party library logging statements would be handled as well. ----- Original Message ----- From: "Martin Burchard" <martin.burchard@pentos.com> To: logback-user@qos.ch Sent: Thursday, 5 March, 2009 14:26:41 GMT +00:00 GMT Britain, Ireland, Portugal Subject: [logback-user] ContextSelector and getLogger Hello, I currently try to get SLF4J and Logback to work on IBM Lotus Domino Server 8.5. It's working. We need to do logging an a per Thread basis because we log into a Domino database. The Java object of a Domino database is based on a session object, and that can not exist over more then one thread. Okay, I created a simple ContextSelector: package de.pentos.domino.logging; import java.util.Arrays; import java.util.List; import ch.qos.logback.classic.LoggerContext; /** * @author Martin Burchard */ public class ContextSelector implements ch.qos.logback.classic.selector.ContextSelector { private final LoggerContext defaultContext; private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>(); public ContextSelector(final LoggerContext context) { defaultContext = context; } public LoggerContext detachLoggerContext(final String loggerContextName) { return getLoggerContext(); } public List<String> getContextNames() { return Arrays.asList(getLoggerContext().getName()); } public LoggerContext getDefaultLoggerContext() { return defaultContext; } public LoggerContext getLoggerContext() { LoggerContext lc = threadLocal.get(); if (lc == null) { threadLocal.set(new LoggerContext()); } return threadLocal.get(); } public LoggerContext getLoggerContext(final String name) { if (getLoggerContext().getName().equals(name)) { return getLoggerContext(); } return null; } } As long as all Loggers are used like this: private Logger log = LoggerFactory.getLogger(DemoWebService.class); everything works fine, I get my logging. But also this is a common way to get my Logger: private static final Logger log = LoggerFactory.getLogger(DemoWebService.class); Because for WebServices and Servlets the VM is not terminated and these Logger Instance will live a long time without ever get refreshed. After the first run I don't see logging any more. How can I deal with external JARs like Apache HTTP Client and other that instantiate the loggers in a static way? Regards, Martin -- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22352655.html Sent from the Logback User mailing list archive at Nabble.com. _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user

Hello Mahoney, I read about MDC and Filter but I don't have a clue how to find the non static session object and the non static database object to write the log into the right database object. The only other way, without using the ContextSelector is to create an Appender that tries to find the corresponding Session with ThreadLocal access. But I think that this wont be very fast... Mahoney wrote:
Have you considered not bothering with the thread local, logging in the normal static way and filtering the output on the basis of the thread name using a logback filter? Or even on the basis of the session's ID, made visible to logback via an MDC value?
Then all 3rd party library logging statements would be handled as well.
----- Original Message ----- From: "Martin Burchard" <martin.burchard@pentos.com> To: logback-user@qos.ch Sent: Thursday, 5 March, 2009 14:26:41 GMT +00:00 GMT Britain, Ireland, Portugal Subject: [logback-user] ContextSelector and getLogger
Hello,
I currently try to get SLF4J and Logback to work on IBM Lotus Domino Server 8.5. It's working. We need to do logging an a per Thread basis because we log into a Domino database. The Java object of a Domino database is based on a session object, and that can not exist over more then one thread. Okay, I created a simple ContextSelector:
package de.pentos.domino.logging;
import java.util.Arrays; import java.util.List;
import ch.qos.logback.classic.LoggerContext;
/** * @author Martin Burchard */ public class ContextSelector implements ch.qos.logback.classic.selector.ContextSelector {
private final LoggerContext defaultContext; private static final ThreadLocal<LoggerContext> threadLocal = new ThreadLocal<LoggerContext>();
public ContextSelector(final LoggerContext context) { defaultContext = context; }
public LoggerContext detachLoggerContext(final String loggerContextName) { return getLoggerContext(); }
public List<String> getContextNames() { return Arrays.asList(getLoggerContext().getName()); }
public LoggerContext getDefaultLoggerContext() { return defaultContext; }
public LoggerContext getLoggerContext() { LoggerContext lc = threadLocal.get(); if (lc == null) { threadLocal.set(new LoggerContext()); } return threadLocal.get(); }
public LoggerContext getLoggerContext(final String name) { if (getLoggerContext().getName().equals(name)) { return getLoggerContext(); } return null; }
}
As long as all Loggers are used like this:
private Logger log = LoggerFactory.getLogger(DemoWebService.class);
everything works fine, I get my logging.
But also this is a common way to get my Logger:
private static final Logger log = LoggerFactory.getLogger(DemoWebService.class);
Because for WebServices and Servlets the VM is not terminated and these Logger Instance will live a long time without ever get refreshed. After the first run I don't see logging any more.
How can I deal with external JARs like Apache HTTP Client and other that instantiate the loggers in a static way?
Regards, Martin -- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22352655.html Sent from the Logback User mailing list archive at Nabble.com.
_______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22353608.html Sent from the Logback User mailing list archive at Nabble.com.

See http://logback.qos.ch/manual/appenders.html#SiftingAppender which will work for all loggers, even those declared statically. You could either put values in MDC when your thread starts or develop your own "discriminator" class based on thread ids. A discriminator has to implement the ch.qos.logback.core.sift.Discriminator interface. Here is a tentative implementation: package myPackage; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.sift.Discriminator; import ch.qos.logback.core.spi.ContextAwareBase; public class ThreadBasedDiscriminator extends ContextAwareBase implements Discriminator<ILoggingEvent>{ boolean started = false; public String getDiscriminatingValue(ILoggingEvent e) { // discriminate based on the id of the current thread // it could also be the "Domino database" for the current thread int id = System.identityHashCode(Thread.currentThread()); return Integer.toString(id); } public String getKey() { // always use the same fixed key return "threadId"; } public boolean isStarted() { return started; } public void start() { started = true; } public void stop() { started = false; } } With the help of this discriminator each thread will use its own specific appender. You could modify the discriminator so that several related thread map to the same value so that they use the same appender. I fear that I still have not understood your environment very well. Thus, the above proposal might be off the mark. HTH, Martin Burchard wrote:
Hello Mahoney,
I read about MDC and Filter but I don't have a clue how to find the non static session object and the non static database object to write the log into the right database object.
The only other way, without using the ContextSelector is to create an Appender that tries to find the corresponding Session with ThreadLocal access. But I think that this wont be very fast...
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

To give you an impression of the pain of a Domino developer, here is a basic agent. package de.pentos.test.agents; import lotus.domino.AgentBase; import lotus.domino.AgentContext; import lotus.domino.Base; import lotus.domino.Database; import lotus.domino.Document; import lotus.domino.NotesException; import lotus.domino.Session; /** * @author Martin Burchard */ public class BasicAgent extends AgentBase { /** * Recycles every Domino object and doesn't care about the thrown * NotesException. * * @param obj */ public static void recycle(final Base obj) { if (obj != null) { try { obj.recycle(); } catch (NotesException e) {} } } @Override public void NotesMain() { Session session = null; AgentContext agCTX = null; Database db = null; Document doc = null; try { session = getSession(); agCTX = session.getAgentContext(); db = agCTX.getCurrentDatabase(); doc = agCTX.getDocumentContext(); // start doing something useful... } catch (NotesException e) { e.printStackTrace(); } finally { recycle(doc); recycle(db); recycle(agCTX); recycle(session); } } } Locking at the NotesMain method, you see that every Domino object that a developer uses needs to be recycled. If you forgot it, the Server will crash after a while. Behind the Java objects are C objects. Another bad thing. If you use a Domino API written of somebody else in your compony, without knowing how it works and your Java code and the other API are accessing the same database (for example to log into the database) both have 2 different Java objects but the same C object. If one of you recycles this database object, the C object is gone... Either your code fails, or the API fails... That is why you always have to deal with the lifetime and accessibility of Domino objects. That is what logging into a Domino database makes that hard... I hope this small excursus makes my question a little bit clearer. If you like I can also post how a basic WebService and Servlet looking like... Ceki Gulcu wrote:
See http://logback.qos.ch/manual/appenders.html#SiftingAppender which will work for all loggers, even those declared statically.
You could either put values in MDC when your thread starts or develop your own "discriminator" class based on thread ids. A discriminator has to implement the ch.qos.logback.core.sift.Discriminator interface.
With the help of this discriminator each thread will use its own specific appender. You could modify the discriminator so that several related thread map to the same value so that they use the same appender.
I fear that I still have not understood your environment very well. Thus, the above proposal might be off the mark.
HTH,
Martin Burchard wrote:
Hello Mahoney,
I read about MDC and Filter but I don't have a clue how to find the non static session object and the non static database object to write the log into the right database object.
The only other way, without using the ContextSelector is to create an Appender that tries to find the corresponding Session with ThreadLocal access. But I think that this wont be very fast...
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22356808.html Sent from the Logback User mailing list archive at Nabble.com.

By the way, what type of appender are you using? Is it a custom made appender for Domino? -- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

This one... But the assumption is that I use a Context Selector so only Log events reach my appender instance that belong to this appender instance. package de.pentos.domino.logging; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import lotus.domino.Database; import lotus.domino.Log; import lotus.domino.NotesException; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.AppenderBase; import de.pentos.domino.base.DominoHelper; import de.pentos.domino.base.IProcess; import de.pentos.domino.base.ProcessManager; /** * @author Martin Burchard * @version $Revision: $ */ public class ALogAppender<E> extends AppenderBase<E> { public static final String CVS_VERSION = "$Revision: $"; private String dbPath; private Log nLog; private Database logDB; private final List<E> logCache = new ArrayList<E>(); private static final String ERROR_WRITING_ALOG = "Error while writing message in ALogAppender"; private static final String ERROR_OBJECT_RECYCLING = "Object ALogAppender recycled during finalize!"; private boolean closed = false; @Override protected void append(final E event) { if (!closed) { logCache.add(event); writeCache(); } } /** * */ private void cleanUP() { IProcess process = ProcessManager.getProcess(); closed = true; try { nLog.close(); } catch (NotesException e) {} process.disposeDB(logDB, this); DominoHelper.recycle(nLog); logDB = null; nLog = null; } /* * (non-Javadoc) * @see de.pentos.base.domino.backend.AbstractDominoContext#finalize() */ public void finalize() { if (!closed) { System.err.println(ERROR_OBJECT_RECYCLING); cleanUP(); } } /** * @return */ private boolean getNLog() { if (nLog == null) { try { IProcess process = ProcessManager.getProcess(); logDB = process.getDB("", dbPath, this); nLog = process.getSession().createLog(process.getName()); nLog.openNotesLog(logDB.getServer(), logDB.getFilePath()); return true; } catch (Throwable th) { th.printStackTrace(); } return false; } return true; } /** * @param dbPath */ public void setDBPath(final String dbPath) { this.dbPath = dbPath; } /* * (non-Javadoc) * @see ch.qos.logback.core.AppenderBase#stop() */ @Override public void stop() { cleanUP(); super.stop(); } /** * */ private void writeCache() { if (getNLog()) { for (Iterator<E> it = logCache.iterator(); it.hasNext();) { E event = it.next(); try { if (event instanceof LoggingEvent) { if (((LoggingEvent) event).getLevel().equals(Level.ERROR)) { nLog.logError(0, layout.doLayout(event)); } else { nLog.logAction(layout.doLayout(event)); } } else { nLog.logAction(layout.doLayout(event)); } } catch (NotesException e) { System.err.println(ERROR_WRITING_ALOG); e.printStackTrace(); } } logCache.clear(); } } } Ceki Gulcu wrote:
By the way, what type of appender are you using? Is it a custom made appender for Domino?
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22356470.html Sent from the Logback User mailing list archive at Nabble.com.

Looking at the getNLog() method reproduced below, it seems like discriminating appenders based on process name would be sufficient. private boolean getNLog() { if (nLog == null) { try { IProcess process = ProcessManager.getProcess(); logDB = process.getDB("", dbPath, this); nLog = process.getSession().createLog(process.getName()); nLog.openNotesLog(logDB.getServer(), logDB.getFilePath()); return true; } catch (Throwable th) { th.printStackTrace(); } return false; } return true; } The discriminator class I mentioned previously could be modified as: public class ProcessBasedDiscriminator implements Discriminator<LoggingEvent>{ public String getDiscriminatingValue(LoggingEvent e) { IProcess process = ProcessManager.getProcess(); return process.getName(); } public String getKey() { // always use the same fixed key return "threadId"; } With the help ProcessBasedDiscriminator just described and SiftingAppender, you could have *one* ALogAppender instance per domino *process* regardless of the containing Java thread. As far as I could tell, that would be quite an elegant solution would it not? Martin Burchard wrote:
This one...
But the assumption is that I use a Context Selector so only Log events reach my appender instance that belong to this appender instance.
package de.pentos.domino.logging;
import java.util.ArrayList; import java.util.Iterator; import java.util.List;
import lotus.domino.Database; import lotus.domino.Log; import lotus.domino.NotesException; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.AppenderBase; import de.pentos.domino.base.DominoHelper; import de.pentos.domino.base.IProcess; import de.pentos.domino.base.ProcessManager;
/** * @author Martin Burchard * @version $Revision: $ */ public class ALogAppender<E> extends AppenderBase<E> { public static final String CVS_VERSION = "$Revision: $"; private String dbPath; private Log nLog; private Database logDB; private final List<E> logCache = new ArrayList<E>(); private static final String ERROR_WRITING_ALOG = "Error while writing message in ALogAppender"; private static final String ERROR_OBJECT_RECYCLING = "Object ALogAppender recycled during finalize!"; private boolean closed = false;
@Override protected void append(final E event) { if (!closed) { logCache.add(event); writeCache(); } }
/** * */ private void cleanUP() { IProcess process = ProcessManager.getProcess(); closed = true; try { nLog.close(); } catch (NotesException e) {} process.disposeDB(logDB, this); DominoHelper.recycle(nLog); logDB = null; nLog = null; }
/* * (non-Javadoc) * @see de.pentos.base.domino.backend.AbstractDominoContext#finalize() */ public void finalize() { if (!closed) { System.err.println(ERROR_OBJECT_RECYCLING); cleanUP(); } }
/** * @return */ private boolean getNLog() { if (nLog == null) { try { IProcess process = ProcessManager.getProcess(); logDB = process.getDB("", dbPath, this); nLog = process.getSession().createLog(process.getName()); nLog.openNotesLog(logDB.getServer(), logDB.getFilePath()); return true; } catch (Throwable th) { th.printStackTrace(); } return false; } return true; }
/** * @param dbPath */ public void setDBPath(final String dbPath) { this.dbPath = dbPath; }
/* * (non-Javadoc) * @see ch.qos.logback.core.AppenderBase#stop() */ @Override public void stop() { cleanUP(); super.stop(); }
/** * */ private void writeCache() { if (getNLog()) { for (Iterator<E> it = logCache.iterator(); it.hasNext();) { E event = it.next(); try { if (event instanceof LoggingEvent) { if (((LoggingEvent) event).getLevel().equals(Level.ERROR)) { nLog.logError(0, layout.doLayout(event)); } else { nLog.logAction(layout.doLayout(event)); } } else { nLog.logAction(layout.doLayout(event)); } } catch (NotesException e) { System.err.println(ERROR_WRITING_ALOG); e.printStackTrace(); } } logCache.clear(); } } }
Ceki Gulcu wrote:
By the way, what type of appender are you using? Is it a custom made appender for Domino?
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

Thank you, I'll try to understand and implement that. Using this way, is it possible to have the Logger configuration also on a per Thread basis? I know this is also a little bit strange but, we have a lot of different processes running. Some do logging using the ALogAppender, some do logging using a DocumentLogger, some do logging onto the console. The possibility to configure all LoggerContext using the JoranConfigurator was also a reason for implementing a ContextSelector. Reading through Chapter 9: Context Selectors I have seen the ContextJNDISelector and now it sounds to me, that ContextSelector isn't such a good idea? Ceki Gulcu wrote:
Looking at the getNLog() method reproduced below, it seems like discriminating appenders based on process name would be sufficient.
private boolean getNLog() { if (nLog == null) { try { IProcess process = ProcessManager.getProcess(); logDB = process.getDB("", dbPath, this); nLog = process.getSession().createLog(process.getName()); nLog.openNotesLog(logDB.getServer(), logDB.getFilePath()); return true; } catch (Throwable th) { th.printStackTrace(); } return false; } return true; }
The discriminator class I mentioned previously could be modified as:
public class ProcessBasedDiscriminator implements Discriminator<LoggingEvent>{
public String getDiscriminatingValue(LoggingEvent e) { IProcess process = ProcessManager.getProcess(); return process.getName(); }
public String getKey() { // always use the same fixed key return "threadId"; }
With the help ProcessBasedDiscriminator just described and SiftingAppender, you could have *one* ALogAppender instance per domino *process* regardless of the containing Java thread. As far as I could tell, that would be quite an elegant solution would it not?
Martin Burchard wrote:
This one...
But the assumption is that I use a Context Selector so only Log events reach my appender instance that belong to this appender instance.
package de.pentos.domino.logging;
import java.util.ArrayList; import java.util.Iterator; import java.util.List;
import lotus.domino.Database; import lotus.domino.Log; import lotus.domino.NotesException; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.AppenderBase; import de.pentos.domino.base.DominoHelper; import de.pentos.domino.base.IProcess; import de.pentos.domino.base.ProcessManager;
/** * @author Martin Burchard * @version $Revision: $ */ public class ALogAppender<E> extends AppenderBase<E> { public static final String CVS_VERSION = "$Revision: $"; private String dbPath; private Log nLog; private Database logDB; private final List<E> logCache = new ArrayList<E>(); private static final String ERROR_WRITING_ALOG = "Error while writing message in ALogAppender"; private static final String ERROR_OBJECT_RECYCLING = "Object ALogAppender recycled during finalize!"; private boolean closed = false;
@Override protected void append(final E event) { if (!closed) { logCache.add(event); writeCache(); } }
/** * */ private void cleanUP() { IProcess process = ProcessManager.getProcess(); closed = true; try { nLog.close(); } catch (NotesException e) {} process.disposeDB(logDB, this); DominoHelper.recycle(nLog); logDB = null; nLog = null; }
/* * (non-Javadoc) * @see de.pentos.base.domino.backend.AbstractDominoContext#finalize() */ public void finalize() { if (!closed) { System.err.println(ERROR_OBJECT_RECYCLING); cleanUP(); } }
/** * @return */ private boolean getNLog() { if (nLog == null) { try { IProcess process = ProcessManager.getProcess(); logDB = process.getDB("", dbPath, this); nLog = process.getSession().createLog(process.getName()); nLog.openNotesLog(logDB.getServer(), logDB.getFilePath()); return true; } catch (Throwable th) { th.printStackTrace(); } return false; } return true; }
/** * @param dbPath */ public void setDBPath(final String dbPath) { this.dbPath = dbPath; }
/* * (non-Javadoc) * @see ch.qos.logback.core.AppenderBase#stop() */ @Override public void stop() { cleanUP(); super.stop(); }
/** * */ private void writeCache() { if (getNLog()) { for (Iterator<E> it = logCache.iterator(); it.hasNext();) { E event = it.next(); try { if (event instanceof LoggingEvent) { if (((LoggingEvent) event).getLevel().equals(Level.ERROR)) { nLog.logError(0, layout.doLayout(event)); } else { nLog.logAction(layout.doLayout(event)); } } else { nLog.logAction(layout.doLayout(event)); } } catch (NotesException e) { System.err.println(ERROR_WRITING_ALOG); e.printStackTrace(); } } logCache.clear(); } } }
Ceki Gulcu wrote:
By the way, what type of appender are you using? Is it a custom made appender for Domino?
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22361882.html Sent from the Logback User mailing list archive at Nabble.com.

Martin Burchard wrote:
Using this way, is it possible to have the Logger configuration also on a per Thread basis?
No, SiftingAppender only controls the appenders nested within it. It does not control the configuration of loggers.
Reading through Chapter 9: Context Selectors I have seen the ContextJNDISelector and now it sounds to me, that ContextSelector isn't such a good idea?
Context selection is a possible approach but logback offers other alternative approaches, e.g. SiftingAppender. Given the unfamiliarity of your environment, it is not possible to propose the right approach off the bat. However, you have correctly identified one problem with context selectors, that is static definition of logger variables. This is a known weakness of context selectors. However, assuming that you are mostly interested in the logs generated by your own code and less about code generated by the libraries you use, you could force logger variables to be non-static and just ignore logs generated by static logger variables. -- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

I used this configuration for testing... <configuration> <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator> <Key>process</Key> <DefaultValue>unknown</DefaultValue> </discriminator> <sift> <appender name="DocApp-${process}" class="de.pentos.domino.logging.DocumentAppender"> <DBPath>${process}</DBPath> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</Pattern> </layout> </appender> </sift> </appender> <root level="DEBUG"> <appender-ref ref="SIFT" /> </root> </configuration> There is only one thing to do... It's essential to close the Domino based appenders (you know, the Domino object problems) I implemented this method in the DocumentAppender. public void stop() { // TODO Auto-generated method stub System.out.println("I " + dbPath + " get stopped now."); super.stop(); } But I never see this sysout :( How can I explicitly stop the Appender? I tried to get my Appender, but I only can get the sifting appender. LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); UUID uuid = UUID.randomUUID(); MDC.put("process", uuid.toString()); ch.qos.logback.classic.Logger root = lc.getLogger(LoggerContext.ROOT_NAME); Appender<LoggingEvent> sift = root.getAppender("SIFT"); if (sift != null) { log.debug("gut the sifting Appender {}", sift.getName()); SiftingAppender sifting = (SiftingAppender) sift; } Appender<LoggingEvent> x = root.getAppender("DocApp-" + uuid.toString()); if (x != null) { log.debug("got the Appender {}", x.getName()); } Is this last thing possible? Ceki Gulcu wrote:
Martin Burchard wrote:
Using this way, is it possible to have the Logger configuration also on a per Thread basis?
No, SiftingAppender only controls the appenders nested within it. It does not control the configuration of loggers.
Reading through Chapter 9: Context Selectors I have seen the ContextJNDISelector and now it sounds to me, that ContextSelector isn't such a good idea?
Context selection is a possible approach but logback offers other alternative approaches, e.g. SiftingAppender. Given the unfamiliarity of your environment, it is not possible to propose the right approach off the bat.
However, you have correctly identified one problem with context selectors, that is static definition of logger variables. This is a known weakness of context selectors. However, assuming that you are mostly interested in the logs generated by your own code and less about code generated by the libraries you use, you could force logger variables to be non-static and just ignore logs generated by static logger variables.
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- View this message in context: http://www.nabble.com/ContextSelector-and-getLogger-tp22352655p22399443.html Sent from the Logback User mailing list archive at Nabble.com.

Hello Martin, SiftingAppender tracks the appenders it creates in a data structure called "AppenderTracer". See [1, 2, 3]. Only through this structure can you access the appenders nested within a SiftingAppender. Note that that unused or "stale" appenders will be closed by the appender tracker after 30 minutes of non-use. Would closing your DocumentAppender instances after 30 minutes work for you? [1] http://logback.qos.ch/xref/ch/qos/logback/classic/sift/SiftingAppender.html [2] http://logback.qos.ch/xref/ch/qos/logback/core/sift/AppenderTracker.html [3] http://logback.qos.ch/xref/ch/qos/logback/core/sift/SiftingAppenderBase.html Martin Burchard wrote:
I used this configuration for testing...
<configuration> <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <discriminator> <Key>process</Key> <DefaultValue>unknown</DefaultValue> </discriminator> <sift> <appender name="DocApp-${process}" class="de.pentos.domino.logging.DocumentAppender"> <DBPath>${process}</DBPath> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</Pattern> </layout> </appender> </sift> </appender> <root level="DEBUG"> <appender-ref ref="SIFT" /> </root> </configuration>
There is only one thing to do... It's essential to close the Domino based appenders (you know, the Domino object problems) I implemented this method in the DocumentAppender.
public void stop() { // TODO Auto-generated method stub System.out.println("I " + dbPath + " get stopped now."); super.stop(); }
But I never see this sysout :( How can I explicitly stop the Appender? I tried to get my Appender, but I only can get the sifting appender.
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); UUID uuid = UUID.randomUUID(); MDC.put("process", uuid.toString()); ch.qos.logback.classic.Logger root = lc.getLogger(LoggerContext.ROOT_NAME); Appender<LoggingEvent> sift = root.getAppender("SIFT"); if (sift != null) { log.debug("gut the sifting Appender {}", sift.getName()); SiftingAppender sifting = (SiftingAppender) sift; } Appender<LoggingEvent> x = root.getAppender("DocApp-" + uuid.toString()); if (x != null) { log.debug("got the Appender {}", x.getName()); }
Is this last thing possible?
Ceki Gulcu wrote:
Martin Burchard wrote:
Using this way, is it possible to have the Logger configuration also on a per Thread basis?
No, SiftingAppender only controls the appenders nested within it. It does not control the configuration of loggers.
Reading through Chapter 9: Context Selectors I have seen the ContextJNDISelector and now it sounds to me, that ContextSelector isn't such a good idea? Context selection is a possible approach but logback offers other alternative approaches, e.g. SiftingAppender. Given the unfamiliarity of your environment, it is not possible to propose the right approach off the bat.
However, you have correctly identified one problem with context selectors, that is static definition of logger variables. This is a known weakness of context selectors. However, assuming that you are mostly interested in the logs generated by your own code and less about code generated by the libraries you use, you could force logger variables to be non-static and just ignore logs generated by static logger variables.
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch _______________________________________________ Logback-user mailing list Logback-user@qos.ch http://qos.ch/mailman/listinfo/logback-user
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch
participants (3)
-
Ceki Gulcu
-
Martin Burchard
-
Robert Elliot