Log4j 2.9.1 以编程方式将 ThresholdFilter 添加到 Appender

问题描述 投票:0回答:1

我对 log4j 2.9.x 有一个严重的问题 我写“严重”是因为 log4j 的文档真的很垃圾! 此版本没有清晰且最新的文档。即使是现有的文档化代码也不会通过编译器,因为它们时不时地进行重大更改,您无法确定它们记录了哪个版本。我用谷歌搜索了一整天,但我发现的大部分内容也已经过时了。
所以我对以编程方式配置 log4j2 有点困惑。

我想达到的目标:

1) 总是独立于当前日志级别打印到控制台的附加“日志级别”(类似于 gradles LIFECYCLE)

2) 登录到控制台和文件

3) 运行时确定的文件名

我找到了一种使用 XML 实现 1) 和 2) 的方法。我还没有找到适合 3) 的方法。我不喜欢为文件名使用静态上下文或系统变量,因为我预计会出现并发问题。

因此我的方法是以编程方式配置log4j。这是我的配置工厂:

package my.abc

import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.LoggerContext
import org.apache.logging.log4j.core.config.Configuration
import org.apache.logging.log4j.core.config.ConfigurationFactory
import org.apache.logging.log4j.core.config.ConfigurationSource
import org.apache.logging.log4j.core.config.Order
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder
import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration
import org.apache.logging.log4j.core.config.plugins.Plugin

@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(50)
class CustomConfigurationFactory extends ConfigurationFactory {

    private static String MESSAGE_PATTERN = "%d{HH:mm:ss.SSS} [%t] %-5level 123 %logger{36} - %msg%n"

    Level logLevel
    String packageToScan

    CustomConfigurationFactory(Level logLevel, String packageToScan) {
        this.logLevel = logLevel
        this.packageToScan = packageToScan
    }

    private Configuration createConfiguration(final String name, ConfigurationBuilder<BuiltConfiguration> builder) {
        builder.setConfigurationName(name)
        builder.setStatusLevel(Level.ERROR)

        //add appender
        builder.add(createConsoleAppender(builder))
        builder.add(createLifecycleAppender(builder))

        //add logger
        builder.add(createLogger(builder))

//        builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
//                .addAttribute("level", logLevel))

        builder.add(builder.newRootLogger(Level.TRACE).add(builder.newAppenderRef("stdout")))
        return builder.build()
    }

    @Override
    Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
        return getConfiguration(loggerContext, source.toString(), null)
    }

    @Override
    Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
        ConfigurationBuilder<BuiltConfiguration> builder = newConfigurationBuilder()
        return createConfiguration(name, builder)
    }

    @Override
    protected String[] getSupportedTypes() {
        return [{ "*" }]
    }

    private AppenderComponentBuilder createConsoleAppender(ConfigurationBuilder<BuiltConfiguration> builder) {
        AppenderComponentBuilder appenderBuilder = builder.newAppender("stdout", "CONSOLE")
        appenderBuilder.add(builder.newLayout("PatternLayout").
                addAttribute("pattern", MESSAGE_PATTERN))
        appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY,
                Filter.Result.NEUTRAL).addAttribute("marker", "lifecycle"))
//        appenderBuilder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT,
//                Filter.Result.DENY).addAttribute("level", logLevel))
        return appenderBuilder
    }

    private AppenderComponentBuilder createLifecycleAppender(ConfigurationBuilder<BuiltConfiguration> builder) {
        AppenderComponentBuilder appenderBuilder = builder.newAppender("lifecycle", "CONSOLE")
        appenderBuilder.add(builder.newLayout("PatternLayout").
                addAttribute("pattern", "%d{HH:mm:ss.SSS} [%t] LIFECYCLE %logger{36} - %msg%n"))
        appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                .addAttribute("marker", "lifecycle"))
        return appenderBuilder
    }

    private LoggerComponentBuilder createLogger(ConfigurationBuilder<BuiltConfiguration> builder) {
        builder.newLogger(packageToScan, "trace")
                .add(builder.newAppenderRef("stdout"))
                .add(builder.newAppenderRef("lifecycle"))
                .addAttribute("additivity", false)
    }
}

您可能会注意到 2 条注释掉的行。那是我的问题。

当我使用

