ljacomet / logging-capabilities

Apache License 2.0
40 stars 6 forks source link

= Logging Capabilities Gradle Plugin

:tip-caption: :bulb: :note-caption: :information_source: :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning:

WARNING: This plugin has been merged with the GradleX https://github.com/gradlex-org/jvm-dependency-conflict-resolution[jvm-dependency-conflict-resolution] plugin. Please update to that other plugin and report issues there. I will follow up on the existing issues here and move the relevant ones to the new plugin.

Ever seen this infamous Slf4J warning?

[source]

SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:.../slf4j-log4j12-1.7.29.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:.../logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

Ever wondered how to make sure all your dependencies' logging ends up in your Log4J 2 configuration?

Then this plugin is for you!

It is built on the https://docs.gradle.org/6.0.1/userguide/component_capabilities.html[Gradle capabilities feature], to detect and optionally select a Java logger implementation so that it is enforced at build time!

== Plugin application and compatibility

[source,kotlin]

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

Have a look at https://plugins.gradle.org/plugin/dev.jacomet.logging-capabilities[the documentation on the plugin portal] for alternate or DSL specific syntax.

|=== | Gradle version | Available feature

| Gradle 5.0+ | The detection part is available

| Gradle 6.0+ | The configuration part is available

| Gradle 8.0+ | All features available, requires version 0.11.0 or above of the plugin

|===

== Detection of invalid logging configurations

Upon application, this plugin defines a set of https://docs.gradle.org/6.0.1/userguide/component_metadata_rules.html#basics_of_writing_a_component_metadata_rule[component metadata rules] that add capabilities to relevant logging related dependencies. The added capabilities and impacted modules can be found in the table below:

|=== | Capability | Impacted modules | Comment

| slf4j-impl | org.slf4j:slf4j-simple, org.slf4j:slf4j-log4j12, org.slf4j:slf4j-jcl, org.slf4j:slf4j-jdk14, ch.qos.logback:logback-classic, org.apache.logging.log4j:log4j-slf4j-impl, org.apache.logging.log4j:log4j-slf4j2-impl | Represents an Slf4J binding

| log4j2-impl | org.apache.logging.log4j:log4j-slf4j-impl, org.apache.logging.log4j:log4j-slf4j2-impl, org.apache.logging.log4j:log4j-core | Represents the native Log4J 2 implementation or delegation to Slf4J

| log4j2-vs-slf4j | org.apache.logging.log4j:log4j-slf4j-impl, org.apache.logging.log4j:log4j-to-slf4j | Represents the Slf4J / Log4J 2 relationship: which one delegates to the other

| slf4j-vs-log4j | org.slf4j:log4j-over-slf4j, org.slf4j:slf4j-log4j12 | Represents the Slf4J / Log4J 1.2 relationship: either Slf4J intercepts or binds to Log4J

| slf4j-vs-log4j2-log4j | org.slf4j:log4j-over-slf4j, org.apache.logging.log4j:log4j-1.2-api, log4j:log4j | Represents the available Log4J implementation: native, with Slf4J or with Log4J 2

| slf4j-vs-jul | org.slf4j:jul-to-slf4j, org.slf4j:slf4j-jdk14 | Represents the Slf4J / java.util.logging relationship: either Slf4 intercepts or binds to JUL

| slf4j-vs-log4j2-jul | org.slf4j:jul-to-slf4j, org.apache.logging.log4j:log4j-jul | Represents JUL replacement: either with Slf4J or with Log4J 2

| commons-logging-impl | commons-logging:commons-logging, org.slf4j:jcl-over-slf4j, org.springframework:spring-jcl | Represents Apache Commons Logging implementation: native or Slf4J

| slf4j-vs-jcl | org.slf4j:jcl-over-slf4j, org.slf4j:slf4j-jcl | Represents the Slf4J / Apache Commons Logging relationship: either Slf4J intercepts or binds to commons-logging

| slf4j-vs-log4j2-jcl | org.slf4j:jcl-over-slf4j, org.apache.logging.log4j:log4j-jcl | Represents the Slf4J or Log4J 2 interception of commons-logging

|===

TIP: All capabilities are in the group dev.jacomet.logging

With the set of capabilities defined above, all configuration resolutions in Gradle will fail if conflicting modules are found in the graph.

== Alignment of logging libraries

In addition to the capability setting and conflict detection, the plugin also registers https://docs.gradle.org/6.0.1/userguide/dependency_version_alignment.html#sec:align-versions-virtual[alignment rules] for Slf4J and Log4J 2 modules.

