Logging
On this page
In order to keep the core of the library independent from external dependencies, WLDT introduces a flexible and configurable logging layer. This design allows developers to extend and integrate the logging system according to the specific requirements of their projects,
without being tied to any particular logging framework. The logging API interfaces and methods are inspired by popular libraries such as Log4j
and Logback, making it easier for developers to integrate WLDT logging with their preferred solutions.
Furthermore, the library provides a default implementation that uses classic Java System.out.* output, ensuring basic logging
functionality is available out-of-the-box for immediate usability. This approach enables both quick adoption and
advanced customization as needed.
π Logging in WLDT
The WLDT logging layer provides a flexible and extensible way to handle logging within your digital twin applications.
It’s designed to allow developers to easily switch between different logging implementations without modifying core application code.
The core components of the WLDT logging layer are:
WldtLogger: An interface defining the standard logging methods (e.g.,info,debug,error).WldtLoggerFactory: An interface for creatingWldtLoggerinstances.WldtLoggerProvider: A utility class that provides a static method to getWldtLoggerinstances and allows setting a customWldtLoggerFactory.WldtDefaultLogger: A default implementation ofWldtLoggerthat prints log messages to the console.WldtDefaultLoggerFactory: A default implementation ofWldtLoggerFactorythat createsWldtDefaultLoggerinstances.
WldtLogger Interface
The WldtLogger interface defines a set of methods for logging messages at different levels of severity.
Methods:
getName(): Returns the name of the logger, typically the fully qualified class name.trace(String msg),trace(String format, Object arg),trace(String format, Object arg1, Object arg2),trace(String format, Object... arguments),trace(String msg, Throwable t): Methods for logging trace level messages. These are typically fine-grained informational events that are most useful for debugging an application.isTraceEnabled(): Checks if the trace level is enabled.debug(String msg),debug(String format, Object arg),debug(String format, Object arg1, Object arg2),debug(String format, Object... arguments),debug(String msg, Throwable t): Methods for logging debug level messages. These are typically used for debugging purposes during development.isDebugEnabled(): Checks if the debug level is enabled.info(String msg),info(String format, Object arg),info(String format, Object arg1, Object arg2),info(String format, Object... arguments),info(String msg, Throwable t): Methods for logging info level messages. These provide general information about the application’s progress.isInfoEnabled(): Checks if the info level is enabled.warn(String msg),warn(String format, Object arg),warn(String format, Object arg1, Object arg2),warn(String format, Object... arguments),warn(String msg, Throwable t): Methods for logging warn level messages. These indicate potential issues or unexpected events that do not prevent the application from continuing.isWarnEnabled(): Checks if the warn level is enabled.error(String msg),error(String format, Object arg),error(String format, Object arg1, Object arg2),error(String format, Object... arguments),error(String msg, Throwable t): Methods for logging error level messages. These indicate serious problems that prevent the application from functioning correctly.isErrorEnabled(): Checks if the error level is enabled.
WldtLoggerFactory Interface
The WldtLoggerFactory interface is responsible for creating instances of WldtLogger.
Method:
getLogger(Class<?> clazz): This method takes aClass<?>object as input and returns aWldtLoggerinstance associated with that class. This allows logs to be categorized by their source class.
WldtLoggerProvider Class
The WldtLoggerProvider acts as the entry point for obtaining logger instances.
Key Features:
- Default Factory: By default, it uses
WldtDefaultLoggerFactoryto createWldtDefaultLoggerinstances. - Custom Factory Support: It allows you to set a custom
WldtLoggerFactoryto integrate with different logging frameworks or implement custom logging behavior.
Methods:
public static void setFactory(WldtLoggerFactory customFactory): Use this method to replace the default logger factory with your own custom implementation. This should typically be called once at the application’s startup.public static WldtLogger getLogger(Class<?> clazz): This static method is used to obtain a logger instance for a specific class. It delegates the logger creation to the currently setWldtLoggerFactory.
WldtDefaultLogger Class
The WldtDefaultLogger is a basic, console-based implementation of the WldtLogger interface.
Features:
- Console Output: Logs messages to the standard output (
System.out). - Timestamping: Each log message is prefixed with a timestamp in the format “yyyy-MM-dd HH:mm:ss.SSS”.
- Log Level Indication: Includes the log level (TRACE, DEBUG, INFO, WARN, ERROR) in each message.
- Class Name: Shows the simple name of the class that requested the logger.
WldtDefaultLoggerFactory Class
The WldtDefaultLoggerFactory is the default factory implementation that creates WldtDefaultLogger instances.
Method:
public WldtLogger getLogger(Class<?> clazz): This method simply returns a newWldtDefaultLoggerinstance, initialized with the provided class.
βοΈ How to Create a Custom Logger and Custom Logger Factory
The WLDT logging layer is designed to be extensible, allowing developers to integrate their preferred logging frameworks (e.g., Log4j, SLF4j, Logback) or implement entirely custom logging logic.
1. Create a Custom Logger
To create a custom logger, you need to implement the WldtLogger interface. This allows you to define how log messages are handled (e.g., written to a file, sent to a remote server, or integrated with an existing logging framework).
Here’s an example of a MyCustomLogger that could integrate with a third-party logging framework:
package it.wldt.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyCustomLogger implements WldtLogger {
private final Logger slf4jLogger;
public MyCustomLogger(Class<?> clazz) { this.slf4jLogger = LoggerFactory.getLogger(clazz); }
@Override public String getName() { return slf4jLogger.getName(); }
@Override public void trace(String msg) { slf4jLogger.trace(msg); }
@Override public void trace(String format, Object arg) { slf4jLogger.trace(format, arg); }
@Override public void trace(String format, Object arg1, Object arg2) { slf4jLogger.trace(format, arg1, arg2); }
@Override public void trace(String format, Object... arguments) { slf4jLogger.trace(format, arguments); }
@Override public void trace(String msg, Throwable t) { slf4jLogger.trace(msg, t); }
@Override public boolean isTraceEnabled() { return slf4jLogger.isTraceEnabled(); }
@Override public void debug(String msg) { slf4jLogger.debug(msg); }
@Override public void debug(String format, Object arg) { slf4jLogger.debug(format, arg); }
@Override public void debug(String format, Object arg1, Object arg2) { slf4jLogger.debug(format, arg1, arg2); }
@Override public void debug(String format, Object... arguments) { slf4jLogger.debug(format, arguments); }
@Override public void debug(String msg, Throwable t) { slf4jLogger.debug(msg, t); }
@Override public boolean isDebugEnabled() { return slf4jLogger.isDebugEnabled(); }
@Override public void info(String msg) { slf4jLogger.info(msg); }
@Override public void info(String format, Object arg) { slf4jLogger.info(format, arg); }
@Override public void info(String format, Object arg1, Object arg2) { slf4jLogger.info(format, arg1, arg2); }
@Override public void info(String format, Object... arguments) { slf4jLogger.info(format, arguments); }
@Override public void info(String msg, Throwable t) { slf4jLogger.info(msg, t); }
@Override public boolean isInfoEnabled() { return slf4jLogger.isInfoEnabled(); }
@Override public void warn(String msg) { slf4jLogger.warn(msg); }
@Override public void warn(String format, Object arg) { slf4jLogger.warn(format, arg); }
@Override public void warn(String format, Object arg1, Object arg2) { slf4jLogger.warn(format, arg1, arg2); }
@Override public void warn(String format, Object... arguments) { slf4jLogger.warn(format, arguments); }
@Override public void warn(String msg, Throwable t) { slf4jLogger.warn(msg, t); }
@Override public boolean isWarnEnabled() { return slf4jLogger.isWarnEnabled(); }
@Override public void error(String msg) { slf4jLogger.error(msg); }
@Override public void error(String format, Object arg) { slf4jLogger.error(format, arg); }
@Override public void error(String format, Object arg1, Object arg2) { slf4jLogger.error(format, arg1, arg2); }
@Override public void error(String format, Object... arguments) { slf4jLogger.error(format, arguments); }
@Override public void error(String msg, Throwable t) { slf4jLogger.error(msg, t); }
@Override public boolean isErrorEnabled() { return slf4jLogger.isErrorEnabled(); }}
2. Create a Custom Logger Factory
Once you have your custom logger, you need a custom logger factory to provide instances of it.
This factory will implement the WldtLoggerFactory interface.
Here’s an example of a MyCustomLoggerFactory that produces MyCustomLogger instances:
package it.wldt.log;
public class MyCustomLoggerFactory implements WldtLoggerFactory {
@Override public WldtLogger getLogger(Class<?> clazz) { return new MyCustomLogger(clazz); }}
3. Set the Custom Logger Factory
Finally, to use your custom logger and factory, you need to instruct the WldtLoggerProvider to use your MyCustomLoggerFactory.
This should be done once at the beginning of your application’s lifecycle, before any loggers are requested.
import it.wldt.log.WldtLoggerProvider;
import it.wldt.log.MyCustomLoggerFactory; // Your custom factory
import it.wldt.log.WldtLogger;
public class MyApplication {
public static void main(String[] args) { // Set the custom logger factory WldtLoggerProvider.setFactory(new MyCustomLoggerFactory());
// Now, any logger obtained will be an instance of MyCustomLogger WldtLogger logger = WldtLoggerProvider.getLogger(MyApplication.class); logger.info("This message will be logged using MyCustomLogger!"); logger.error("An error occurred!", new RuntimeException("Something went wrong")); }}
By following these steps, you can seamlessly integrate custom logging solutions into your WLDT digital twin applications,
ensuring that your logging infrastructure meets your specific project requirements.