ljacomet / logging-capabilities

Apache License 2.0
40 stars 6 forks source link

Using enforceLog4J2 within Spring Boot projects #17

Closed tkgregory closed 1 week ago

tkgregory commented 2 years ago

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?

ljacomet commented 1 year ago

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 commented 1 week ago

Replaced by: