logging-capabilities icon indicating copy to clipboard operation
logging-capabilities copied to clipboard

Using enforceLog4J2 within Spring Boot projects

Open tkgregory opened this issue 3 years ago • 1 comments

Many thanks for this plugin! The detection part is working great for me, but I have a query on the configuration part.

Recommended approach

Within a Gradle project I'm apply 2 Spring Boot dependencies:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:2.6.2")
    implementation("org.springframework.boot:spring-boot-starter-log4j2:2.6.2")
}

And I have a simple application class to show me what logging implementation is being used.

package com.tomgregory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Application {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Application.class);
        logger.error("Logging class: {}", logger.getClass());
    }
}

Spring Boot's recommends using a module replacement rule to remove the default logback implementation from spring-boot-starter-web and replace it with log4j2.

    modules {
        module("org.springframework.boot:spring-boot-starter-logging") {
            replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
        }
    }

This works great when I run the application.

$ ./gradlew run -q
10:31:40.536 [main] ERROR com.tomgregory.Application - Logging class: class org.apache.logging.slf4j.Log4jLogger

Doing the same thing with the logging-capabilities plugin.

I remove the module replacement rule, apply the plugin, and call enforceLog4J2() as per the build.gradle.kts below.

plugins {
    application
    id("dev.jacomet.logging-capabilities") version "0.10.0"
}

application {
    mainClass.set("com.tomgregory.Application")
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:2.6.2")
    implementation("org.springframework.boot:spring-boot-starter-log4j2:2.6.2")
}

loggingCapabilities {
    enforceLog4J2()
}

But I get an error at runtime.

$ ./gradlew run

> Task :compileJava FAILED
C:\workspace\exclude-dependency-examples\example3\src\main\java\com\tomgregory\Application.java:3: error: package org.slf4j does not exist
import org.slf4j.Logger;

What I found so far

The error comes because org.slf4j:slf4j-api that contains import org.slf4j.Logger; isn't on the runtime classpath.

Using the module replacement rule, in the dependencies task output we see slf4j-api brought in by log4j-slf4j-impl.

|    |    +--- org.springframework.boot:spring-boot-starter-logging:2.6.2 -> org.springframework.boot:spring-boot-starter-log4j2:2.6.2
|    |    |    +--- org.apache.logging.log4j:log4j-slf4j-impl:2.17.0
|    |    |    |    +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.32

Using the logging-capabilities plugin, we see that log4j-slf4j-impl doesn't bring in any transitive dependencies, as it does above:

\--- org.springframework.boot:spring-boot-starter-log4j2:2.6.2
     +--- org.apache.logging.log4j:log4j-slf4j-impl:2.17.0
     +--- org.apache.logging.log4j:log4j-core:2.17.0 (*)

It could be quite helpful for Spring Boot developers to be able to use this plugin rather than use the module replacement rule.

Is my intended use case supported?

tkgregory avatar Jan 18 '22 10:01 tkgregory

Thank you for your interest in this plugin.

This looks like another symptom of #12. There is a compatibility issue with the Spring Dependency Management plugin. I need to dig into that to figure out what's happening.

ljacomet avatar Apr 22 '23 15:04 ljacomet

Replaced by:

  • gradlex-org/jvm-dependency-conflict-resolution#166

ljacomet avatar Sep 08 '24 13:09 ljacomet