Andrei
Andrei

Reputation: 51

How to destroy log4j logger instances

I have a code that run concurrently, and for each run it need log something in file. Each execution - new story with unique file.

So, I can't just get the logger for classname, add file appender, write logs, close and remove appender, because when concurrect code run - logger will contains both appenders, and logs will be written to both files.

So, I can create a new logger instance for each execution Logger.getLogger(classname + counter), but how to mark it as garbage after work is done?

P.S. Moreover... I need somethimes print to console from all this loggers.

Maybe I do something wrong, maybe log4j not created for this pattern and I have to implement it. But log4j - priority choose for me, because it already widely used in this big application.

Thank you in advance, Andrei!

Upvotes: 1

Views: 1279

Answers (1)

Angelo Fuchs
Angelo Fuchs

Reputation: 9941

You can't destroy / clean up Appender instances. But you can improve the solution you have with Log4j2 toolbox.

Routing things to dynamic files in Log4j is possible, but most of the time a Marker in a file is sufficient, or at least a starting point for more complex routing.

Please do read up on its documentation. Especially the last paragraph is important to your case:

Some important rules about Markers must be considered when using them.

  1. Markers must be unique. They are permanently registered by name so care should be taken to insure that Markers used in your application are distinct from those in the application's dependencies, unless that is what is desired.
  2. Parent Markers can be added or removed dynamically. However, this is fairly expensive to do. Instead, it is recommended that the parents be identified when obtaining the Marker the first time as shown in the examples above. Specifically, the set method replaces all the markers in a single operation while add and remove act on only a single Marker at a time.
  3. Evaluating Markers with multiple ancestors is much more expensive than Markers with no parents. For example, in one set of tests to evaluate whether a Marker matched its grandparent took 3 times longer than evaluating the Marker itself. Even then though, evaluating Markers is inexpensive compared to resolving the callers class name or line number.

Then you can use the marker in the configuration in places where a pattern is expected like this: $${marker:}. I haven't used that in a filename though and doubt that it works, but you can create routing based on the Marker.

I used this simple test script, please note the use of the Marker that is created for each line from the Scanner. In your case it would be created by config or from Servlet input or similar.

package toTest;

import java.util.Scanner;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;

public class TestMe {
    private final Logger myOneAndOnlyLogger = LogManager.getLogger("MyCentralName");
    public static void main(String[] args) {
        new TestMe().doMyThing();
    }

    private void doMyThing() {
        Scanner input = new Scanner(System.in);
        String line = "";
        while(!line.equals("QUIT")) {
            System.out.println("Line: ");
            line = input.nextLine();
            Marker forThisRound = MarkerManager.getMarker(line);
            myOneAndOnlyLogger.log(Level.ERROR, forThisRound, "1");
            myOneAndOnlyLogger.log(Level.ERROR, forThisRound, "2");
            System.out.println("Line done.");
        }
    }
}

and this log4j2.properties (a rolling file example I had at hand with marker in the pattern):

status = error
name = MarkerExample

#Make sure to change log file path as per your need
property.filename = /tmp/java/marker.log

filters = threshold

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appenders = rolling

appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = /tmp/java/debug-backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} $${marker:} %-5p %c{1}:%L - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MBONE
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20

loggers = rolling

#Make sure to change the package structure as per your application

logger.rolling.name = MyCentralName
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile

Upvotes: 0

Related Questions