如何纯程序化地配置log4j 2.x?

31

如何使用纯编程方式(没有任何格式的配置文件)配置log4j 2.3控制台附加器

基本上,我正在寻找此1.x代码的2.x版本。

在我的类中,我将使用

private static final Logger logger = LogManager.getLogger();
// 
    // some method
       logger.debug(someString);

没有任何配置,我(如预期)遇到了以下错误:

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

虽然使用配置文件的方法似乎已经充分记录, 但我找不到一个仅包含代码的例子。

我找到的最接近的例子是这篇文章, 但它仍然使用了一个虚拟文件。

以下是我最好的尝试(尽管完全失败):

private static void configureLog4J() {
    PatternLayout layout = PatternLayout.createDefaultLayout();
    ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(layout);
    LoggerConfig loggerConfig = new LoggerConfig();
    loggerConfig.addAppender(appender, DEBUG, null);
}

我有遗漏什么吗?

如果这仍然是一个RTFM案例,请指引我正确的方向。


直接使用log4j2,不需要任何slf4j吗? - Sotirios Delimanolis
@SotiriosDelimanolis - 是的,那就是意图。 - PM 77-1
1
直到最近,logback也缺少纯编程配置。请参见https://dev59.com/Wn3aa4cB1Zd3GeqPd3wK。在该SO帖子中有一个log4j2链接,我无法使用我的init代码启动log4j2,但是最新的logback将允许您控制init过程。 - Adam Gent
5个回答

26
package com;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.nio.charset.Charset;

public class MyLoggerTest  {

    public static void main(String[] args){
        LoggerContext context= (LoggerContext) LogManager.getContext();
        Configuration config= context.getConfiguration();

        PatternLayout layout= PatternLayout.createLayout("%m%n", null, null, Charset.defaultCharset(),false,false,null,null);
        Appender appender=ConsoleAppender.createAppender(layout, null, null, "CONSOLE_APPENDER", null, null);
        appender.start();
        AppenderRef ref= AppenderRef.createAppenderRef("CONSOLE_APPENDER",null,null);
        AppenderRef[] refs = new AppenderRef[] {ref};
        LoggerConfig loggerConfig= LoggerConfig.createLogger("false", Level.INFO,"CONSOLE_LOGGER","com",refs,null,null,null);
        loggerConfig.addAppender(appender,null,null);

        config.addAppender(appender);
        config.addLogger("com", loggerConfig);
        context.updateLoggers(config);

        Logger logger=LogManager.getContext().getLogger("com");
        logger.info("HELLO_WORLD");


    }
}

不确定这是否符合您的要求。这将创建一个默认配置并添加一个控制台日志记录器。但是,LogManager.getLogger()无法工作,并且使用LogManager.getContext().getLogger()不允许使用日志记录器层次结构。老实说,我不建议采用编程方法,因为Log4j2对此很敏感。


14
这里是一个完整的log4j 2.8编程配置示例。它有三个appender:RollingFile、JDBC和SMTP。
有1个类和2个属性配置文件,其中一个用于将该类注册为log4j2 configurationFactory,另一个用于设置属性,例如日志文件目录。
类#1:MPLoggingConfiguration。
package com.websitester.config;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.zip.Deflater;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.SmtpAppender;
import org.apache.logging.log4j.core.appender.db.ColumnMapping;
import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig;
import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
import org.apache.logging.log4j.core.appender.db.jdbc.DataSourceConnectionSource;
import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
import org.apache.logging.log4j.core.appender.rolling.OnStartupTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.config.AppenderRef;
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.DefaultConfiguration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Order;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.layout.HtmlLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;

public class MPLoggingConfiguration {

    public static final String WEBSITESTER_LOGGER_NAME = "com.websitester";
    public static final String FILE_PATTERN_LAYOUT = "%n[%d{yyyy-MM-dd HH:mm:ss}] [%-5p] [%l]%n\t%m%n%n";
    public static final String LOG_FILE_NAME = "awmonitor.log";
    public static final String LOG_FILE_NAME_PATTERN = "awmonitor-%i.log";  

    /**
     * Just to make JVM visit this class to initialize the static parts.
     */
    public static void configure() {
    }

