Search code examples
javalog4jlog4j2

How to create a Java library that packs log4j that all our microservices can then use?


Gradle v7.3.3. (Caveat, I'm no Java programmer, but I understand Gradle.)

Like many companies, we've been hit with the Log4j vulnerabilities. To alleviate this we update all our microservices' build.gradle files to the then latest log4j v2.16.0

dependencies {
    implementation ('org.apache.logging.log4j:log4j-api') {
        version {
            strictly '[2.16.0]'
        }
    }
    implementation ('org.apache.logging.log4j:log4j-core') {
        version {
            strictly '[2.16.0]'
        }
    }
    ...
}

But then they found vulnerabilities in v2.16.0, so we had to go through the same excercise in updating all or our build.gradle files to use log4j v2.17.0. I'm reading that we may have to do this yet again?

How can I create a library or a plugin that depends on the latest log4j version, that our microservices can then just depend on, like this?

dependencies {
    implementation ('com.mycompany.log4j:company-log4j:2.+')
}

So we just then our internal log4j library will have this instead?

dependencies {
    implementation ('org.apache.logging.log4j:log4j-api') {
        version {
            strictly '[2.17.0]'
        }
    }
    implementation ('org.apache.logging.log4j:log4j-core') {
        version {
            strictly '[2.17.0]'
        }
    }
    ...
}

I created a simple Java library with this code

/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package com.company.log4j;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Library {
    private static Logger log = LogManager.getLogger(Library.class.getName());

    public boolean someLibraryMethod() {
        log.info("This is an INFO level log message!");
        log.error("This is an ERROR level log message!");
        return true;
    }
}

and it does build, but when I unpack the JAR file, I don't see the log4j artifacts?


Solution

  • You could create a gradle platform project to bundle common dependencies such as log4j. The project will produce a maven BOM (bill of material) that can be handled like a usual maven artifact and can easily be imported with gradle. Other projects simply depend on the platform project, can keep using log4j and will use newer versions once the platform requires a newer version.

    In your projects the dependencies to log4j are still declared but without version information. The needed version is provided by means of the platform/BOM.

    logging-dependencies/build.gradle:

    plugins {
      id 'java-platform'
    }
    
    dependencies {
      constraints {
        api group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.17.0'
        api group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.0'
      }
    }
    

    project-xyz/build.gradle:

    dependencies {
        implementation platform(group: 'my.company.dependencies', name: 'logging-dependencies', version: '2.+')
    
        implementation group: 'org.apache.logging.log4j', name: 'log4j-api'
        implementation group: 'org.apache.logging.log4j', name: 'log4j-core'
        …
    }