本文介绍了为什么我在扩展log4j时无法在log4j中记录方法名和代码行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我扩展了log42记录器。

I've extended log42 logger.

想法:
我应该将枚举传递给log方法,以便在运行时选择appender。

The idea:I should pass enum to log method, in order to choose appender in runtime.

我的界面:

public interface MyLoggerInterface {
    void info(String logMessage, MyLoggerAppenderEnum... appender);
    public static MyLoggerInterface getLogger(Class aClass, MyLoggerAppenderEnum... appender) {
        return MyLoggerInterfaceImpl.getLogger(aClass, appender);
    }
}

实施:

    public class MyLoggerInterfaceImpl extends Logger implements MyLoggerInterface {
    private static final String FQCN = MyLoggerInterfaceImpl.class.getName();

    protected MyLoggerInterfaceImpl(LoggerContext context, String name, MessageFactory messageFactory) {
        super(context, name, messageFactory);
    }

    public static MyLoggerInterface getLogger(Class aClass, MyLoggerAppenderEnum... appenders) {
        return getLogger(aClass.getName(), appenders);
    }

    private static MyLoggerInterface getLogger(String name, MyLoggerAppenderEnum... appenders) {
        return (MyLoggerInterfaceImpl) org.apache.logging.log4j.LogManager.getLogger(name);
    }

    @Override
    public void info(String logMessage, MyLoggerAppenderEnum... appenders) {
        this.log(FQCN, Level.INFO, null, new SimpleMessage(logMessage), null, appenders);
    }

    private void log(String fqcn, Level level, Marker marker, Message message, Throwable throwable, MyLoggerAppenderEnum... appenders) {
        Arrays.stream(appenders)
                .map(appender -> findAppenderByName(appender))
                .collect(Collectors.toList())
                .forEach(appender ->
                        appender.append(
                                new Log4jLogEvent(this.getName(), marker, fqcn, level, message, new ArrayList<Property>(), throwable)
                        )
                );
    }

    private Appender findAppenderByName(MyLoggerAppenderEnum appenders) {
        return this.getAppenders().get(appenders.name());
    }
}

但是注意 log4j 2.X LoggerFactory从1.X版本删除。所以我实现了其他类,以避免 ClassCastException (Logger to MyLoggerInterfaceImpl)。

But Note that in log4j 2.X LoggerFactory is removed from 1.X version. So I implement additional classes, in order to avoid ClassCastException (Logger to MyLoggerInterfaceImpl) .

所以。 MyContext:

So. MyContext:

    public class MyLoggerContext extends LoggerContext {
    public MyLoggerContext(String name) {
        super(name);
    }
    @Override
    protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
        return new MyLoggerInterfaceImpl(ctx, name, messageFactory);
    }
}

上下文选择器:

public class MyLoggerContextSelector implements ContextSelector {

    private final LoggerContext CONTEXT = new MyLoggerContext("MyLoggerContext");
    public LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext) {
        return CONTEXT;
    }
    public LoggerContext getContext(String fqcn, ClassLoader loader, boolean currentContext, URI configLocation) {
        return CONTEXT;
    }
    public List<LoggerContext> getLoggerContexts() {
        return Arrays.asList(CONTEXT);
    }
    public void removeContext(LoggerContext context) {
    }
}

Context Factory:

Context Factory:

public class MyLoggerLog4jContextFactory extends Log4jContextFactory {
    public MyLoggerLog4jContextFactory() {
        super(new MyLoggerContextSelector(), new DefaultShutdownCallbackRegistry());
    }
}

和经理:

public class MyLoggerManager {
    public static void initialize(String configURL) {
        try {
            System.setProperty("log4j2.loggerContextFactory", "ge.test.core.logging.MyLoggerLog4jContextFactory");
            System.setProperty("Log4jLogEventFactory", "org.apache.logging.log4j.core.impl.DefaultLogEventFactory");
            Configurator.initialize(null, configURL);
        } catch (Exception ex ) {
            System.err.println("Cannot initialize Log4J using configuration url:" + configURL);
        }
    }
}

