5

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

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

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

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

LogManager 返回的 logger 是与Logger接口不同的 logger。

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

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

4

3 回答 3

9

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

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

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

<?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>
于 2013-09-28T22:11:38.800 回答
0

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

我想我会使用公认答案的解决方案,所以这是我为解决 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);
        }

    }

}

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

于 2013-09-30T09:55:46.347 回答
0

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

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

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

于 2013-09-27T14:30:19.403 回答