0

I am doing the upgrade work from log4j1.2.17 to log4j2.12.0. I need to implement the features what we has done with log4j1.x. However, I'm having some tough problems:

  1. In log4j1.x, after extends class FileAppender, I can use org.apache.log4j.FileAppender#setFile to modify the current output file. How to setFile in log4j2 ?

  2. In log4j1.x, by org.apache.log4j.xml.DOMConfigurator#doConfigure I can do the configuration incrementally. How to do that in log4j2 ? The scenario as follow : Firstly, I built the basic log4j2 configuration (logger apper layout) wit API. Then I want to use the file log4j2-new.xml to update the configuration incrementally based on the previous configuration.

update:

For the question2, the backgroung is as following: I configure the basic logging system with a few XML. However, there is another module called reportService needs to programmatically create some loggers to write report data to files. The basic logging system needs to support hot deployment, which means that the logger needs to be refreshed when the xml changes without removing the loggers create by reportService. I have read the manual of log4j2. And I tried CompositeConfiguration, but It seems the class can't take the config from ((LoggerContext) LogManager.getContext(false)).getConfiguration() as parameter. I think the reason is CompositeConfiguration can't use config that has called initialize(). Here is a example (I use log4j2-base.xml to simulate creating loggers programmatically )

public class LoadXmlConfigurationTest {

    public static final String BASE_CONFIG = "log4j2-base.xml";
    public static final String EXT_CONFIG = "log4j2-ext.xml";

    @Test
    public void loadXmlConfigTest() {
        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, BASE_CONFIG);

        Configuration baseConfig = ((LoggerContext) LogManager.getContext(false)).getConfiguration();

        final Configuration extConfig = loadConfiguration("log4j2-ext.xml");

        List<AbstractConfiguration> configs = new ArrayList<>();

        configs.add((AbstractConfiguration) baseConfig);
        configs.add((AbstractConfiguration) extConfig);

        CompositeConfiguration compositeConfiguration = new CompositeConfiguration(configs);