酷!一切正常!!! 和用法:

 MyLoggerManager.initialize("Log4j2.xml");
 MyLoggerInterface logger = MyLoggerInterface.getLogger(AppLauncher.class);
 logger.info("test", MyLoggerAppenderEnum.Console);

但是问题是,如果我使用扩展自定义记录器,我可以' t log方法名称和行。布局是正确的! 如果我不使用扩展自定义记录器,也会记录mehtod名称和行!

BUT problem is that If I use extend custom logger, I can't log method name and line. Layout is correct! If I dont use extended custom logger, mehtod name and line is logged too!

<Configuration status="WARN">
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} %method:%line - %msg%n"/>
    </Console>
    ...
</Appenders>
<Loggers>
    ...
    <Logger name="Console" level="trace" additivity="false">
        <appender-ref ref="Console" level="trace"/>
    </Logger>
    ...
    <Root level="error">
        ...
        <AppenderRef ref="Console"/>
    </Root>
</Loggers>
</Configuration>

问题:
我想记录方法名称和行也是如此。但它之后我扩展了我的记录器类(布局语法是正确的!)

Question: I want to log method name and line too. but it does not work after I extend my logger class (layout syntaxt is correct!)

我发现扩展记录器示例

I found the extended logger example here

我的代码在

我使用 log4j 2.9.1

推荐答案

我认为有这里有两个问题:

I think there are two questions here:


  1. 创建可以正确记录位置信息的自定义或扩展记录器的好方法是什么?

  2. 应用程序如何在运行时动态选择目标appender?

1。自定义记录器

Log4j如何打印方法名称和行号是通过遍历堆栈跟踪(对于每个事件)并在中找到类/方法调用记录器的应用程序。它可以这样做,因为Log4j知道记录器的完全限定类名(FQCN)。使用自定义记录器时,这需要不同的FQCN,并且还要求堆栈跟踪具有相同的结构:自定义记录器FQCN和应用程序类/方法在堆栈跟踪中与标准Log4j记录器相同的行数。这可能很难实现。

How Log4j can print the method name and line number is by walking the stack trace (for each event) and finding the class/method in the application that called the logger. It can do this because Log4j knows the fully qualified class name (FQCN) of the logger. With a custom logger this requires a different FQCN and also requires that the stack trace has the same structure: the custom logger FQCN and the application class/method are the same number of lines apart in the stack trace as with the standard Log4j logger. This can be tricky to get right.

您是否可以使用记录器包装器实现目标? Log4j附带一个。此工具最初旨在支持自定义日志级别,并记录在手册的自定义日志级别页面上。

Would it be possible for you to achieve your goals with a logger wrapper? Log4j comes with a Logger wrapper generator tool. This tool was originally meant to support custom log levels and is documented on the Custom Log Levels page of the manual.

生成的记录器代码将处理FQCN,您可以将其作为进一步增强功能的基础。

The generated logger code will take care of the FQCN and you can use it as a base for the further enhancements you have in mind.

2。在运行时动态选择Appender

此要求很常见,Log4j2提供内置解决方案,因此您不需要为其创建自定义记录器这个。

This requirement is common enough that Log4j2 provides a built-in solution, so you should not need to create a custom logger for this.

解决这个问题的标准方法是配置。此appender可以将日志事件路由到一组预定义的appender,或者它可以在必要时动态添加新的appender。

The standard way to solve this is to configure a Routing Appender. This appender can route log events to a set of predefined appenders or it can dynamically add new appenders if necessary.

手册页有三个例子,但(我如何动态写入单独的日志文件?)可能非常适合您的要求。该示例使用 ThreadContext 映射来控制后续事件(在当前线程中)记录到哪个日志文件。

The manual page has three examples, but the example in the FAQ page ("How do I dynamically write to separate log files?") may be a fairly close fit to your requirements. That example uses the ThreadContext map to control which log file subsequent events (in the current thread) get logged to.

这篇关于为什么我在扩展log4j时无法在log4j中记录方法名和代码行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 04:28