Log4J 2 Interoperability

Log4J is split into log4j-api and log4j-core jars:

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>${log4j2.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>${log4j2.version}</version>
</dependency>

To configure GeoTools to use Log4J API:

GeoTools.setLoggerFactory("org.geotools.util.logging.Log4J2LoggerFactory");

Reference:

Log4j Guidance

Communication from different Logging frameworks have to Log4J 2 API:

To bridge slf4j to Log4J:

  • Include the following jar:

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j18-impl</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    
  • This routes slf4j api calls to log4j-core.

To bridge java util logging to Log4J:

  • Include the following jar:

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-jul</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    
  • This bridge provides the following mapping:

    • java.util.logging.Filter: yes

    • java.util.logging.Handler: no

    Levels:

    Java Level

    Log4j Level

    OFF

    OFF

    FATAL

    FATAL

    SEVERE

    ERROR

    WARNING

    WARN

    INFO

    INFO

    CONFIG

    CONFIG

    FINE

    DEBUG

    FINER

    TRACE

    FINEST

    FINEST

    ALL

    ALL

  • There are several ways to enable java.util.logging bridge to Log4J:

    • System property:

      -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager
      
    • System property during application init:

      System.setProperty("java.util.logging.manager","org.apache.logging.log4j.jul.LogManager");
      
    • Setup configure application logging.properties with the following:

      handlers = org.apache.logging.log4j.jul.Log4jBridgeHandler
      org.apache.logging.log4j.jul.Log4jBridgeHandler.propagateLevels = true
      
    • Explicitly call Log4jBridgeHandler.install() during application init:

    Log4jBridgeHandler.install();
    
  • To bridge Log4J 1.x to Log4J (replacing the need for Reload4J):

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-1.2-api</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    

    Reference: https://logging.apache.org/log4j/2.x/manual/migration.html

Log4j Integration

The following example is taken from our integration testing, this test only has Log4j 2 API in play so GeoTools.init()` is able to unambiguously determine ``Log4JLoggerFactory can be used.

  1. Setup pom.xml with dependencies on geotools and Log4J:

    <project
      xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.geotools.tutorial</groupId>
      <artifactId>log4j</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
      <name>GeoTools Log4j Integration Example</name>
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.deploy.skip>true</maven.deploy.skip>
        <geotools.version>31-SNAPSHOT</geotools.version>
        <log4j2.version>2.17.2</log4j2.version>
      </properties>
      <dependencies>
        <dependency>
          <groupId>org.geotools</groupId>
          <artifactId>gt-metadata</artifactId>
          <version>${geotools.version}</version>
        </dependency>
        <dependency>
          <groupId>org.geotools.ogc</groupId>
          <artifactId>net.opengis.ows</artifactId>
          <version>${geotools.version}</version>
        </dependency>
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-api</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-core</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
        <!-- java util logging logging to log4j 2 -->
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-jul</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
        <!-- commons logging bridge to log4j 2 -->
        <!--
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-jcl</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
        -->
        <!-- slf4j bridge to log4j 2 -->
        <!--<dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-slf4j-impl</artifactId>
          <version>${log4j2.version}</version>
        </dependency>
        -->
      </dependencies>
      <repositories>
        <repository>
          <id>osgeo</id>
          <name>OSGeo Release Repository</name>
          <url>https://repo.osgeo.org/repository/release/</url>
          <snapshots><enabled>false</enabled></snapshots>
          <releases><enabled>true</enabled></releases>
        </repository>
        <repository>
          <id>osgeo-snapshot</id>
          <name>OSGeo Snapshot Repository</name>
          <url>https://repo.osgeo.org/repository/snapshot/</url>
          <snapshots><enabled>true</enabled></snapshots>
          <releases><enabled>false</enabled></releases>
        </repository>
      </repositories>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
              <source>11</source>
              <target>11</target>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.1.0</version>
          </plugin>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
              <executable>java</executable>
              <arguments>
                <argument>-classpath</argument>
                <!-- uses dependencies and build directory -->
                <classpath/>
                <argument>-Djava.awt.headless=true</argument>
                <argument>org.geotools.tutorial.logging.Log4JIntegration</argument>
              </arguments>
            </configuration>
            <executions>
              <execution>
                <id>jul</id>
                <phase>integration-test</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
                <configuration>
                  <executable>java</executable>
                  <arguments>
                    <argument>-classpath</argument>
                    <!-- uses dependencies and build directory -->
                    <classpath/>
                    <argument>-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager</argument>
                    <argument>-Dlog4j2.configurationFile=log4j2-production.xml</argument>
                    <argument>-Djava.awt.headless=true</argument>
                    <argument>org.geotools.tutorial.logging.Log4JIntegration</argument>
                  </arguments>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </project>
    
  2. Configure log4j wtih log4j2.xml added to src/main/resources:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Concise Syntax -->
    <Configuration status="info" dest="out">
        <CustomLevels>
          <CustomLevel name="CONFIG" intLevel="450" />
          <CustomLevel name="FINEST" intLevel="700" />
        </CustomLevels>>
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%date{HH:mm:ss.SSS} %-6level [%logger{2}] - %msg%n"/>
            </Console>
        </Appenders>
        <Loggers>
            <Logger name="org.geotools.tutorial.logging" level="all" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Logger name="org.geotools" level="debug" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Root level="error">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    

    Of interest above is defining the CONFIG and FINEST custom levels.

  3. During startup logback will search for log4j2.xml on the CLASSPATH.

    To search for a different file on the classpath use -Dlog4j2.configurationFile=log4j2-production.xml.

  4. Application Log4JIntegration.java startup example for src/min/java.

    Example is taking care to call GeoTools.init() prior to logger use:

    /*
     *    GeoTools - The Open Source Java GIS Toolkit
     *    http://geotools.org
     *
     *    (C) 2016, Open Source Geospatial Foundation (OSGeo)
     *
     *    This file is hereby placed into the Public Domain. This means anyone is
     *    free to do whatever they wish with this file. Use it well and enjoy!
     */
    package org.geotools.tutorial.logging;
    
    import java.util.logging.Logger;
    
    import org.geotools.util.factory.GeoTools;
    import org.geotools.util.logging.Log4J2LoggerFactory;
    import org.geotools.util.logging.Logging;
    /** Example illustrating use of Log4J 2 API and startup environment. */
    public class Log4JIntegration {
        static {
            GeoTools.init();
        }
    
        static final Logger LOGGER = Logging.getLogger(Log4JIntegration.class);
    
        public static void main(String args[]) {
            LOGGER.info("Welcome to Log4j Integration Example");
            if (!LOGGER.getClass().getName().equals("org.geotools.util.logging.Log4J2Logger")) {
                LOGGER.severe("Log4J2Logger expected, but was:" + LOGGER.getClass().getName());
            }
    
            // Log4J2 properties
            LOGGER.info("Welcome to Log4j Integration Example");
            checkProperty("log4j2.configurationFile");
            LOGGER.config("Configuration " + Logging.ALL.lookupConfiguration());
    
            LOGGER.finest("Everything is finest...");
            LOGGER.finer("Everything is finer...");
            LOGGER.fine("Everything is fine...");
            LOGGER.config("Everything is configured...");
            LOGGER.log(Logging.OPERATION, "Everything is operating...");
            LOGGER.info("Everything is okay.");
            LOGGER.warning("Everything is alarming!");
            LOGGER.severe("Everything is terrible!");
            LOGGER.log(Logging.FATAL, "Everything has died!");
        }
    
        private static void checkProperty(String property) {
            if (System.getProperties().containsKey(property)) {
                LOGGER.config(property + "=" + System.getProperty(property));
            }
        }
    }
    
  5. An exec:exec target is provided to make this easier to test:

    mvn exec:exec
    

    Is the equivalent of:

    java -Djava.awt.headless=true \\
         org.geotools.tutorial.logging.Log4JIntegration
    
  6. An exec:exec@jul target is provided to try out a more realistic production setting.

    mvn exec:exec@jul
    

    Is the equivalent of:

    java -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \\
         -Dlog4j2.configurationFile=log4j2-production.xml \\
         -Djava.awt.headless=true \\
         org.geotools.tutorial.logging.Log4JIntegration
    

    This makes use of the log4j2-production.xml configuration, and sets up log4j jul bridge.

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Concise Syntax -->
    <Configuration status="info" dest="out">
        <CustomLevels>
          <CustomLevel name="CONFIG" intLevel="450" />
          <CustomLevel name="FINEST" intLevel="700" />
        </CustomLevels>>
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%date{HH:mm:ss.SSS} %-6level [%logger{2}] - %msg%n"/>
            </Console>
        </Appenders>
        <Loggers>
            <Logger name="org.geotools" level="config" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
            <Root level="error">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>
    

    This logging configuration reduces the levels recorded.