Due to a bug in Gradle versions [5.2, 6.2[, alignment is disabled by default for these versions. Users with Gradle [6.0, 6.2[ can opt back in with loggingCapabilities.enableAlignment(). Note that enabling alignment for these versions may cause some capabilities conflict to remain undetected. See https://github.com/ljacomet/logging-capabilities/issues/4[this issue] for details.

== Expressing preference over a logging solution

The plugin also contributes a project extension that allows to configure which logging solution to use in a declarative fashion. This solution is https://docs.gradle.org/6.0.1/userguide/dependency_capability_conflict.html#sub:selecting-between-candidates[based on APIs] introduced in Gradle 6.0.

The extension is accessed as follows:

[source,kotlin]

// Assuming the plugin has been applied loggingCapabilities { // Configuration goes here }

TIP: The different configuration options documented below do not add dependencies. Make sure to have the expected dependency in your graph, either as a direct or transitive one.

The plugin first provides a number of high-level, one stop solutions, for selecting a logging solution:

|=== | Method | Documentation | Required dependency

| enforceLogback() + enforceLogback(String configurationName) | This will configure all capabilities to resolve in favour of http://logback.qos.ch/[LOGBack] and route all alternative logging solutions through Slf4J. | ch.qos.logback:logback-classic

| enforceLog4J2() + enforceLog4J2(String configurationName) | This will configure all capabilities to resolve in favour of http://logging.apache.org/log4j/2.x/[Log4J 2] and route all alternative logging solutions through Log4J 2. | org.apache.logging.log4j:log4j-slf4j-impl

| enforceSlf4JSimple() + enforceSlf4JSimple(String configurationName) | This will configure all capabilities to resolve in favour of Slf4J simple and route all alternative logging solutions through Slf4J. | org.slf4j:slf4j-simple

|===

TIP: The method without parameter will apply the setup to all dependency configuration, while the other one will limit the setup to the specified dependency configuration.

If you want a finer grained control, the plugin provides lower level entry points for solving the different logging capability conflicts: |=== | Method | Accepted parameter values | Documentation

| selectSlf4JBinding(Object notation) | Value must be an Slf4J binding implementation known by the plugin: org.slf4j:slf4j-simple, org.slf4j:slf4j-log4j12, org.slf4j:slf4j-jcl, org.slf4j:slf4j-jdk14, ch.qos.logback:logback-classic or org.apache.logging.log4j:log4j-slf4j-impl | Configures the provided Slf4J binding for selection, configuring related capabilities if needed

| selectSlf4JBinding(String configurationName, Object notation) | A dependency configuration name, that canBeResolved=true + A notation as above | Configures the provided Slf4J binding for selection, configuring related capabilities if needed, only for the provided dependency configuration

| selectLog4J12Implementation(Object notation) | Value must be a Log4J 1.2 implementation known by the plugin: org.slf4j:log4j-over-slf4j, org.apache.logging.log4j:log4j-1.2-api, log4:log4j or org.slf4j:slf4j-log4j12 | Configures the provided Log4J 1.2 implementation for selection, configuring related capabilities if needed

| selectLog4J12Implementation(String configurationName, Object notation) | A dependency configuration name, that canBeResolved=true + A notation as above | Configures the provided Log4J 1.2 implementation for selection, configuring related capabilities if needed, only for the provided dependency configuration

| selectJulDelegation(Object notation) | Value must be a java.util.logging interceptor or binding known by the plugin: org.slf4j:jul-to-slf4j, org.slf4j:slf4j-jdk14 or org.apache.logging.log4j:log4j-jul | Configures the provided JUL integration of binding for selection, configuring related capabilities if needed

| selectJulDelegation(String configurationName, Object notation) | A dependency configuration name, that canBeResolved=true + A notation as above | Configures the provided JUL integration for selection, configuring related capabilities if needed, only for the provided dependency configuration

| selectJCLImplementation(Object notation) | Value must be a Apache Commons Logging interceptor or binding known by the plugin: org.slf4j:jcl-over-slf4j, commons-logging:commons-logging, org.slf4j:slf4j-jcl or org.apache.logging.log4j:log4j-jcl | Configures the provided commons logging interceptor or binding for selection, configuring related capabilities if needed

| selectJCLImplementation(String configurationName, Object notation) | A dependency configuration name, that canBeResolved=true + A notation as above | Configures the provided commons logging interceptor or binding for selection, configuring related capabilities if needed, only for the provided dependency configuration

| selectSlf4JLog4J2Interaction(Object notation) | Value must be a Log4J 2 module for Slf4J interaction known by the plugin: org.apache.logging.log4j:log4j-to-slf4j or org.apache.logging.log4j:log4j-slf4j-impl | Configures the Log4J 2 / Slf4J integration, configuring related capabilities if needed

| selectSlf4JLog4J2Interaction(Sting configurationName, Object notation) | A dependency configuration name, that canBeResolved=true + A notation as above | Configures the Log4J 2 / Slf4J integration, configuring related capabilities if needed, only for the provided dependency configuration

|===

TIP: Notations above are those accepted by https://docs.gradle.org/6.0.1/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.html#org.gradle.api.artifacts.dsl.DependencyHandler:create(java.lang.Object)[`DependencyHandler.create(notation)] in Gradle that resolves to anExternalDependency. Most often this is agroup:name:version`String.

== Building and reporting issues

You will need a JDK 8+ to build this project.

WARNING: This build is configured to publish build scans always.

Use the GitHub issue tracker for reporting bugs and feature requests.