    @Plugin(category = ConfigurationFactory.CATEGORY, name = "MPConfigurationFactory")
    @Order(15)
    public static class MPConfigurationFactory  extends ConfigurationFactory {
        public static final String[] SUFFIXES = new String[] {".json", "*"};

        @Override
        protected String[] getSupportedTypes() {
            return SUFFIXES;
        }

        @Override
        public Configuration getConfiguration(LoggerContext arg0, ConfigurationSource arg1) {
            return new Log4j2Configuration(arg1);
        }
    }

    private static class Log4j2Configuration extends DefaultConfiguration {

        public Log4j2Configuration(ConfigurationSource source) {
            super.doConfigure();
            setName("mp-log4j2");

            String logFilePath = "/log/weblogic/wl-moniport/";

            // LOGGERS
            //      com.websitester
            AppenderRef[] refs = new AppenderRef[] {};
            Property[] properties = new Property[] {};
            LoggerConfig websitesterLoggerConfig = LoggerConfig.createLogger(true, Level.INFO, WEBSITESTER_LOGGER_NAME, "true", refs, properties, this, null);
            addLogger(WEBSITESTER_LOGGER_NAME, websitesterLoggerConfig);


            // APPENDERS
            final Charset charset = Charset.forName("UTF-8");

            //      MP ROLLING FILE
            TriggeringPolicy mpFileCompositePolicy = CompositeTriggeringPolicy.createPolicy(
                    SizeBasedTriggeringPolicy.createPolicy("3 M"),
                    OnStartupTriggeringPolicy.createPolicy(1));
            final DefaultRolloverStrategy mpFileRolloverStrategy = DefaultRolloverStrategy.createStrategy("9", "1", "max", Deflater.NO_COMPRESSION + "", null, true, this);
            Layout<? extends Serializable> mpFileLayout = PatternLayout.newBuilder()
                    .withPattern(FILE_PATTERN_LAYOUT)
                    .withPatternSelector(null)
                    .withConfiguration(this)
                    .withRegexReplacement(null)
                    .withCharset(charset)
                    .withAlwaysWriteExceptions(isShutdownHookEnabled)
                    .withNoConsoleNoAnsi(isShutdownHookEnabled)
                    .withHeader(null)
                    .withFooter(null)
                    .build();
            Appender mpFileAppender = RollingFileAppender.newBuilder()
                    .withAdvertise(Boolean.parseBoolean(null))
                    .withAdvertiseUri(null)
                    .withAppend(true)
                    .withBufferedIo(true)
                    .withBufferSize(8192)
                    .setConfiguration(this)
                    .withFileName(logFilePath + LOG_FILE_NAME)
                    .withFilePattern(logFilePath + LOG_FILE_NAME_PATTERN)
                    .withFilter(null)
                    .withIgnoreExceptions(true)
                    .withImmediateFlush(true)
                    .withLayout(mpFileLayout)
                    .withCreateOnDemand(false)
                    .withLocking(false)
                    .withName("error_file_web")
                    .withPolicy(mpFileCompositePolicy)
                    .withStrategy(mpFileRolloverStrategy)
                    .build();
            mpFileAppender.start();
            addAppender(mpFileAppender);
            getLogger(WEBSITESTER_LOGGER_NAME).addAppender(mpFileAppender, Level.DEBUG, null);


            // JDBC
            if (System.getProperty("log4jjdbcjndiName") != null){
                ColumnConfig[] columnConfigs = new ColumnConfig[] {
                        ColumnConfig.newBuilder()
                        .setConfiguration(this)
                        .setName("DATED")
                        .setPattern(null)
                        .setLiteral(null)
                        .setEventTimestamp(true)
                        .setUnicode(false)
                        .setClob(false)
                        .build(),
                        ColumnConfig.newBuilder()
                        .setConfiguration(this)
                        .setName("LOGGER")
                        .setPattern("%logger")
                        .setLiteral(null)
                        .setEventTimestamp(false)
                        .setUnicode(false)
                        .setClob(false)
                        .build(),
                        ColumnConfig.newBuilder()
                        .setConfiguration(this)
                        .setName("LOG_LEVEL")
                        .setPattern("%level")
                        .setLiteral(null)
                        .setEventTimestamp(false)
                        .setUnicode(false)
                        .setClob(false)
                        .build(),
                        ColumnConfig.newBuilder()
                        .setConfiguration(this)
                        .setName("MESSAGE")
                        .setPattern("%message")
                        .setLiteral(null)
                        .setEventTimestamp(false)
                        .setUnicode(false)
                        .setClob(false)
                        .build(),
                        ColumnConfig.newBuilder()
                        .setConfiguration(this)
                        .setName("NODE")
                        .setPattern("" + System.getProperty("log4jmpserverid"))
                        .setLiteral(null)
                        .setEventTimestamp(false)
                        .setUnicode(false)
                        .setClob(false)
                        .build()
                };
                ConnectionSource dataSourceConnectionSource = DataSourceConnectionSource.createConnectionSource(System.getProperty("log4jjdbcjndiName"));
                if (dataSourceConnectionSource != null){
                    Appender jdbcAppender = JdbcAppender.newBuilder()
                            .setBufferSize(0)
                            .setColumnConfigs(columnConfigs)
                            .setColumnMappings(new ColumnMapping[]{})
                            .setConnectionSource(dataSourceConnectionSource)
                            .setTableName("MTDTLOGS")
                            .withName("databaseAppender")
                            .withIgnoreExceptions(true)
                            .withFilter(null)
                            .build();
                    jdbcAppender.start();
                    addAppender(jdbcAppender);
                    getLogger(WEBSITESTER_LOGGER_NAME).addAppender(jdbcAppender, Level.WARN, null);
                }
            };

            // SMTP
            if (System.getProperty("log4jemailSubject") != null){
                if (System.getProperty("log4jemailLevel").equalsIgnoreCase("error")) {
                    Layout<? extends Serializable> mpHtmlLayout = HtmlLayout.createLayout(false, "Monitor de Portales", null, null, "x-small", null);

                    Appender smtpAppender = SmtpAppender.createAppender(
                            this,
                            "SMTP",
                            System.getProperty("log4jemailTo"), 
                            System.getProperty("log4jemailcc"), 
                            System.getProperty("log4jemailbcc"), 
                            System.getProperty("log4jemailFrom"), 
                            System.getProperty("log4jemailreplyTo"), 
                            System.getProperty("log4jemailSubject"), 
                            System.getProperty("log4jemailProtocol"), 
                            System.getProperty("log4jemailHost"), 
                            System.getProperty("log4jemailPort"), 
                            System.getProperty("log4jemailUserName"), 
                            System.getProperty("log4jemailPassword"), 
                            "false", 
                            "50", 
                            mpHtmlLayout, 
                            null, 
                            "true");
                    smtpAppender.start();
                    addAppender(smtpAppender);
                    getLogger(WEBSITESTER_LOGGER_NAME).addAppender(smtpAppender, Level.ERROR, null);
                }
            }
        }
    }
}

