Reload4J Interoperability¶
Apache has announced Log4J 1 has reached end-of-life, the Reload4J project has taken over maintaining the API:
<reload4j.version>1.2.19</reload4j.version>
<dependency>
<groupId>ch.qos.reload4j</groupId>
<artifactId>reload4j</artifactId>
<version>${reload4j.version}</version>
</dependency>
If your application uses Log4J directly during compile time, you can safely set up with:
GeoTools.setLoggerFactory(Log4JLoggerFactory.getInstance());
If your application only depends reload4j being provided at runtime:
Logging.ALL.setLoggerFactory("org.geotools.util.logging.Log4JLoggerFactory");
Note
If reload4j is not found on the CLASSPATH Log4JLoggerFactory results in unpredictable behavior!
It will typically throws a NoClassDefFoundError
(the unchecked error, not the checked exception) at some future point. The error may not be thrown at the moment setLoggerFactory
is invoked, but rather be delayed until a message is first logged, which may surprise the user.
Reference:
Reload4J Guidance¶
In a more complicated setup using multiple libraries you may also end up including:
slf4j-reload4j: used to bridge any components using slf4j api
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-reload4j</artifactId> <version>${slf4j.version}</version> </dependency>
jcl-over-sl4j: used to bridge any components using commons-logging to sl4j (which can be bridged to reload4j above).
<dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j.version}</version> </dependency>
commons-logging: Assume Log4J 1 API
Use
commons-logging.properties
:org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
Use of multiple logging frameworks prevents
GeoTools.init()
ability to determine which API to use requiring the use of:Logging.ALL.setLoggerFactory("org.geotools.util.logging.Log4JLoggerFactory");
Reload4J Integration¶
The following example is taken from our integration testing, this test only has the reload4j
in play so GeoTools.init()` is able to unambiguously determine ``Log4JLoggerFactory
can be used.
Setup
pom.xml
with dependencies on geotools and reload4j:<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>reload4j</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>GeoTools Reload4J 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> <reload4j.version>1.2.19</reload4j.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>ch.qos.reload4j</groupId> <artifactId>reload4j</artifactId> <version>${reload4j.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> <classpath/> <argument>-Djava.awt.headless=true</argument> <argument>org.geotools.tutorial.reload.Reload4JIntegration</argument> </arguments> </configuration> </plugin> </plugins> </build> </project>
Configure reload4j wtih
log4j.properties
added tosrc/main/resources
:log4j.rootLogger=ERROR, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM HH:mm:ss} %p [%c{2}] - %m%n log4j.category.org.geotools=ALL
During startup log4j will search for
log4j.properties
on the CLASSPATH, or to search for a different file use the system property:-Dlog4j.configuration=log4-debug.properties
Application
Reload4Integration.java
startup example forsrc/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) 2022, 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.reload; import java.util.logging.Logger; import org.geotools.util.factory.GeoTools; import org.geotools.util.logging.Logging; import org.geotools.util.logging.Log4JLoggerFactory; /** Example illustrating use of Reload4J (providing Log4J 1 API) and startup environment. */ public class Reload4JIntegration { public static final Logger LOGGER = initLogger(); public static void main(String args[]) { LOGGER.info("Welcome to Reload4J Integration Example"); if (!LOGGER.getClass().getName().equals("org.geotools.util.logging.Log4JLogger")) { LOGGER.severe("Log4JLogger expected, but was:" + LOGGER.getClass().getName()); } // Log4J Properties checkProperty("log4j.defaultInitOverride"); checkProperty("log4j.configuratorClass"); checkProperty("log4j.configuratorClass"); if (System.getProperties().containsKey("log4j.configuration")) { LOGGER.config("log4j.configurationFile=" + System.getProperty("log4j.configuration")); } LOGGER.config("Configuration " + Log4JLoggerFactory.getInstance().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 Logger initLogger() { GeoTools.init(); return Logging.getLogger(Reload4JIntegration.class); } private static void checkProperty(String property) { if (System.getProperties().containsKey(property)) { LOGGER.config(property + "=" + System.getProperty(property)); } } }
An
exec:exec
target is provided to make this easier to test:mvn exec:exec
Note
Avoid testing with
exec:java
which uses maven java runtime environment (already pre-configured for logging).