Java 9 defines a minimal logging API which platform classes can use to log messages, together with a service interface for consumers of those messages. In this tutorial, we’re gonna take a look at new Java 9 Platform Logging API and an example that implements LoggerFinder service which can route platform log messages to the logging framework for application logger instead of system logger.
I. Platform Logging API and Service
An implementation of LoggerFinder
is loaded with help of java.util.ServiceLoader API using system class loader. Basing on this implementation, an application/framework can plug in its own external logging backend, without configuring java.util.logging or that backend.
We can pass the class name or module (related to specific Logger) to the LoggerFinder
so that the LoggerFinder
can know which logger to return.
public class MyLoggerFinder extends LoggerFinder { @Override public Logger getLogger(String name, Module module) { // return Logger depending on name/module... } }
If no concrete implementation is found, JDK default LoggerFinder
implementation will be used. java.util.logging (in java.logging module) now becomes backend. So log messages will be routed to java.util.logging.Logger.
We obtain loggers that are created from the LoggerFinder
using factory methods of the System class:
package java.lang; ... public class System { System.Logger getLogger(String name) { ... } System.Logger getLogger(String name, ResourceBundle bundle) { ... } }
II. Example
1. Overview
In logger package:
– We will create 2 concrete Loggers that implement Logger
interface: MyLogger and MyNewLogger.
– MyLoggerFinder is an implementation of LoggerFinder that can return desired Logger by name.
We build logger package into demo.logging module.
This module provides an implementation of LoggerFinder service.
In app packagee, we create a Test Class that uses System factory method getLogger(name)
to get Loggers and use them.
We build app package into demo.app module.
Folder tree should be:
src/ ----com.javasampleapproach.demo.app/ module-info.java com/ javasampleapproach/ platformlogging/ app/ MainApp.java ----com.javasampleapproach.demo.logging/ module-info.java com/ javasampleapproach/ platformlogging/ logger/ MyLoggerFinder.java MyLogger.java MyNewLogger.java mods/ ----com.javasampleapproach.demo.app/ ----com.javasampleapproach.demo.logging/
2. Practice
2.1 Create implementation of LoggerFinder
package com.javasampleapproach.platformlogging.logger; import java.lang.System.Logger; import java.lang.System.LoggerFinder; public class MyLoggerFinder extends LoggerFinder { @Override public Logger getLogger(String name, Module module) { if (name.equals("MyNewLogger")) return new MyNewLogger(); else return new MyLogger(); } }
2.2 Create implementations of Logger
package com.javasampleapproach.platformlogging.logger; import java.lang.System.Logger; import java.util.ResourceBundle; import static java.text.MessageFormat.format; public class MyLogger implements Logger { @Override public String getName() { return "MyLogger"; } @Override public boolean isLoggable(Level level) { switch (level) { case OFF: case TRACE: case DEBUG: case INFO: case WARNING: case ERROR: case ALL: default: return true; } } @Override public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) { System.out.printf("Log from MyLogger [%s]: %s - %s%n", level, msg, thrown); } @Override public void log(Level level, ResourceBundle bundle, String format, Object... params) { System.out.printf("Log from MyLogger [%s]: %s%n", level, format(format, params)); } }
package com.javasampleapproach.platformlogging.logger; import java.lang.System.Logger; import java.util.ResourceBundle; import static java.text.MessageFormat.format; public class MyNewLogger implements Logger { @Override public String getName() { return "MyNewLogger"; } @Override public boolean isLoggable(Level level) { switch (level) { case OFF: case TRACE: case DEBUG: case INFO: case WARNING: case ERROR: case ALL: default: return true; } } @Override public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) { System.out.printf("Log from MyNewLogger [%s]: %s - %s%n", level, msg, thrown); } @Override public void log(Level level, ResourceBundle bundle, String format, Object... params) { System.out.printf("Log from MyNewLogger [%s]: %s%n", level, format(format, params)); } }
2.3 Create Module Info file for demo.logging module
module com.javasampleapproach.demo.logging { provides java.lang.System.LoggerFinder with com.javasampleapproach.platformlogging.logger.MyLoggerFinder; }
2.4 Create a Test Class
package com.javasampleapproach.platformlogging.app; import java.lang.System.Logger; import java.lang.System.Logger.Level; public class MainApp { private static Logger LOGGER1 = System.getLogger("MyLogger"); private static Logger LOGGER2 = System.getLogger("MyNewLogger"); public static void main(String[] args) { LOGGER1.log(Level.ERROR, "This is just an Error Log test."); LOGGER1.log(Level.INFO, "Hello World!"); LOGGER2.log(Level.ERROR, "This is just an Error Log test."); LOGGER2.log(Level.INFO, "Hello World!"); } }
2.5 Create Module Info file for demo.app module
module com.javasampleapproach.demo.app { requires java.logging; exports com.javasampleapproach.platformlogging.app; }
2.6 Run and check result
javac -d mods/com.javasampleapproach.demo.logging src/com.javasampleapproach.demo.logging/module-info.java src/com.javasampleapproach.demo.logging/com/javasampleapproach/platformlogging/logger/*.java javac -d mods/com.javasampleapproach.demo.app src/com.javasampleapproach.demo.app/module-info.java src/com.javasampleapproach.demo.app/com/javasampleapproach/platformlogging/app/MainApp.java java --module-path mods -m com.javasampleapproach.demo.app/com.javasampleapproach.platformlogging.app.MainApp
The result is:
Log from MyLogger [ERROR]: This is just an Error Log test. Log from MyLogger [INFO]: Hello World! Log from MyNewLogger [ERROR]: This is just an Error Log test. Log from MyNewLogger [INFO]: Hello World!
III. Source Code
Last updated on September 12, 2018.