我扩展了log42记录器.
想法:
我应该将枚举传递给log方法,以便在运行时选择appender.
我的界面:
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).
所以. 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) {
}
}
上下文工厂:
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);
但问题是,如果我使用扩展自定义记录器,我无法记录方法名称和行.布局是正确的!如果我不使用扩展自定义记录器,也会记录mehtod名称和行!
<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>
题:
我也想记录方法名称和行.但是我扩展我的记录器类后它不起作用(布局syntaxt是正确的!)
我找到了扩展记录器示例here
我的代码在gitlab这里
我使用log4j 2.9.1
解决方法:
我认为这里有两个问题:
>创建可以正确记录位置信息的自定义或扩展记录器的好方法是什么?
>应用程序如何在运行时动态选择目标appender?
1.自定义记录器
Log4j如何打印方法名称和行号是通过遍历堆栈跟踪(对于每个事件)并在调用记录器的应用程序中查找类/方法.它可以这样做,因为Log4j知道记录器的完全限定类名(FQCN).使用自定义记录器时,这需要不同的FQCN,并且还要求堆栈跟踪具有相同的结构:自定义记录器FQCN和应用程序类/方法与堆栈跟踪中的行数相同,与标准Log4j记录器相同.为了做到这一点,这可能很棘手.
您是否可以使用记录器包装器实现目标? Log4j附带一个Logger wrapper generator tool.此工具最初用于支持自定义日志级别,并记录在手册的“自定义日志级别”页面上.
生成的记录器代码将处理FQCN,您可以将其用作进一步增强功能的基础.
2.在运行时动态选择Appender
此要求很常见,Log4j2提供了内置解决方案,因此您不需要为此创建自定义记录器.
解决此问题的标准方法是配置Routing Appender.此appender可以将日志事件路由到一组预定义的appender,或者它可以在必要时动态添加新的appender.
手册页有三个示例,但example in the FAQ page(“如何动态写入单独的日志文件?”)可能非常适合您的要求.该示例使用ThreadContext映射来控制后续事件(在当前线程中)记录到哪个日志文件.