Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly manage the lifecycle of logfiles created with plugins using Java logging with log4j2

My team is working on a plugin for NetBeans which uses logging to a specified file. The logging system uses SLF4J + Log4J2; the only configuration information that I am aware of is a log4j2.xml file with logging properties for the plugin, and the following:

    LoggerContext loggerContext = (LoggerContext)LogManager.getContext(false);
    loggerContext.reconfigure();

(The fact that I am using SLF4J and NetBeans is not really relevant here.)

The question I have, is what is the correct way to start logging when the plugin starts and stop logging when the plugin exits? (The JVM keeps on going with NetBeans so the log files aren't automatically closed.)

I looked up LoggerContext and I see stop() and terminate() methods but I can't find any documentation for how applications are supposed to use LoggerContext, so I don't know whether this LifeCycle stuff is an internal detail or something that an application is supposed to use.


Some more specific details:

Our plugin lifecycle looks something like the diagram below (only one instance is permitted to run at a time). The "A" and "B" refer to log configurations that we would want to have in effect.

     [Something starts within the JVM to load log4j classes first.
      It might be our plugin, it might be something else.
A     log4j's default mechanism kicks in to create log configuration A]
|
|    [time passes]
|
|    [our plugin starts]
A    [log4j classes first loaded, 
|    default mechanism kicks in to create log configuration A]
A    setup log configuration B
A B  log event 1
A B  log event 2
A B  log event 3
A    shutdown log configuration B
|    [our plugin exits]
|
|    [time passes]
|
|    [our plugin starts]
A    setup log configuration B
A B  log event 1
A B  log event 2
A B  log event 3
A    shutdown log configuration B
|    [our plugin exits]
|
|    [time passes]
|
     [JVM exits]

The configuration A is associated with the JVM and is the default configuration created by log4j the first time it is loaded by the JVM.

Configuration B is managed programmatically by the plugin and should have explicit startup / shutdown independent of configuration A (and configuration A should not be affected).

Is there any way to achieve this with log4j 2.0?

like image 774
Jason S Avatar asked Jun 01 '16 18:06

Jason S


2 Answers

In Log4j 2.5 you can call Configurator.shutdown(). With Log4j 2.6 you can call LogManager.shutdown().

like image 126
rgoers Avatar answered Oct 22 '22 17:10

rgoers


You can safely use separate LoggerContexts for different components of your application.

terminate() and stop() are absolutely identical. Actually, the former just calls the latter and does nothing more. terminate() is a part of the LoggerContext own api, while stop() is a part of wider LifeCycle interface.

If you want to implement separation of your logging contexts properly, use the mechanism of context selectors. Lo4j2 provides the interface ContextSelector for classes that are responsible for choosing the context that will be returned by LogManager.getContext(). There're several implementations out of the box:

  • BasicContextSelector choses a context based on the current thread.
  • BundleContextSelector for using in an OSGI environment.
  • JndiContextSelector for multiple web applications in a single servlet container.
  • ClassLoaderContextSelector choses a context based on the caller's class loader.

In order to specify which selector you'd like to use, set the Log4jContextSelector Java option.

For instance, if your plugins run in separate threads, you can chose the BasicContextSelector:

-DLog4jContextSelector=org.apache.logging.log4j.core.selector.BasicContextSelector

and use separate LoggerContexts for you plugins this way (this code must be run in the plugin's thread):

// Create a separate context for the plugin
LoggerContext pluginContext = new LoggerContext("PluginContext");

// Setup pluginContext...

// BasicContextSelector uses ContextAnchor.THREAD_CONTEXT
// to bind contexts to threads:
ContextAnchor.THREAD_CONTEXT.set(pluginContext);

// Your plugin works and has access to your logging context
LogManager.getContext(false); // Returns pluginContext

// Stop and remove the plugin's logging context
pluginContext.stop();
ContextAnchor.THREAD_CONTEXT.set(null);

Depending on the architecture of your plugins, you can use one of these implementations or provide your own, it's pretty easy, you'll just need to implement the following methods:

  • getContext() to create new contexts (or return existing ones) based on the parameters provided to LogManager.getContext(...).
  • getLoggerContexts() to return the collection of the available contexts (the ones you created before).
  • removeContext() to remove the contexts from memory when they are stopped (terminated).
like image 37
Andrew Lygin Avatar answered Oct 22 '22 18:10

Andrew Lygin