How to configure log4j 2.x purely programmatically?
Asked Answered
M

5

33

How do I configure log4j 2.3 with console appender pure programmatically (no configuration files of any format)?

Basically I'm looking for 2.x version of this 1.x code.

In my classes I would then use

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

Without any configuration I'm (as expected) facing

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

While usage of configuration files seems to be properly documented, I couldn't find a good example of a bare-bone code-only case.

The closest I got is this article that still uses a dummy file.

Here's my best (though completely unsuccessful) shot:

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

Have I missed something?

If it's still a RTFM case, please point me into the right direction.

Mensal answered 17/6, 2015 at 3:46 Comment(3)
log4j2 directly, without any slf4j?Irrevocable
@SotiriosDelimanolis - Yes, that was the intent.Mensal
Pure programmatic config was missing from logback as well until recently. See #22335941. . There is a log4j2 link in that SO post that I could never get log4j2 to boot using my init code but the latest logback will allow you to control the init process.Thaumatology
A
27
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");


    }
}

Not sure if this is what you are looking for. This creates a default configuration and adds a console logger. However, LogManager.getLogger() does not work and using the LogManager.getContext().getLogger() does not allow for logger hierarchy. Honestly i don't recommend the programmatic approach, Log4j2 is allergic to it.

America answered 22/6, 2015 at 22:43 Comment(0)
S
14

Here is a complete example for log4j 2.8 programmatically configuration. It has 3 appenders: RollingFile, JDBC and SMTP.

There are 1 class and 2 properties config files, one for register the class as a log4j2 configurationFactory, and the other one for set properties like the log file directory.

Class #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);
                }
            }
        }
    }
}

Config file: src/main/resources/log4j2.component.properties

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

Config file: src/main/resources/log4j2websitester.json

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

In my case, I set all the properties (accessed in MPLoggingConfiguration via System.getProperty) in other classes, for example:

System.setProperty("log4jjdbcjndiName", "weblogic-monitor");

When you changed some properties and want to reconfigure log4j2, you must make this call:

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

Hope this helps

Samovar answered 8/2, 2017 at 16:30 Comment(4)
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.....unable to get configuration properly. please helpAquiline
I edit the response, changing the first config file including the "configurationFile" property and including the content of the new config file.Samovar
this doesn't work anymore with 2.11.x API - you need to use ConfigurationBuilder baeldung.com/log4j2-programmatic-configNodab
It seems the connection source is still dependent on a config file. Can't seem to find a way to completely configure JdbcAppender programmatically.Friendly
G
8

The documentation recommends the builder api for programmatic configuration. Using this api, your configureLog4J() method could look something like this:

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());

}

Now, the trick is - and afaik the manual does not state that clearly enough - that this static initialization method as to be called prior to any calls of LogManager.getLogger().

For a minimal working example, you could use a static initialization block like so

private static final Logger log;

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

That said, configuring logging in a programmatic way is imho not a good idea for any non trivial project: you would have to recompile, test and ship your code each time you want to temporarily increase log levels on certain loggers to diagnose production problems. Therefore I would strongly recommend against using it.

Glue answered 19/9, 2019 at 2:44 Comment(3)
You can set log levels via system properties and load them in log configuration builder. No need to recompile the code this way.Haggadah
Seems like the programmatic documentation is fine. In some newer applications it my be more difficult to combine xml configuration with programmatic where all the existing configuration is in Java. Thank you for the head start here, your sample code worked immediately logging.apache.org/log4j/2.0/manual/…Finstad
Would calling LoggerContext ctx = Configurator.initialize(builder.build()); ctx.updateLoggers(); at the end not do the trick?Cerulean
E
3

You Can customize your own ConfigurationFactory in log4j. Pls refer to https://logging.apache.org/log4j/2.x/manual/customconfig.html. It seems it can meet your need.


Sorry for that. Does this way meet your need? I just test, and it runs well even though it still outputs the error message you mentioned above.

    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");
Eduardo answered 17/6, 2015 at 5:58 Comment(4)
I believe you misunderstood my question. I was asking how to create simple configuration from scratch that does not rely on existence of any configurations files.Mensal
I need ConsoleAppender configured in a way that will cause no problems when I use LogManager.getLogger() in my main code.Mensal
The first cast to LoggerContext seems redundant because that method already returns one. But LoggerContext also doesn't have a method called getConfiguration() either.Swanky
@Trejkaz There are two LoggerContext definitions in log4j2: one is an interface and one is a class. The cast is necessary to get into the class, which does have a getConfiguration() method.Colophon
T
2

if you want it for console appender , there is an very easy way if you are using maven, just put these 2 dependencies in your pom.xml and all things will be printed on ur console . nothing needed ...no log4j.properties file at all. slf4j extends log4j and has so many rich features.

          <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>
Transported answered 24/6, 2015 at 18:7 Comment(1)
Adding other dependencies like "slf4j" causes additional maintenance overhead in a long-run of a project.Jess

© 2022 - 2024 — McMap. All rights reserved.