【问题标题】:Passing dynamic value to log4j2 xml config将动态值传递给 log4j2 xml 配置
【发布时间】:2018-08-24 09:21:59
【问题描述】:

我正在尝试使用 log4j2 编写一个日志模块,该模块需要以 json 的形式将日志写入控制台 STDOUT。

为此,我尝试以 JSON 的形式使用 PatternLayout,如下所示。

在将值从我的代码动态传递到 log4j2.xml 配置文件以在写入日志时在运行时替换它们时遇到一些困难。

我尝试使用 StructuredDataMessage 和 MapMessages 替换 Map 中的值,如 https://logging.apache.org/log4j/2.0/manual/lookups.html 中所述。

我也尝试了 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}

如果您看到 lastvalue 系统属性未按预期通过代码值进行替换。

【问题讨论】:

    标签: java logging log4j log4j2


    【解决方案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);,它将一个名为mapMsgMapMessage 实例传递给info 方法。由于消息是MapMessage 的一个实例,并且它包含一个名为name 的键,因此映射查找将用它为消息中的name 键找到的值替换${map: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 的架构是如何设计的。祝你好运!

    【讨论】:

    • 感谢详细的解释,对我帮助很大。所以基本上因为 StructuredDataMessage 和 StringMapMessage 依赖于日志消息,所以我也需要将它们传递给我的记录器,对吗?如果我还需要传递我的日志消息,除了地图之外,我可以将它们作为其他参数单独传递吗?还是只需要作为映射键传递?
    • 如果您使用结构化数据查找,那么您必须将StructuredDataMessage 的实例传递给记录器,以便查找可以在该消息中找到值。同样,如果您使用 Map Lookup,则必须传递 MapMessage 的实例(我在上面的示例中使用了 StringMapMessage)。我所指的实例是消息,因此您要记录的所有数据都需要包含在该实例中。见javadoc for the Logger class
    【解决方案2】:

    在 xml 文件本身中编辑 PatternLayout 是不可行的,如果值是静态的并且保持不变,那么它可以工作,否则您需要通过扩展内置方法来实现自定义模式,然后相应地格式化您的消息。 下面的链接会让你知道同样的事情。

    https://blog.10pines.com/2020/03/02/log-custom-json-with-log4j2/

    【讨论】:

    • 可以添加更多详细信息吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多