        ((LoggerContext) LogManager.getContext(false)).reconfigure(compositeConfiguration);
        Logger logger = LogManager.getLogger("EXT");
        Logger rootLogger = LogManager.getRootLogger();
        logger.error("ext logger info");
        rootLogger.error("root logger info");
    }


    private Configuration loadConfiguration(final String resourcePath) {
        try (final InputStream in = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
            return new XmlConfiguration(new LoggerContext("test"), new ConfigurationSource(in));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

output is as follow, the baseConfig doesn't work:

2020-03-01 10:26:41,394 main ERROR No logging configuration
EXT | 2020-03-01 10:26:41,411 ERROR [main] (LoadXmlConfigurationTest.java:50) - ext logger info
10:26:41.413 [main] ERROR  - root logger info

log4j2-base.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
    <Console name="ROOT" target="SYSTEM_OUT">
        <PatternLayout>
            <pattern>ROOT | %d %-5p [%t] %C{2} (%F:%L) - %m%n</pattern>
        </PatternLayout>
    </Console>
</Appenders>


<Loggers>
    <Root level="debug">
        <AppenderRef ref="ROOT" />
    </Root>
</Loggers>
</Configuration>

log4j2-ext.xml

log4j
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="EXT" target="SYSTEM_OUT">
            <PatternLayout>
                <pattern>EXT | %d %-5p [%t] (%F:%L) - %m%n</pattern>
            </PatternLayout>
        </Console>
    </Appenders>


    <Loggers>
        <Logger name="EXT" level="DEBUG" additivity="false">
            <AppenderRef ref="EXT" />
        </Logger>
    </Loggers>
</Configuration>
Ticks
  • 528
  • 1
  • 5
  • 11
  • There is a bunch of documentation about migration: https://logging.apache.org/log4j/2.x/manual/migration.html – Markiian Benovskyi Feb 29 '20 at 19:26
  • This might help: [link](https://stackoverflow.com/questions/15441477/how-to-add-log4j2-appenders-at-runtime-programmatically/33472893#33472893) – vionixt Feb 29 '20 at 19:28

1 Answers1

2

When migrating from Log4j 1 to Log4j 2 too often people just look at the customizations they made for Log4j 1 and try to port them to Log4j 2. This is absolutely the wrong approach.

When migrating from Log4j 1 to Log4j 2 your first step should be to gather what your requirements are for logging. Once you have those you should look at how Log4j 2 could natively implement that. Log4j 2 contains many features that were not available in Log4j 1 and its architecture is very different.

It also would be very difficult to answer your questions above without knowing what you are trying to accomplish. Are you trying to change the file name while the configuration is running? In Log4j 2 the file name is immutable in the File Appender because it can't be changed in a thread-safe manner. You can, however, create a new Configuration with a new FileAppender configured to point to a new file.

What do you mean by "incrementally"? Log4j 2 lets your programmatically create a configuration in a few various ways. Log4j's Programmatic Configuration page documents how to do that. What does it mean you want to use log4j2-new.xml to update the configuration incrementally? Are you wanting to add to the configuration or replace it? Log4j allows composite configurations so you can merge configurations. You would first create your configuration, then the XMLConfiguration, merge them together in a CompositeConfiguration and then pass that to one of the Configurator classes Configurator methods. However, this may make it difficult for you to allow automatic configuration should the configuration file need to be changed.

Frequently people find that they do not need programmatic configuration. Log4j provides Appenders that dynamically create other appenders, a PatternSelector that lets you format records based on attributes in the log event, Lookups that let you dynamically inject values into your configuration at run time, and other features.

So again, before embarking on migrating from Log4j 1 to Log4j 2 first evaluate your requirements and then look at Log4j 2's existing features to determine who to meet them. If you have questions about that feel free to ask here on Stackoverflow or email the Log4j user's mailing list.

rgoers
  • 8,696
  • 1
  • 22
  • 24
  • I've updated the description of question 2, please help me with the CompositeConfiguration. – Ticks Mar 01 '20 at 02:36
  • I must be missing something. In your update you say you need to add Loggers to write report data to files. Loggers don't write anything, Appenders do. Loggers are just a way to generate an event with a way of classifying it (i.e. the logger name). Loggers are created simply by calling LogManager.getLogger(). Your example shows trying to create a CompositeConfiguration manually but you could have done this just by doing -Dlog4j2.configurationFile=log4j2-base.xml,log4j2-ext.xml. – rgoers Mar 01 '20 at 04:06
  • I said add Loggers means "add logger with itsappender and all other components belong to it". I konw use log4j2.configurationFile to config multiple files(as shown in my code System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, BASE_CONFIG);). But I said "I use log4j2-base.xml to simulate creating loggers programmatically )". In fact, the base config is set by api, not a really xml. I want to know how to use CompositeConfiguration to combine the config in the context (already been initialized) with a new xml file. – Ticks Mar 01 '20 at 04:23
  • My core requirement is : The basic logging system needs to support hot deployment, which means that the logger needs to be refreshed when the xml changes without removing the loggers create by reportService – Ticks Mar 01 '20 at 04:30
  • Loggers are never removed. To support hot deployment all you need to do is add monitorInterval="300" to the configuration element to have Log4j check for changes every 300 seconds. When the configuration is changed the Loggers are pointed at the LoggerConfig definitions in the new configuration. Yes, you can create a configuration programmatically and combine it with an XML configuration or another one created programmatically. The real question is, "do you really need to". I haven't heard any requirements around that. – rgoers Mar 01 '20 at 05:52
  • To use CompositeConfiguration all you do is create each configuration individually and then do new CompositeConfiguration(configs) where configs is a List of configurations. Then pass the resulting configuration to Configurator to initialize or reconfigure. CompositeConfiguration uses a MergeStrategy to determine how to merge the configurations. See http://logging.apache.org/log4j/2.x/manual/configuration.html#CompositeConfiguration. – rgoers Mar 01 '20 at 05:57
  • I have tried CompositeConfiguration with two new creating config, and success. But I want to know if 'new CompositeConfiguration(configs)' can take LogManager.getContext(false)).getConfiguration(); as the parameter? Because I tried and failed. – Ticks Mar 02 '20 at 16:58
  • That would create a problem as you are getting the currently running configuration and trying to add it to a new one. What will happen is the Configuration will attempted to be started but since it is already started it will fail. – rgoers Mar 03 '20 at 04:17