builder.add(builder.newFilter("ThresholdFilter"...
时,所有日志在不符合日志级别时都会被过滤掉。甚至是“生命周期”——无论如何我都想记录的日志。

当我使用

appenderBuilder.add(builder.newFilter("ThresholdFilter...
我收到错误消息
2017-11-23 12:12:34,905 main ERROR appender CONSOLE has no parameter that matches element ThresholdFilter

奇怪的是。因为当我用 xml 配置所有东西时,我可以做那件事(将 thresholdfilter 添加到单个 appender)并且它有效。

<Appender type="Console" name="STDOUT" >
   <Filters>
      <Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/>
      <Filter type="ThresholdFilter" level="${logLevel}" onMatch="ACCEPT" onMismatch="DENY" />
   </Filters>
   <Layout type="PatternLayout" pattern="${pattern}"/>
</Appender>

要在这里完成整个 xml,适用于需求 1) 和 2)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" strict="true" name="XMLConfigTest"
               packages="org.apache.logging.log4j.test">
    <Properties>
        <Property name="logLevel">error</Property>
        <Property name="filename">target/test.log</Property>
        <Property name="pattern">%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
    </Properties>

    <Appenders>
        <Appender type="Console" name="STDOUT">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/>
                <Filter type="ThresholdFilter" level="${logLevel}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="${pattern}"/>
        </Appender>
        <Appender type="Console" name="LIFECYCLE">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="%d{HH:mm:ss.SSS} [%t] LIFECYCLE %logger{36} - %msg%n"/>
        </Appender>
        <Appender type="File" name="File" fileName="${filename}">
            <Layout type="PatternLayout">
                <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
            </Layout>
        </Appender>
    </Appenders>

    <Loggers>
        <Logger name="my.abc" level="trace" additivity="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="LIFECYCLE"/>
        </Logger>

        <Root level="error">
            <AppenderRef ref="STDOUT"/>
        </Root>
    </Loggers>
</Configuration>

必须有一种方法可以将 ThresholdFilter 添加到 appender。我找到了 ThresholdFilter 类。但我完全不知道如何使用它。实例化工作

ThresholdFilter.createFilter(...)
但我可以在哪里添加它?

长话短说..

如何在没有 xml 的情况下将 ThresholdFilter 添加到单个 Appender?

java log4j log4j2 configure
1个回答
0
投票

仅供参考

我解决所有需求的方法:

我切换回 XML 并添加了一些动态属性。一些属性取自系统属性。如果它们不存在,将采用定义为 xml 属性的默认值。

特别查看属性“logFile”。它混合了系统属性(用于修复日志文件的路径)并由 log4j 动态确定。这不是真正的线程安全。但在我们的情况下可以接受。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" strict="true" name="XMLConfig"
               packages="org.apache.logging.log4j.test">
    <Properties>
        <Property name="logLevel">INFO</Property>
        <Property name="logFileDir">log/</Property>
        <Property name="logLevelFile">OFF</Property>
        <Property name="logFile">${sys:logFileDir}ConfigMgmt-${date:yyyyMMdd_HHmmss-SSS}.log</Property>
        <Property name="packageName">com.hamburgsud</Property>
        <Property name="messagePattern">%d{HH:mm:ss.SSS} (Test) %-5level %logger{1.} - %msg%n</Property>
    </Properties>

    <Appenders>
        <!-- Console Logging -->
        <Appender type="Console" name="STDOUT">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/>
                <Filter type="ThresholdFilter" level="${sys:logLevel}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="${sys:messagePattern}"/>
        </Appender>
        <!-- LIFECYCLE is analog to gradles lifecycle log level - means always logging
             When the marker for lifecycle-logging is added, the message will always be logged -->
        <Appender type="Console" name="LIFECYCLE">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="%logger{1.} - %msg%n"/>
        </Appender>


        <!-- File Logging -->
        <Appender type="File" name="FILE" fileName="${sys:logFile}" createOnDemand="true">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/>
                <Filter type="ThresholdFilter" level="${sys:logLevelFile}" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="${sys:messagePattern}"/>
        </Appender>
        <!-- Counterpart to consoles LIFECYCLE logging. When file logging is activated and a log is marked with
             lifecycle, the message will be logged to the file too. -->
        <Appender type="File" name="FILELIFECYCLE" fileName="${sys:logFile}" createOnDemand="true">
            <Filters>
                <Filter type="MarkerFilter" marker="lifecycle" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <Layout type="PatternLayout" pattern="%logger{1.} - %msg%n"/>
        </Appender>
    </Appenders>

    <Loggers>
        <Root level="trace" additivity="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="LIFECYCLE"/>
            <AppenderRef ref="FILE"/>
            <AppenderRef ref="FILELIFECYCLE"/>
        </Root>
    </Loggers>
</Configuration>
© www.soinside.com 2019 - 2024. All rights reserved.