Jetty 11 changed the logging to SLF4J - how to access it?

Viewed 968

We understand that Jetty 11 has basically changed logging from version 10 (no internal Jetty classes, moreover Jetty 11 is commited to use SLF4 as a base logging).

The problem

We have a rudimentary knowledge of SLF4J (used it before and we've even read the Jetty 11 SLF4J sources ,too), but currently we don't see any way to "teach" Jetty 11 a new logging (aka there are no "setLogging()" methods in the Jetty 11 source code as there were before).

Global (Jetty) parameters, alas, can't be our solution just yet.

The state (aka our requirements)

  • We have already solved the "RequestLog" outputs of Jetty, no problems there, we need the "normal" Jetty-log outputs.
  • We need to control (many) modules/jars etc. via a unified logging.
  • Our logging is simple but requires that no output happens on the console (stdout / stderr etc). In best case the logging gets an instance of an Exception/Runtime, too.
  • Therefore, we need to route the Jetty output from the "Jetty server" through our internal logging. Using SLF4? If there is no other way (and we see no other way up to now), gladly.
  • Switching back to Jetty 10, sadly, is not an option.

Could this be solved in any way we are not aware of (yet)? Any idea would be very appreciated, thank you!

1 Answers

The switch from Jetty logging to Slf4j was actually done in Jetty 10.0.0.

slf4j was designed for unified logging, it can capture into a single logging location implementation all of the logging events generated from libraries that use ...

  • slf4j API
  • java.util.logging API
  • log4j1 API
  • log4j2 API
  • commons-logging API
  • logback API
  • org.apache.juli.logging API

and if you use slf4j version 2.x series, there's even rudimentary support for capturing java.lang.System.Logger API.

With slf4j, you have 2 categories of jar files to think about.

Bridge API JARs

These are slf4j based JARs that merely capture the above logging events and route them to slf4j. You can choose 0..n of these JARs to use.

There's dozens of options here. Here's some common ones

  • jcl-overs-slf4j - captures Jakarta Commons Logging events and sends to slf4j
  • jul-to-slf4j - captures Java Util Logging events and sends them to slf4j
  • log4j-over-slf4j - captures Log4j 1.x events and sends them to slf4j
  • log4j2-overs-slf4j - captures Log4j 2.x events and sends them to slf4j
  • osgi-over-slf4j - captures osgi logging bundle events and sends them to slf4j

See http://www.slf4j.org/legacy.html

Implementation Binding JAR

These are the implementation of slf4j-api, and is the final binding of all logging events, it is the thing that decides what to do with the logging event (eg: write it to disk, ignore it, send it to a logging database, etc)

You have many choices here as well, here's some common jars to pick from (pick only 1!)

  • logback-classic - slf4j to Logback (Eclipse Jetty group's favorite logging implementation)
  • slf4j-jdk14 - slf4j to Java Util Logging
  • slf4j-log4j12 - slf4j to Log4j 1.2.x
  • log4j-slf4j-impl - slf4j to Log4j 2.x (see https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/)
  • slfj-jcl - slf4j to Jakarta Commons Logging
  • jetty-slf4j-impl - Jetty 10+ implementation of the slf4j api

See: http://www.slf4j.org/manual.html#swapping

Since Jetty 10.0.x, the jetty-slf4j-impl exists, which provides an out of the box implementation that simply writes to System.err (aka STDERR) with some decent logging filtering by level in the usual jetty-logging.properties.

See https://search.maven.org/artifact/org.eclipse.jetty/jetty-slf4j-impl

Important advice

  • Don't use multiple binding implementations. Narrow it down to 1 binding implementation and purge all other logging implementation jars.
  • Don't accidentally create a loop with introducing a Bridge API Jar and a Binding Implementation JAR with the same logging technology. (eg: using binding log4j-over-slf4j and slf4j-log4j12 at the same time)
  • There is no "configuration" to wire up these binding or bridge jars, their mere existence in the classloader is enough to make them work. See the slf4j manual on how that works.

We have already solved the "RequestLog" outputs of Jetty, no problems there, we need the "normal" Jetty-log outputs.

Interesting, this is "solved" by actually using slf4j, as that's the only non-deprecated implementation of RequestLog.Writer in Jetty 10 and Jetty 11.

The way this works, is the Slf4jRequestLogWriter will emit events to a single named logger (the name of which you can configure in Slf4jRequestLogWriter.setLoggerName(String)) using the slf4j-api. Then it reaches the logging implementation and is routed wherever that logging configuration decides based on that logger name (file, with rolling, syslog, sent to a different system for aggregation, logstash, etc)

Did you really implement your own RequestLog.Writer instead of just using your preferred logging logging library? (libraries like logback, log4j2, log4j1, and even java.util.logging can easily create separate log files for RequestLog events).

⚠️ Note: do not use logback-access for RequestLog at this time (It does not fully support jakarta.servlets yet, and has many bugs that result in bad request log data. See open PR at https://github.com/qos-ch/logback/pull/532)

Related