Java Log4j2:为多个日志动态创建日志文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19052070/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:43:00  来源:igfitidea点击:

Log4j2: Dynamic creation of log files for multiple logs

javalogginglog4jlog4j2

提问by spaceemotion

I am currently creating a system that can have modules (think of them as plugins), where each one of them can have their own log, dedicated.

我目前正在创建一个可以拥有模块(将它们视为插件)的系统,其中每个模块都可以拥有自己的专用日志。

I would like to use the log4j2 project for logging, but I seem to have some trouble with the file appenders.

我想使用 log4j2 项目进行日志记录,但我似乎在文件附加程序方面遇到了一些麻烦。

The main project (the module loader and "core" of the whole thing) should have its own log file, while the modules should have their own (like mod_XXXXXXXX.log).

主项目(模块加载器和整个项目的“核心”)应该有自己的日志文件,而模块应该有自己的(如mod_XXXXXXXX.log)。

By reading the documentation about the appenders I discovered the FileAppenderclass, and I was going to use that. Until I found out that I can't just simple add the appender to the default logger created by LogManager.getLog().

通过阅读关于 appender 的文档,我发现了这个FileAppender类,我打算使用它。直到我发现我不能简单地将 appender 添加到由LogManager.getLog().

The logger returned by the LogManager is a different logger than the Loggerinterface.

LogManager 返回的记录器与Logger接口不同。

Even searching did not give me any near solution, all I found was predefined file logs in the xml configuration - which is not what I want.

即使搜索也没有给我任何接近的解决方案,我发现的只是 xml 配置中的预定义文件日志 - 这不是我想要的。

Thank you for reading; even the slightest clue is welcome :)

感谢您的阅读;即使是最轻微的线索也是受欢迎的:)

采纳答案by Remko Popma

if you really need to determine the log file dynamically, take a look at the Log4J2 RoutingAppender. A longer example is in the FAQand these stackoverflow questions may be of interest: Wildcard pattern for RoutingAppender of Log4j2and How to write different logs in different files with log4j2 (MDC in xml)?

如果您确实需要动态确定日志文件,请查看 Log4J2 RoutingAppender。一个更长的例子在FAQ 中,这些 stackoverflow 问题可能很有趣: Log4j2 的 RoutingAppender 的通配符模式如何使用 log4j2 (MDC in xml) 在不同的文件中写入不同的日志?

Note that you need to set values in the ThreadContextmap that the RoutingAppender uses to decide which appender to route the log event to. This means that you would need to put some value in the ThreadContext map every time your code enters a different plugin.

请注意,您需要ThreadContext在 RoutingAppender 用来决定将日志事件路由到哪个 appender的映射中设置值。这意味着每次您的代码进入不同的插件时,您都需要在 ThreadContext 映射中放入一些值。

However, do you really need it to be this dynamic? If you know in advance what plugins you have, you can just declare a logger for each plugin (using the package name of the plugin is a common way to do this), and map each such logger to a separate appender.

但是,您真的需要它如此动态吗?如果你事先知道你有哪些插件,你可以为每个插件声明一个记录器(使用插件的包名是一种常见的方法),并将每个这样的记录器映射到一个单独的附加器。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <File name="plugin1" fileName="logs/plugin1.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <File name="plugin2" fileName="logs/plugin2.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Logger name="com.mycomp.project.plugin1" level="debug">
      <AppenderRef ref="plugin1" level="debug" />
    </Logger>
    <Logger name="com.mycomp.project.plugin2" level="debug">
      <AppenderRef ref="plugin2" level="debug" />
    </Logger>
    <Root level="trace">
      <AppenderRef ref="MyFile" level="trace" />
    </Root>
  </Loggers>
</Configuration>

回答by babernathy

I'm assuming you want your module management code define the logger configuration, right? If so, you may want to take a look at this portion of the manual which talks about extending LoggerConfig which based on what your asking is what I thinkyou are looking for.

我假设您希望您的模块管理代码定义记录器配置,对吗?如果是这样,您可能需要查看手册的这一部分,该部分讨论了根据您的要求扩展 LoggerConfig,这是我认为您正在寻找的内容。

http://logging.apache.org/log4j/2.x/manual/extending.html

http://logging.apache.org/log4j/2.x/manual/extending.html

For what it's worth, I've been involved in large plug-in based systems before (using OSGi) and we honestly haven't taken this route. It's usually easier to just grep the class or package you are interested in from a single log file.

就其价值而言,我之前(使用 OSGi)参与过基于大型插件的系统,老实说,我们还没有走这条路。从单个日志文件中 grep 感兴趣的类或包通常更容易。

回答by spaceemotion

Even though Remko Popma's answer might be the most efficient way to do the logging, I built a small class that can create log files on it's own.

尽管 Remko Popma 的答案可能是进行日志记录的最有效方法,但我构建了一个可以自行创建日志文件的小类。

I think I will use the accepted answer's solution, so here is the code I wrote to work around the XML file stuff:

我想我会使用接受的答案的解决方案,所以这里是我写的代码来解决 XML 文件的问题:

import gnu.trove.map.hash.THashMap;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.async.AsyncLoggerContext;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.message.FormattedMessageFactory;
import org.apache.logging.log4j.message.MessageFactory;

import java.io.File;
import java.io.IOException;
import java.util.Map;

/**
 * Represents a manager for custom log files stored inside a log folder.
 */
public class LoggingManager {
    /** The default log file extension */
    public static final String FILE_EXTENSION = "log";

    /** The global context used for all loggers */
    private final LoggerContext context;

    /** The global message factory used for all loggers */
    private final MessageFactory msgFactory;

    /** A map of all created logs */
    private final Map<String, Logger> logCache;

    /** The folder containing the log files */
    private final File logFolder;


    public LoggingManager(String name, File logFolder) throws IOException {
        this.logFolder = logFolder;

        if(!logFolder.exists()) {
            if(!logFolder.mkdirs()) {
                throw new IOException("Could not create log folder");
            }
        }

        this.logCache = new THashMap<String, Logger>();

        // Create logger context
        this.context = new AsyncLoggerContext(name);

        // Create formatted message factory
        this.msgFactory = new FormattedMessageFactory();
    }

    public Logger getLogger(String name) {
        Logger logger = logCache.get(name);

        // Create a new one
        if(logger == null) {
            logger = new SimpleLogger(name);

            FileAppender appender = FileAppender.createAppender(
                    new File(logFolder, name + "." + FILE_EXTENSION).getAbsolutePath(),
                    "true",
                    "false",
                    "file_appender-" + name,
                    "true",
                    "false",
                    "true",
                    PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, null, null, "UTF-8", "true"),
                    null,
                    "false",
                    null,
                    null
            );

            appender.start();
            logger.getContext().getConfiguration().getLoggerConfig("root").addAppender(appender, Level.ALL, null);

            // Add to log cache
            logCache.put(name, logger);
        }

        // Return the logger
        return logger;
    }

    private class SimpleLogger extends Logger {

        public SimpleLogger(String name) {
            super(context, name, msgFactory);

            // Set to all levels
            this.setLevel(Level.ALL);
        }

    }

}

If you don't use troveyou can replace it with a normal java HashMapif you want.

如果您不使用,trove您可以根据需要将其替换为普通的 java HashMap