配置文件:src/main/resources/log4j2.component.properties

log4j.configurationFactory=com.websitester.config.MPLoggingConfiguration$MPConfigurationFactory
log4j.configurationFile=log4j2websitester.json

配置文件:src / main / resources / log4j2websitester.json

{"logFilePath" : "/log/weblogic/wl-moniport/"}

在我的情况下,我在其他类中设置所有属性(通过System.getProperty访问MPLoggingConfiguration),例如:
System.setProperty("log4jjdbcjndiName", "weblogic-monitor");

当您更改了一些属性并希望重新配置log4j2时,您必须进行以下调用:

final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
ctx.reconfigure();

希望这能有所帮助。

错误状态记录器:未找到log4j2配置文件。使用默认配置:仅将错误记录到控制台。设置系统属性“log4j2.debug”以显示Log4j2内部初始化日志...无法正确获取配置,请帮忙解决。 - this_is_om_vm
我编辑响应,更改第一个配置文件,包括“configurationFile”属性,并包含新配置文件的内容。 - Carlos Cuesta
1
这段程序代码已经无法在2.11.x API上使用了 - 你需要使用ConfigurationBuilder https://www.baeldung.com/log4j2-programmatic-config - madduci
似乎连接源仍然依赖于配置文件。无法找到完全以编程方式配置JdbcAppender的方法。 - Galya

6

文档建议使用构建器API进行编程配置。使用此API,您的 configureLog4J()方法可能如下所示:

