Java 9 Platform Logging API and Service

java-9-platform-logging-and-service-feature-image

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

jigsaw-platform-logging



By grokonez | April 25, 2017.

Last updated on September 12, 2018.



Related Posts


Got Something To Say:

Your email address will not be published. Required fields are marked *

*