我正在尝试使用log4j2编写一个日志记录模块,该模块需要以json的形式将日志写入控制台STDOUT。
为此,我尝试使用如下所示的JSON形式的PatternLayout。
在将代码中的值动态传递到log4j2.xml配置文件以在运行时替换日志时替换它们时,我遇到了一些困难。
我尝试使用StructuredDataMessage和MapMessages来替换https://logging.apache.org/log4j/2.0/manual/lookups.html中提到的Map中的值。
我也尝试了StrLookup和ContextMaplookup,但到目前为止没有成功。
Below is my xml config
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="App" packages="com.test.common.logging">
<Properties>
<Property name="filename">target/rolling1/rollingtest.log</Property>
<Property name="maptype">$${map:type}</Property>
</Properties>
<ThresholdFilter level="debug"/>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%highlight{{'logged time': '%d{dd MMM yyyy HH:mm:ss}',
'LEVEL' : '%level',
'CLASS' : '%c{-1}',
'Module' : '[%t]',
'message' : '%m',
'error' : '%exception',
'class' : '%C',
'threadid' : '%tid',
'threadname' : '%thread',
'whatisthis' : '${filename}',
'processid' : '%pid',
'logdir' : '$${sd:type}'
'location' : '${log4j:configLocation}'
'systemproperty' : '$${ctx:key-}'
}}%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
下面是我的代码,试图使用结构化数据消息,strlookup和mapmessage传递动态值
public class App
{
public static void main( String[] args )
{
Logger logger = LogManager.getLogger(App.class);
// ConfigurationBuilder<BuiltConfiguration> builder
// = ConfigurationBuilderFactory.newConfigurationBuilder();
//
// LayoutComponentBuilder standard
// = builder.newLayout("PatternLayout");
// standard.
//
System.out.println( "Hello World!" );
StructuredDataMessage message = new StructuredDataMessage("1", "name", "string");
message.put("1", "nme");
// MapMessage mapm = new MapMessage(map)
MapMessage map = new MapMessage();
map.put("type", "value");
map.put("key", "value");
map.put("name", "arun");
StrLookup strlook = new StrLookup() {
public String lookup(LogEvent event, String key) {
// TODO Auto-generated method stub
return null;
}
public String lookup(String key) {
// TODO Auto-generated method stub
return "value";
}
};
ContextMapLookup lookup = new ContextMapLookup();
System.out.println(lookup.lookup("key"));
System.out.println(strlook.lookup("key"));
// MapLookup.setMainArguments(args);
System.setProperty("log_dir", App.class.getSimpleName());;
logger.trace("trace log message");
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
logger.fatal("Fatal log message");
logger.info("Info log message[]");
logger.error("null exception[]", new NullPointerException());
// Lay
}
}
我的输出:
Hello World!
null
value
[36m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'DEBUG', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'Debug log message', 'error' : '', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'DEBUGid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
[32m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'INFO', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'Info log message', 'error' : '', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'INFOid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
[1;31m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'ERROR', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'Error log message', 'error' : '', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'ERRORid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
[1;31m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'FATAL', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'Fatal log message', 'error' : '', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'FATALid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
[32m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'INFO', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'Info log message[]', 'error' : '', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'INFOid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
[1;31m{'logged time': '24 Aug 2018 12:32:51', 'LEVEL' : 'ERROR', 'CLASS' : 'test.common.logging.App', 'Module' : '[main]', 'message' : 'null exception[]', 'error' : ' java.lang.NullPointerException
at com.test.common.logging.App.main(App.java:69)
', 'class' : 'com.test.common.logging.App', 'threadid' : '1', 'threadname' : 'main', 'whatisthis' : 'target/rolling1/rollingtest.log', 'processid' : 'ERRORid', 'logdir' : '${sd:type}' 'location' : '/Users/parunkarthick/ferry-commons/common/logging/target/classes/log4j2.xml' 'systemproperty' : '${ctx:key}' [m}
如果您看到的最后一个值系统属性未通过替换代码值反映出预期效果,那么
答案 0 :(得分:1)
我认为您对log4j2有一些基本的误解。我认为最好的办法不是为我列出您在代码中看到的所有问题,而是为我提供一些示例代码并解释输出。我认为当您看到一些有效的代码时,您会明白哪里出了问题。
出于本示例的目的,我通过删除看起来有效的元素并集中于那些不起作用的元素来简化了log4j2配置文件。我将PatternLayout
更改为以下内容:
<PatternLayout pattern="{'LEVEL' : '%level', 'typeFromStructMsg' : '${sd:type}', 'contextValue' : '${ctx:myContextKey}', 'nameFromMapMsg' : '${map:name}', 'mySysProperty' : '${sys:mySysProperty}'}%n" />
我还修改了您提供的App
类:
package example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.message.MapMessage;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.apache.logging.log4j.message.StringMapMessage;
import org.apache.logging.log4j.message.StructuredDataMessage;
public class App {
private static final Logger logger = LogManager.getLogger();
public static void main( String[] args )
{
ThreadContext.put("myContextKey", "myContextValue");
StructuredDataMessage structMsg = new StructuredDataMessage("1", "name", "string");
StringMapMessage mapMsg = new StringMapMessage();
mapMsg.put("name", "arun");
System.setProperty("mySysProperty", "sys prop value");
logger.info(mapMsg);
logger.warn(structMsg);
logger.error("Error log message");
}
}
运行App
类时,将生成以下控制台输出:
{'LEVEL' : 'INFO', 'typeFromStructMsg' : '${sd:type}', 'contextValue' : 'myContextValue', 'nameFromMapMsg' : 'arun', 'mySysProperty' : 'sys prop value'}
{'LEVEL' : 'WARN', 'typeFromStructMsg' : 'string', 'contextValue' : 'myContextValue', 'nameFromMapMsg' : '${map:name}', 'mySysProperty' : 'sys prop value'}
{'LEVEL' : 'ERROR', 'typeFromStructMsg' : '${sd:type}', 'contextValue' : 'myContextValue', 'nameFromMapMsg' : '${map:name}', 'mySysProperty' : 'sys prop value'}
请注意,在输出的第一行中,我们看到:'nameFromMapMsg' : 'arun'
,而在其他行中,我们看到:'nameFromMapMsg' : '${map:name}'
第一行输出是由以下代码行生成的:logger.info(mapMsg);
,它将名为MapMessage
的{{1}}的实例传递给mapMsg
方法。由于该消息是info
的实例,并且包含一个名为MapMessage
的键,因此映射查找会将name
替换为它在消息中为${map:name}
键找到的值。这就是为什么只有第一行输出显示name
的原因,而其他输出行则根据不是'nameFromMapMsg' : 'arun'
实例的消息生成。
同样,请注意在输出的第二行中我们如何看到MapMessage
。这是因为日志是从'typeFromStructMsg' : 'string'
生成的,该StructuredDataMessage
是用“字符串”类型定义的:
StructuredDataMessage structMsg = new StructuredDataMessage("1", "name", "string");
在输出的其他行中,我们没有传递StructuredDataMessage
,因此在这些行中我们看到'typeFromStructMsg' : '${sd:type}'
,因为log4j2找不到该类型的值。
最后,请注意,在所有输出行中,我们如何看到:'mySysProperty' : 'sys prop value'
。这是因为系统属性查找不取决于传递给记录器的消息类型。此查找始终能够找到系统属性mySysProperty
的值,因为我们定义了它:
System.setProperty("mySysProperty", "sys prop value");
并且,正如我之前所说,系统属性与消息无关(它们不存储在消息中)。
'contextValue' : 'myContextValue'
也是如此-ThreadContext
与消息无关,并且由于我们为此键定义了一个值:
ThreadContext.put("myContextKey", "myContextValue");
无论发送到记录器的消息类型如何,查找总是能够找到该值。
我希望该示例代码有助于说明如何使用某些查询以及如何设计log4j2的体系结构。祝你好运!
答案 1 :(得分:0)
在xml文件本身中编辑PatternLayout是不可行的,如果值是静态的并且将保持不变,那么它将起作用,否则,您需要通过扩展内置方法来实现自定义模式,然后相应地格式化消息。 以下链接将使您了解相同的情况。
https://blog.10pines.com/2020/03/02/log-custom-json-with-log4j2/