Skip to main content

Using Loggers

Overview

In this lesson you will learn how to enable logging for your application.

  • Time to Complete: 30 minutes

What You Will Learn

  1. How to create the logging file
  2. The options that can be put into a logging file

Pre-requisites

Before starting this lesson, you should already be familiar with

  1. Creating a launch configuration for the application

Instructions

Logger Manager

Logs in the enactor application are handled by a manager class. These will interpret the log configuration and then initialise a delegate from a logging library to be used at runtime.

  • JavaLoggerManager - This is a simple logger manager that delegates to the java logging API. Several logging functions are not handled by the manager
  • Log4jLoggingManager - This is the default logger manager that delegates to the Log4j library.

Other logging managers exist, such as the LazyLoggerManager and the RestrictedVolumeLoggerManager, but these are focused on specific capabilities, and do not make sense being configured at an application level.

Log Levels

The log level defines what should be logged. This means any at that severity level, as well as anything at a higher severity level. There are several standard log levels available. Starting with the most severe, with equivalent severity levels placed together, these are

  • LOG_ERROR
  • LOG_VITAL_INFORMATION
  • LOG_WARNING
  • LOG_INFORMATION
  • LOG_FINE / LOG_TRACE
  • LOG_FINER / LOG_DEBUG
  • LOG_FINEST

There are also two special severity levels that define the maximum and minimum severity levels.

  • LOG_NONE
  • LOG_ALL

While log levels are, technically, numeric, the logger manager will only convert them to a named level. This allows for better consistency when different logger libraries may have different

The Log Configuration File

The Enactor application expects log files to follow a specific name structure. They will be discovered automatically on the application's classpath. The default logging configuration needs a file named enactor.log.properties.

The first thing that needs to be defined is where the logs will be sent to. These configuration options all use the prefix log4mj, which is intrepreted by every logger manager.

  • log4mj.home=/Path/To/Application/Logs - This defines where the logfile will be placed. During development, it can be convenient to have every log file sent to one place. For production, it is better to keep logfiles with their application
  • log4mj.file.name=filename.log - The name of output file. Each application should use a distinct filename.
  • log4mj.logToScreen=true - This controls whether the logs will also be output to the console.
  • log4mj.loggerManagerClassname=com.enactor.core.logging.Log4jLoggerManager - Define the manager class to use for logging.

With the output for the logs defined, it is necessary to control what will actually be logged. This is done by assigning log levels to the parts of the application you want to receive information about. Logging will be inherited, so care should be taken to avoid creating unnecessarily large log files.

  • .level=LOG_ERROR

To define a log level you must use the above syntax. If there is actually nothing before .level then this entry will apply to every class since it is the root level. If using the root level, then it is best to use a high severity level.

  • com.enactor.level=LOG_ERROR

All Enactor classes will be in a package that starts with com.enactor. It can be useful to control these separately to non-Enactor classes.

  • route.to.my.package.OptionalClassName.level=LOG_WARNING

In general, to control the logging for a class you need to use the fully qualified class name, followed by the .level suffix. Segments of the class name can be omitted, for instance route.to.my.package.level=LOG_DEBUG. For each class, the most specific log entry will be used. So here, the class OptionalClassName would only log warnings, but any of its siblings on the package would also log debug output as well.

Application Log Files

An Enactor deployment can actually consist of multiple applications. Most often, this will be a Tomcat or equivalent server based deployment, but any deployment could have multiple applications embedded into a single runtime execution. Since different applications will have different concerns that need to be tracked, having only a single log file can become unwieldy to manage. So Enactor supports the ability to define a log configuration file for each embedded application.

To define an application specific log configuration, you will need a file whose name uses the following format: enactor-'application-context'.log.properties. For instance, a Tomcat server running the application Web Retail Processing would need a configuration file named enactor-web-retail-processing.log.properties.

The format for the log file remains the same. You just need to ensure that the name of the output is distinct. Note that this will always work, irregardless of the actual number of applications being executed.

Class Loggers

Having a log file defined means little if nothing in the application attempts to write to it. When implementing custom java code you will need to include the following import:

  • import com.enactor.core.utilities.Logger

Then you will need to create an instance of the logger in the context of your class.

...
private static final Logger logger = Logger.getLogger(MyClass.class);
...

Finally, submit a message for the logger to write to the log file.

...
logger.log(Logger.LOG_DEBUG, "Total duration: " + totalDuration + "ms");
...

When creating the message to log, it is good practice to avoid taking too much time to do so. This can mean placing logging inside conditional blocks. For more complex messages, this can also mean following best practices for creating java strings. This means using java.lang.StringBuilder directly instead of relying on string concatenation since the performance of StringBuilder is more consistent. It can also mean avoiding more complex utilities with variable performance. For instance, String.format is primarily meant to be used for message localisation, which is not something a log file necessarily needs.

Task

Update an existing customisation to produce logging.

  1. Go to an action you've previously created and include a logger.
  2. Have the action create multiple log messages, each with a different log level from the following
    1. LOG_ERROR
    2. LOG_WARNING
    3. LOG_DEBUG