简体   繁体   中英

While logging log create log file dynamically for that class and log into that file only in log4j

I'm using log4j in my project for logging. I have one package and it contains 50+ java classes. While logging the logs from these if log statement comes from class A it should go in A.log file and etc for other classes. I want to do it at runtime. Don't want to write appenders in log4j.xml for all classes. At runtime it should be able to identify that log comes from class A so it log it into A.log file. I want achieve this by doing minimal or no changes in java file as its already implemented. Can I achieve by doing changes only in log4j.xml file of that package. System.setProperty("logfilename","className") is not useful for me. Any help appreciated.

Before anything else I feel that I should point out this seems like a very unusual logging strategy . Normally you would want to see a holistic view of what happened during a particular user's interaction with your program rather than having to refer to dozens of log files trying to trace through the flow of the logic. For example if a user runs your program by invoking class A and then class A uses Class B you already have to look at 2 logs in order to follow the user experience. Imagine having to do this for a complex user interaction that flows into many more classes and also imagine how difficult it would be to trace when the execution goes from A to B to A to B to C to B to A. Just in this small example you would have to look at the log for A and somehow recognize that execution is going to class B then look at the log for B and so on.

With all of the above said, if you still want to go this route then I don't believe you can achieve what you want with only a configuration change but it is possible with minimal code in addition to a configuration change.

If you take a look at the log4j2 manual in the section regarding Extending Log4j2 Lookups you'll see an example there of how to implement a lookup. You can create your own lookup for the logger name as follows:

package example;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;

@Plugin(name = "logger", category = "Lookup")
public class LoggerLookup implements StrLookup{
    /**
     * Lookup the value for the key.
     * @param key  the key to be looked up, may be null
     * @return The value for the key.
     */
    public String lookup(String key) {
        return null;
    }


    /**
     * Lookup the value for the key using the data in the LogEvent.
     * @param event The current LogEvent.
     * @param key  the key to be looked up, may be null
     * @return The value associated with the key.
     */
    public String lookup(LogEvent event, String key) {
        if("name".equals(key)){
            return event.getLoggerName();
        }
        return null;
    }
}

Now, use the Routing Appender to dynamically create appenders as needed based on the logger name at runtime by configuring it similar to the following example:

    <Routing name="MyRoutingAppender">
        <Routes pattern="$${logger:name}">
            <Route>
                <File
                    fileName="logs/${logger:name}.txt"
                    name="appender-${logger:name}">
                    <PatternLayout>
                        <Pattern>%d{HH:mm:ss.SSS} [%t] %-5level ${logger:name} - %msg%n</Pattern>
                    </PatternLayout>
                </File>
            </Route>
        </Routes>
    </Routing>

Below are two sample classes that generate some logging.

First the main class:

package example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SomeClass {

    private static final Logger log = LogManager.getLogger();   

    public static void main(String[] args){
        log.info("Here's some info!");
        log.error("Some erorr happened!");

        AnotherClass ac = new AnotherClass();
        ac.logSomething();
    }
}

Second class:

package example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class AnotherClass {
    private static final Logger log = LogManager.getLogger();   

    public void logSomething(){
        log.info("This is yet another info message");
    }
}

Below is the configuration file (log4j2.xml) I used to test with. Note that I used console appender at first to verify the new lookup was working and decided to leave it in as it may be helpful to those reading this answer.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level ${logger:name} - %msg%n" />
        </Console>

        <Routing name="MyRoutingAppender">
            <Routes pattern="$${logger:name}">
                <Route>
                    <File
                        fileName="logs/${logger:name}.txt"
                        name="appender-${logger:name}">
                        <PatternLayout>
                            <Pattern>%d{HH:mm:ss.SSS} [%t] %-5level ${logger:name} - %msg%n</Pattern>
                        </PatternLayout>
                    </File>
                </Route>
            </Routes>
        </Routing>
    </Appenders>

    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console" level="info" />          
            <AppenderRef ref="MyRoutingAppender" level="info" />
        </Root>
    </Loggers>
</Configuration>

Running SomeClass generates two log files called "example.AnotherClass.txt" and "example.SomeClass.txt" in a directory named "logs". The logs in the first file were generated by AnotherClass and those in the second file were generated by SomeClass

示例代码生成的日志文件

All of this is just a proof of concept so you will need to modify it to fit your needs. Hopefully it illustrates the general way you could achieve what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM