【问题标题】:How do I conditionally add log4j2 appender depending on java system property?如何根据 java 系统属性有条件地添加 log4j2 appender?
【发布时间】:2016-01-28 07:06:54
【问题描述】:

我试图弄清楚如何根据是否给定/设置 java 系统属性来将附加程序添加到记录器。

假设我有这样的基本配置:

<Logger name="myLogger" level="info" additivity="false">
  <AppenderRef ref="myAppender1" />
  <AppenderRef ref="myAppender2" />
</Logger>

所以现在我想找到一种方法,如果我提供参数 -PaddAppender2,则有条件地仅添加第二个附加程序。像这样的:

<Logger name="myLogger" level="info" additivity="false">
  <AppenderRef ref="myAppender1" />
  <?if (${sys:enableAppender2:-false) == "true"}>
  <AppenderRef ref="myAppender2" />
  </?if> 
</Logger>

我该怎么做?

我知道我可以例如在给定属性(“logLevel”)上使级别动态化(如果没有给出属性,则“info”是默认值):

<Logger name="test" level="${sys:logLevel:-info}" additivity="false">

我查看了documentation for filters,但我无法弄清楚。当然,如果过滤器甚至是正确的方法。

【问题讨论】:

    标签: java log4j log4j2


    【解决方案1】:

    没有任何脚本的解决方案:

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="error" strict="true">
        <Properties>
            <Property name="appenderToUse">stdout_${sys:LOG4J_LAYOUT:-plain}</Property>
        </Properties>
    
        <Appenders>
            <Appender type="Console" name="stdout_plain">
                <Layout type="PatternLayout" pattern="%d [%t] %-5p %c - %m%n"/>
            </Appender>
    
            <Appender type="Console" name="stdout_json">
                <Layout type="JSONLayout" compact="true" eventEol="true" stacktraceAsString="true" properties="true"/>
            </Appender>
        </Appenders>
    
        <Loggers>
            <Root level="info">
                <AppenderRef ref="${appenderToUse}"/>
            </Root>
        </Loggers>
    </Configuration>
    

    【讨论】:

    • 结合NullAppender,这个解决方案让我很开心——现在我可以在需要时打开/关闭Console appender!
    • 即使没有明确的&lt;Property&gt; 声明也可以内联工作
    【解决方案2】:

    Robert 提供的解决方案有效,但效率不高,因为每个日志记录都会评估一次脚本。

    只评估一次脚本的更有效的解决方案是将ScriptAppenderSelectorNullAppender 一起使用:

    根据文档:

    ScriptAppenderSelector

    构建配置时,ScriptAppenderSelector appender 会调用一个脚本来计算 appender 名称。然后,Log4j 使用 ScriptAppenderSelector 的名称创建 AppenderSet 下列出的附加程序之一。配置完成后,Log4j 会忽略 ScriptAppenderSelector。

    NullAppender

    一个忽略日志事件的 Appender。用于与 1.2 版兼容并方便编写 ScriptAppenderSelector

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn" name="ScriptAppenderSelectorExample">
        <Appenders>
            <ScriptAppenderSelector name="SelectConsole">
                <Script language="groovy"><![CDATA[
                    if (System.getProperty("CONSOLE_APPENDER_ENABLED", 'true').equalsIgnoreCase('true')) {
                        return "Console"
                    } else {
                        return "Null"
                    }
                ]]></Script>
                <AppenderSet>
                    <Console name="Console" target="SYSTEM_OUT">
                        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
                    </Console>
                    <Null name="Null" />
                </AppenderSet>
            </ScriptAppenderSelector>
    
            <ScriptAppenderSelector name="SelectFile">
                <Script language="groovy"><![CDATA[
                    if (System.getProperty("FILE_APPENDER_ENABLED", 'true').equalsIgnoreCase('true')) {
                        return "File"
                    } else {
                        return "Null"
                    }
                ]]></Script>
                <AppenderSet>
                    <File name="File" fileName="application.log">
                        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
                    </File>
                    <Null name="Null" />
                </AppenderSet>
            </ScriptAppenderSelector>
    
            <ScriptAppenderSelector name="SelectSMTP">
                <Script language="groovy"><![CDATA[
                    if (System.getProperty("SMTP_APPENDER_ENABLED", 'true').equalsIgnoreCase('true')) {
                        return "SMTP"
                    } else {
                        return "Null"
                    }
                ]]></Script>
                <AppenderSet>
                    <SMTP name="SMTP"
                          subject="App: Error"
                          from="log4j@example.com"
                          to="support@example.com"
                          smtpHost="smtp.example.com"
                          smtpPort="25"
                          bufferSize="5">
                    </SMTP>
                    <Null name="Null" />
                </AppenderSet>
            </ScriptAppenderSelector>
        </Appenders>
        <Loggers>
            <Root level="info">
                <AppenderRef ref="SelectConsole"/>
                <AppenderRef ref="SelectFile"/>
                <AppenderRef ref="SelectSMTP"/>
            </Root>
        </Loggers>
    </Configuration>
    

    参考文献

    【讨论】:

    • groovy 好像不支持
    【解决方案3】:

    类似于rgoers 解决方案,但使用 而不是。该解决方案受益于 Nashorn 引擎是 Java 8,因此不需要额外的依赖项。

    <Scripts>
      <Script name="isAppender2Enabled" language="nashorn"><![CDATA[
        var System = Java.type('java.lang.System'),
            Boolean = Java.type('java.lang.Boolean');
        Boolean.parseBoolean(System.getProperty('enableAppender2', 'false'));
      ]]></Script>
    </Scripts>
    
    <Loggers>
      <Logger name="myLogger" level="info" additivity="false">
        <AppenderRef ref="myAppender1" />
        <AppenderRef ref="myAppender2">
          <ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
            <ScriptRef ref="isAppender2Enabled" />
          </ScriptFilter>
        </AppenderRef>
      </Logger>
    </Loggers>
    

    请注意,ScriptFilter 在每次发生 Log4j 事件时都会评估脚本。因此,可以在运行时启用/禁用附加程序(通过更改系统属性的值)并立即生效。另一方面,脚本评估可能会对日志记录性能产生负面影响。

    【讨论】:

    • 警告:Nashorn JavaScript 引擎在 Java 11 中被标记为已弃用 - 请参阅 Mark Reinhold 的推文:twitter.com/mreinhold/status/1006572693825601536。 Java 11 中产生以下警告:Warning: Nashorn engine is planned to be removed from a future JDK release。您可以使用 JVM 选项来阻止打印警告:-Dnashorn.args="--no-deprecation-warning"
    【解决方案4】:

    我无法单独通过配置文件找到解决方案,但我找到了一个以编程方式解决问题的解决方案。

    请注意,在我们的特定情况下,我们总是记录到“本地日志”(“splunk local”),但在特定情况下(由属性控制),我们还希望将相同的信息记录到另一个位置(即不是相对的)并定期读取并转发到 splunk 服务器(“splunk 转发器”)。

    这就是为什么我们可以将大部分属性从一个记录器复制到另一个记录器。

    private static final Logger SPLUNK_LOG = getLogger();
    
    private static Logger getLogger() {
        if (!BooleanUtils.toBoolean(SystemUtils.getJavaPropertyValue(ENABLE_PROPERTY_NAME, "false"))) {
            return LoggerFactory.getLogger(SPLUNK_LOG_NAME);
        } else {
            LOG.info("Dynamically adding splunk forwarder appender");
            try {
                final LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
                final Configuration configuration = loggerContext.getConfiguration();
    
                // configure appender based on local splunk appender
                final RollingFileAppender splunkLocal = (RollingFileAppender) configuration.getAppender(LOCAL_LOG_NAME);
                final RollingFileAppender splunkForwarder = RollingFileAppender.createAppender(FORWARDER_FILE_NAME,
                        FORWARDER_FILE_PATTERN, FORWARDER_APPEND, FORWARDER_NAME, null, null, null,
                        splunkLocal.getManager().getTriggeringPolicy(), splunkLocal.getManager().getRolloverStrategy(),
                        splunkLocal.getLayout(), splunkLocal.getFilter(), null, FORWARDER_ADVERTISE, null, null);
                splunkForwarder.start();
    
                // add splunk forwarder appender to splunk logger
                final LoggerConfig loggerConfig = configuration.getLoggerConfig(SPLUNK_LOG_NAME);
                loggerConfig.addAppender(splunkForwarder, Level.INFO, null);
    
                LOG.info("Successfully added splunk forwarder appender");
                return loggerContext.getLogger(SPLUNK_LOG_NAME);
            } catch (Exception ex) {
                throw new IllegalStateException("Failed to dynamically add splunk forwarder appender", ex);
            }
        }
    }
    

    如果有人知道如何仅通过配置文件做到这一点,那就太好了。

    【讨论】:

      【解决方案5】:

      处理这种情况的方式是使用过滤器。在这种情况下,您可以使用脚本过滤器。

      <Logger name="myLogger" level="info" additivity="false">
        <AppenderRef ref="myAppender1" />
        <AppenderRef ref="myAppender2">
           <ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
            <Script language="groovy"><![CDATA[
               return System.getProperty("enableAppender2", "false").equalsIgnoreCase("true");
            ]]></Script>
          </ScriptFilter>
        </AppenderRef>
      </Logger>
      

      【讨论】:

        【解决方案6】:

        基于此线程中的一些想法,这是我为有条件地登录到控制台所做的。

        示例用例

        1. 始终记录到文件附加程序。
        2. 仅在某些环境中登录到控制台。

        解决方案

        • 对于控制台日志记录,设置系统属性additional.log.appender=console
        • 或者,通过省略此属性来禁用控制台日志记录。
        • 在 Logger AppenderRef 中,使用 ${sys:additional.log.appender:-null}
          • 如果设置了系统属性,则将日志发送到控制台附加程序,如果未设置,则默认为空附加程序。 (null appender 忽略日志)

        系统属性

        # set for console logging
        additional.log.appender=console
        

        log4j2.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <Configuration>
            <Appenders>
                <RollingFile name="file"
                    fileName="my-file.log"
                    filePattern="my-file%i.log">
                    <PatternLayout pattern="%d %5p [%t] %c - %m%n" />
                    <Policies>
                        <SizeBasedTriggeringPolicy size="10 MB" />
                    </Policies>
                    <DefaultRolloverStrategy max="10" />
                </RollingFile>
         
                <Console name="console" target="SYSTEM_OUT">
                    <PatternLayout pattern="%d %5p [%t] %c - %m%n" />
                </Console>
         
                <Null name="null" />
            </Appenders>
         
            <Loggers>
                <Logger name="com.acme" level="DEBUG">
                </Logger>
         
                <Root level="INFO">
                    <AppenderRef ref="file" />
                    <AppenderRef ref="${sys:additional.log.appender:-null}" />
                </Root>
            </Loggers>
        </Configuration>
        

        【讨论】:

          猜你喜欢
          • 2023-03-17
          • 1970-01-01
          • 2013-08-14
          • 1970-01-01
          • 1970-01-01
          • 2012-04-01
          • 2022-01-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多