Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

System Logger

  • February 18, 2022
  • 2668 Unique Views
  • 3 min read
Table of Contents
System.Logger APICompatible implementationsYour own System.Logger implementationConclusion

December was not a good time for Java developers and even less for Ops. The former had to repackage their apps with a fixed Log4J's version, and the latter had to redeploy them - several times. Yet, every cloud has a silver lining. In my case, I learned about System.Logger.

In short, System.Logger is a façade over your logging engine. Instead of using, say, SFL4J's API and the wanted implementation, you'd use System.Logger instead of SLF4J. It's available since Java 9, and it's a bummer that I learned about it only recently.

System.Logger API

The API is a bit different than other logging APIs: it avoids different logging methods such as debug(), info() in favor of a single log() one where you pass a logging Level parameter.

If you don't provide any corresponding implementation on the classpath, System.Logger defaults to JUL.

public class LoggerExample {

  private static final System.Logger LOGGER = System.getLogger("c.f.b.DefaultLogger"); // 1

  public static void main(String[] args) {
      LOGGER.log(DEBUG, "A debug message");
      LOGGER.log(INFO, "Hello world!");
  }
}
  1. Get the logger

Running the above snippet outputs the following:

Dec 24, 2021 10:38:15 AM c.f.b.DefaultLogger main
INFO: Hello world!

Compatible implementations

Most applications currently use Log4J2 or SLF4J. Both provide a compatible System.Logger implementation.

For Log4J, we need to add two dependencies:

<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>            <!-- 1 -->
        <version>2.17.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>    <!-- 2 -->
        <artifactId>log4j-jpl</artifactId>
        <version>2.17.0</version>
    </dependency>
</dependencies>
  1. Log4J implementation
  2. Bridge from System.Logger to Log4J

The same logging snippet as above now outputs the following:

11:00:07.373 [main] INFO  c.f.b.DefaultLogger - Hello world!

To use SLF4J instead, use the following dependencies:

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>               <!-- 1 -->
        <version>2.0.0-alpha5</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk-platform-logging</artifactId> <!-- 2 -->
        <version>2.0.0-alpha5</version>
    </dependency>
</dependencies>
  1. Basic SLF4J implementation. Any other implementation will do, e.g. Logback
  2. Bridge from System.Logger to Log4J

The snippet outputs:

[main] INFO c.f.b.DefaultLogger - Hello world!

Your own System.Logger implementation

System.Logger relies on Java's ServiceLoader mechanism. Both log4j-jpl and slf4j-jdk-platform-logging contain a META-INF/services/java.lang.System$LoggerFinder file that points to a LoggerFinder implementation.

We can create our own based on System.out for educational purposes.
The first step is to implement the logger itself.

public class ConsoleLogger implements System.Logger {

    private final String name;

    public ConsoleLogger(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isLoggable(Level level) {
        return level.getSeverity() >= Level.INFO.getSeverity();
    }

    @Override
    public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
        if (isLoggable(level)) {
            System.out.println(msg);
            thrown.printStackTrace();
        }
    }

    @Override
    public void log(Level level, ResourceBundle bundle, String format, Object... params) {
        if (isLoggable(level)) {
            System.out.println(MessageFormat.format(format, params));
        }
    }
}

Then, we need to code the System.LoggerFinder:

public class ConsoleLoggerFinder extends System.LoggerFinder {

    private static final Map<String, ConsoleLogger> LOGGERS = new HashMap<>(); // 1

    @Override
    public System.Logger getLogger(String name, Module module) {
        return LOGGERS.computeIfAbsent(name, ConsoleLogger::new);              // 2
    }
}
  1. Keep a map of all existing loggers
  2. Create a logger if it doesn't already exist and store it

Finally, we create a service file:

ch.frankel.blog.ConsoleLoggerFinder

And now, running the same code snippet outputs:

Hello world!

Conclusion

While the API is more limited than other more established logging APIs, System.Logger is a great idea. It offers a façade that's part of the JDK. Thus, it avoids using a third-party façade that needs to wire calls to another unrelated implementation, e.g. SLF4J to Log4J2.

For this reason, I think I'll be trying System.Logger if only to get some hands-on experience.

The complete source code for this post can be found in Maven format there.

To go further:

Originally published at A Java Geek on February 13th, 2022

Backpressure in Reactive Systems

Considering a fast data producer and a slow data consumer, backpressure is the mechanism that “pushes back” on the producer not to be overwhelmed by data.

The Flatten Maven plugin

You don’t need to wait until the release of Maven 5; we can use the Maven Flatter Plugin to between build POM and consumer POM. It’s a freebie; consider using it if you are a library developer.

Treat Security as a Risk

Security is the poster child of a Non-Functional Requirement: most people don’t care until the proverbial matter hits the rotary propeller.

You’re Running Untrusted Code!

I’m afraid the deprecation of the Security Manager just added several lines to that risk, all linked to running untrusted code.

Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

Comments (0)

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

No comments yet. Be the first.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard