Enforce usage of a specific logging API like Slf4j in an application and avoid usage of unwanted frameworks.

Note
This tutorial is written for version 1.4.0 of jQAssistant.

1. Overview

Java frameworks or libraries very often introduce transient dependencies into a project. This way usually a set of multiple logging APIs becomes available but there should only be one used by the application code. As the provided APIs look very similar it is quite easy for developers to use the wrong framework by accident.

This tutorial demonstrates how popular logging frameworks can be identified and usage of the SLF4j API can be enforced.

Note
This tutorial assumes that jQAssistant has already been integrated into the build process. The example provided under Resources is based on Apache Maven. The shown rules are located in the file jqassistant/logging.adoc.

2. Identify Logging Frameworks

In a first step the logger types of all known and potentially available logging frameworks are identified by the concept logging:Logger:

[[logging:Logger]]
[source,cypher,role=concept]
.Labels the Logger APIs of known logging frameworks with `Logger`.
----
MATCH
  (logger:Type)
WHERE
  logger.fqn in [
    "java.util.logging.Logger",
    "org.apache.commons.logging.Log",
    "org.apache.log4j.Logger",
    "org.apache.logging.log4j.Logger",
    "org.slf4j.Logger"
  ]
SET
  logger:Logger
RETURN
  logger as Logger
----

The types listed in the WHERE clause are the APIs providing methods that are invoked to log messages.

Tip
There’s a logging framework missing in the list? Just add it and let us know!

The second concept logging:Slf4jLogger selects the logging framework that shall be used by the application, in this case it’s the SLF4j API:

[[logging:Slf4jLogger]]
[source,cypher,role=concept,requiresConcepts="logging:Logger"]
.Adds the label `Slf4j` to the type `org.slf4j.Logger`.
----
MATCH
  (slf4jLogger:Logger)
WHERE
  slf4jLogger.fqn = "org.slf4j.Logger"
SET
  slf4jLogger:Slf4j
RETURN
  slf4jLogger
----

The concept is based on logging:Logger and adds another label Slf4j to the type org.slf4j.Logger.

3. Find Invocations Of Unwanted Logging Frameworks

Relying on these concepts the constraint logging:Slf4jLoggerMustBeUsed can be defined:

[[logging:Slf4jLoggerMustBeUsed]]
[source,cypher,role=constraint,requiresConcepts="logging:Logger,logging:Slf4jLogger"]
.Only methods provided by the Slf4j API must be used for logging.
----
MATCH
  (:Type)-[:DECLARES]-(method:Method)-[invokes:INVOKES]->(loggerMethod:Method),
  (logger:Logger)-[:DECLARES]->(loggerMethod)
WHERE NOT
  logger:Slf4j
RETURN
  method as Method, invokes.lineNumber as LineNumber, logger as InvalidLogger
----

The rule returns all methods that invoke a logger method which is not provided by the Slf4j API as violations and reports the affected method, the line number and the API of the used logging framework.

4. Activate The Rules

For execution the constraint should be included within a group. The file jqassistant/logging.adoc therefore defines logging:Default:

[[logging:Default]]
[role=group,includesConstraints="logging:Slf4jLoggerMustBeUsed"]
== Logging

This document describes the rules regarding logging.

The group itself is included in the group default defined in jqassistant/index.adoc:

[[default]]
[role=group,includesGroups="logging:Default"]
== Default Rules

This section describes that default rules that are executed during each build.

- <<logging:Default>>

5. Resources