(文档链接)
public static void configureLog4J() {
  ConfigurationBuilder<BuiltConfiguration> builder =
      ConfigurationBuilderFactory.newConfigurationBuilder();

  // configure a console appender
  builder.add(
      builder.newAppender("stdout", "Console")
          .add(
              builder.newLayout(PatternLayout.class.getSimpleName())
                  .addAttribute(
                      "pattern",
                      "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
                  )
          )
  );

  // configure the root logger
  builder.add(
      builder.newRootLogger(Level.INFO)
          .add(builder.newAppenderRef("stdout"))
  );

  // apply the configuration
  Configurator.initialize(builder.build());

}

现在,关键是 - 但据我所知,手册没有清晰地说明 - 在调用LogManager.getLogger()之前,必须首先调用此静态初始化方法。 对于一个最小化的工作示例,您可以使用类似如下的静态初始化块。
private static final Logger log;

static {
  configureLog4J();
  log = LogManager.getLogger(MyAwesomeClass.class);
}

话虽如此,对于任何非微不足道的项目来说,在程序化方式中配置日志记录并不是一个好主意:每当您想临时增加某些日志记录器的日志级别以诊断生产问题时,您都必须重新编译、测试和发布您的代码。因此,我强烈建议不要使用它。


您可以通过系统属性设置日志级别,并在日志配置构建器中加载它们。这样就不需要重新编译代码了。 - izogfif
程序文档看起来很好。在一些新的应用程序中,将XML配置与所有现有Java配置组合可能更加困难。感谢您在这里提供的帮助,您的示例代码立即生效。 https://logging.apache.org/log4j/2.0/manual/customconfig.html#Programmatic_Configuration - Greg
在最后调用 LoggerContext ctx = Configurator.initialize(builder.build()); ctx.updateLoggers(); 不就可以解决问题了吗? - lzdt

3
您可以自定义log4j中的ConfigurationFactory。请参考https://logging.apache.org/log4j/2.x/manual/customconfig.html。它似乎可以满足您的需求。
很抱歉,这种方式是否满足您的需求?我已经测试过了,即使它仍然输出了您上面提到的错误信息,但它运行良好。
    LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();
    Layout<? extends Serializable> layout = PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, config, null,
        null,true, true,null,null);

    Appender appender = FileAppender.createAppender("/tmp/log4jtest.txt", "false", "false", "File", "true",
        "false", "false", "4000", layout, null, "false", null, config);
    appender.start();
    config.addAppender(appender);
    AppenderRef ref = AppenderRef.createAppenderRef("File", null, null);
    AppenderRef[] refs = new AppenderRef[] {ref};
    LoggerConfig loggerConfig = LoggerConfig.createLogger("false", Level.INFO, "org.apache.logging.log4j",
        "true", refs, null, config, null );
    loggerConfig.addAppender(appender, null, null);
    config.addLogger("simpleTestLogger", loggerConfig);
    ctx.updateLoggers();


    Logger l = ctx.getLogger("simpleTestLogger");
    l.info("message of info level shoud be output properly");
    l.error("error message");

我相信您误解了我的问题。我在问如何从零开始创建简单配置,而不依赖于任何配置文件的存在。 - PM 77-1
我需要以一种配置ConsoleAppender的方式,使得在我的主要代码中使用LogManager.getLogger()时不会出现任何问题。 - PM 77-1
第一个转换为LoggerContext似乎是多余的,因为该方法已经返回了一个LoggerContext。但是LoggerContext也没有名为getConfiguration()的方法。 - Hakanai
2
@Trejkaz 在 log4j2 中有两个 LoggerContext 的定义:一个是接口,另一个是类。必须进行类型转换才能进入具有 getConfiguration() 方法的类。 - Mike

2
如果您想用于控制台输出,且正在使用Maven,则有一种非常简单的方法,只需将以下两个依赖项放入您的pom.xml文件中,所有内容都将打印在控制台上。不需要任何其他设置...完全不需要log4j.properties文件。slf4j扩展了log4j,并具有许多丰富的功能。
          <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>
                 <version>1.7.5</version>
                 <scope>compile</scope>
          </dependency>

          <dependency>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-simple</artifactId>
                 <version>1.7.5</version>
          </dependency>

添加其他依赖项,如“slf4j”,会在项目的长期维护中增加额外的工作量。 - Rohit Gaikwad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接