# Joram Logging configuration

Joram logging is based on Java Logging APIs, contained in the package java.util.logging. Basically, Java Logging includes
support for delivering plain text or XML-formatted log records to memory, output streams, consoles, files, and sockets. In
addition, the logging APIs are capable of interacting with logging services that already exist on the host operating system.

This document describes the essential configuration functions when using Joram. For advanced usage you are advised
to refer to the Java documentation.

## Introduction

By default, configuration of Joram logging is done using the "conf/log.properties" properties file. A standard version of
this file is provided with the Joram shipping.

The configuration is described by several properties. The main concepts defined are:
 - Levels: Each message has a severity level. The level gives an indication of the importance and urgency of the message.
 - Loggers: The loggers represent the points of interest of the application, they are organized hierarchically. Each
 application message is sent to a particular logger.
 - Handlers: An handler represents an output, ConsoleHandler or FileHandler for example.
 - Formatters: The formatters are used to format the content of the messages before they are sent to the handlers.

## Configuration

### Levels

Each log message has an associated log Level object. The Level gives a rough guide to the importance and urgency of a log message.

Traditionally Java Logging defines seven standard log levels, ranging from FINEST (the lowest priority, with the lowest value)
to SEVERE (the highest priority, with the highest value). In order of priority: SEVERE, WARNING, INFO, CONFIG, FINE, FINER and
FINEST. Additionally, level OFF Turns off logging, and level ALL enables logging of all messages.

Joram defines and uses 5 different levels:
 - FATAL: It characterizes a very high error message.
 These messages are rare and reflect a serious malfunction of the application, the outcome of which is normally a shutdown.
 - ERROR: It characterizes an error message.
 These messages are rare and indicate an important error leading to the malfunction of one or more components.
 - WARN: It characterizes a warning message.
 These messages indicate an abnormal situation requiring the user's attention even if the integrity of the application is not
 compromised.
 - INFO: It characterizes a information message.
 These messages provide information about the operation of the application. It may be important to filter subjects correctly
 to limit the size of logs.
 - DEBUG: It characterizes a debugging message.
 These messages give detailed information about the operation of the application, they are normally reserved for developers.

### Loggers

Logger objects are organized in a hierarchical namespace and child Logger objects inherits some logging properties from
their parent. The root logger (named "") has no parent. In particular, a logger may inherit:
 - Logging level: If a logger's level is not defined, then the logger will use the level recursively inherited from
 its parent.
 - Handlers: By default, a Logger will log any output messages to its parent's handler, and so on recursively up the tree.

### Handlers

Java provides the following Handler classes:
 - StreamHandler: A simple handler for writing formatted records to an OutputStream.
 - ConsoleHandler: A simple handler for writing formatted records to System.err
 - FileHandler: A handler that writes formatted log records either to a single file, or to a set of rotating log files.
 - SocketHandler: A handler that writes formatted log records to remote TCP ports.
 - MemoryHandler: A handler that buffers log records in memory.

#### ConsoleHandler

This Handler publishes log records to stderr. By default the SimpleFormatter is used to generate brief summaries.

The ConsoleHandler is initialized using the following properties. If properties are not defined (or have invalid
values) then the specified default values are used.
 - java.util.logging.ConsoleHandler.level specifies the default level for the Handler (defaults to Level.INFO).
 - java.util.logging.ConsoleHandler.filter specifies the name of a Filter class to use (defaults to no Filter).
 - java.util.logging.ConsoleHandler.formatter specifies the name of a Formatter class to use (defaults to java.util.logging.SimpleFormatter).
 - java.util.logging.ConsoleHandler.encoding the name of the character set encoding to use (defaults to the default platform encoding).

#### FileHandler

The FileHandler can either write to a specified file, or it can write to a rotating set of files.

For a rotating set of files, as each file reaches a given size limit, it is closed, rotated out, and a new file opened.
Successively older files are named by adding "0", "1", "2", etc. into the base filename. So the most recent file is the
one with index "0". 

The FileHandler is initialized using the following properties. If properties are not defined (or have invalid values) then the
specified default values are used.
 - java.util.logging.FileHandler.level specifies the default level for the Handler (defaults to Level.ALL).
 - java.util.logging.FileHandler.filter specifies the name of a Filter class to use (defaults to no Filter).
 - java.util.logging.FileHandler.formatter specifies the name of a Formatter class to use (defaults to java.util.logging.XMLFormatter)
 - java.util.logging.FileHandler.encoding the name of the character set encoding to use (defaults to the default platform encoding).
 - java.util.logging.FileHandler.limit specifies an approximate maximum amount to write (in bytes) to each file. If this is zero,
 then there is no limit (default).
 - java.util.logging.FileHandler.count specifies how many output files to cycle through (defaults to 1).
 - java.util.logging.FileHandler.pattern specifies a pattern for generating the output file name. See below for details. (Defaults
 to "%h/java%u.log").
 - java.util.logging.FileHandler.append specifies whether the FileHandler should append onto any existing files (defaults to false).

A pattern consists of a string that includes the following special components that will be replaced at runtime:
 - "/" the local pathname separator
 - "%t" the system temporary directory
 - "%h" the value of the "user.home" system property
 - "%g" the generation number to distinguish rotated logs
 - "%u" a unique number to resolve conflicts
 - "%%" translates to a single percent sign "%"

If no "%g" field has been specified and the file count is greater than one, then the generation number will be added to the end of
the generated filename, after a dot.

Normally the "%u" unique field is set to 0. However, if the FileHandler tries to open the filename and finds the file is currently
in use by another process it will increment the unique number field and try again. This will be repeated until FileHandler finds a
file name that is not currently in use. If there is a conflict and no "%u" field has been specified, it will be added at the end of
the filename after a dot. (This will be after any automatically added generation number.)

### Formatters

Java also includes two standard Formatter classes:
 - SimpleFormatter: Writes brief "human-readable" summaries of log records.
 - XMLFormatter: Writes detailed XML-structured information.

Joram offers an extended formatter with the class "org.objectweb.util.monolog.jul.LogFormatter".

#### SimpleFormatter

Print a configurable summary of the LogRecord in a human readable format. The summary will typically be 1 or 2 lines.

The log message can be customized by a format string specified in the "java.util.logging.SimpleFormatter.format" property.
This format is defined by the java.util.Formatter class in a C language printf-style. The parameters are:
 - 1 - a Date object representing event time of the log record.
 - 2 - a string representing the caller, if available; otherwise, the logger's name.
 - 3 - the logger's name.
 - 4 - the log level.
 - 5 - the log message.
 - 6 - a string representing the throwable associated with the log record and its stacktrace beginning with a newline character, if any; otherwise, an empty string.

For example, the format string "%1$tc %2$s %4$s: %5$s%6$s%n" prints a message including the timestamp (1$), the source (2$), the log level (4$) and the log message (5$) followed with the throwable and its backtrace (6$), if any:

~~~~
Tue Mar 22 13:11:31 PDT 2011 MyClass SEVERE: message without an exception
~~~~
     
#### XMLFormatter

Format a LogRecord into a standard XML format.

#### LogFormatter

As the SimpleFormatter, it prints a configurable summary of the LogRecord.
It offers an extended format which makes it possible to obtain more complete and precise messages.

The log message can be customized by a format string specified in the "java.util.logging.SimpleFormatter.format" property,
or by the format parameter of the handler. This format is defined by the java.util.Formatter class in a C language printf-style.
The parameters are:
   - 1 - Date/Time in "dd/MM/yyyy hh:mm:ss.SSS" format representing event time of the log record.
   - 2 -  a Date object representing event time of the log record.
   - 3 - Full class name of the caller, if available; otherwise, an empty string.
   - 4 - Class name of the caller, if available; otherwise, an empty string.
   - 5 - Method name of the caller, if available; otherwise, an empty string.
   - 6 - Line number of the caller, if available; otherwise, an empty string.
   - 7 - the logger's name.
   - 8 - the name of Level.
   - 9 - the localized name of Level.
   - 10 - the log message.
   - 11 - a unique Thread identifier.
   - 12 - the thread name.
   - 13 - a string representing the throwable associated with the log record and its stacktrace beginning
   with a newline character, if any; otherwise, an empty string.

For example, we detail below the 'short' and 'long' formats used in the default Joram configuration.

The short format (`%1$s %7$s %8$s: %10$s%n`) writes the time of event, the source logger, the level
and the message, for example:

~~~~
10/02/2022 07:30:54.460 com.scalagent.jorammq.mqtt.adapter.KeyStoreStatus WARN: Checking MQTT adapter key store .\conf\keystore: found no valid X509 trusted certificate
~~~~

The long format (`%1$s %7$s %8$s [%12$s %4$s.%5$s(%6$s)]: %10$s%13$s%n`) adds information about
the event source (the thread name, the class, method and line number of the caller) and prints an eventual
throwable associated with the event. For example:

~~~~
10/02/2022 07:30:54.460 com.scalagent.jorammq.mqtt.adapter.KeyStoreStatus WARN [FelixStartLevel KeyStoreStatus.doUpdate(444)]: Checking MQTT adapter key store .\conf\keystore: found no valid X509 trusted certificate
~~~~

### Advanced topics

The logging configuration file path can be customized using Java properties:
  - "fr.dyade.aaa.DEBUG_DIR" is used to determine the directory containing the configuration file (by default
  the working directory).
   - "fr.dyade.aaa.DEBUG_FILE" is used to define the name of the configuration file (by default "a3debug.cfg).

The logging configuration can be debugged by setting the "org.objectweb.util.monolog.debug" Java property to true.

## Default configuration

In this section we explained the content of the Joram default configuration.
There are 2 parts in this configuration, the first is dedicated to handlers, the second to loggers.

### Handlers configuration

In the first part below, we declare two handlers, a ConsoleHandler and a FileHandler,and we configure them
 - ConsoleHandler:
   - It uses the specific Joram formatter classes with a short message format.
   - Only messages with a priority greater than or equal to WARN are printed.
 - FileHandler:
   - It uses the specific Joram formatter classes with a long message format.
   - All messages are printed.
   - Messages are written to a rotating set of 10 files of 10Mb.

### Loggers configuration

This part is divided in 6 sections:
 - first the definition of the level of the root logger (WARN). This level will be inherited by all its descendants if
 they do not define a specific level. If there is no other logger definition, only messages with a priority higher to 
 WARN would be written.
 - The four next sections define loggers of different components:
   - 'fr.dyade.aaa' subtree concerns the base runtime of Joram.
   - 'fr.dyade.aaa.jndi2' subtree concerns the JNDI implementation.
   - 'org.objectweb.joram' subtree concerns the JMS implementation.
 - The last section defines loggers of third party software embedded with Joram.

By default, all loggers are configured with the WARN level. Many other loggers are commented out to allow detailed logging
of some Joram components.

### Default configuration file

~~~~
# (C) 2021 - 2022 ScalAgent Distributed Technologies
# All rights reserved

handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# -----------------------------------------------------------------------
# Console logging
# -----------------------------------------------------------------------
java.util.logging.ConsoleHandler.formatter = org.objectweb.util.monolog.jul.LogFormatter
java.util.logging.ConsoleHandler.format = %1$s %7$s %8$s: %10$s%n
java.util.logging.ConsoleHandler.level = WARN

# -----------------------------------------------------------------------
# File logging
# -----------------------------------------------------------------------
java.util.logging.FileHandler.pattern = ./log/server-%u.%g.log
java.util.logging.FileHandler.limit = 10000000
java.util.logging.FileHandler.count = 10
java.util.logging.FileHandler.formatter = org.objectweb.util.monolog.jul.LogFormatter
java.util.logging.FileHandler.format = %1$s %7$s %8$s [%12$s %4$s.%5$s(%6$s)]: %10$s%13$s%n
java.util.logging.FileHandler.level = ALL
java.util.logging.FileHandler.append = true

java.util.logging.SimpleFormatter.format = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n
org.objectweb.util.monolog.jul.LogFormatter.format = %1$s %7$s %8$s [%12$s %4$s.%5$s(%6$s)]: %10$s%13$s%n

.level = WARN

#==================================================
# A3 Runtime
fr.dyade.aaa.level = WARN

#fr.dyade.aaa.agent.Agent.level = DEBUG
#fr.dyade.aaa.agent.Engine.level = DEBUG
#fr.dyade.aaa.util.Transaction.level = DEBUG
#fr.dyade.aaa.agent.Network.level = DEBUG
#fr.dyade.aaa.agent.Service.level = DEBUG

#==================================================
# Joram/JNDI component
#fr.dyade.aaa.jndi2.client.level = DEBUG
#fr.dyade.aaa.jndi2.server.level = DEBUG

#==================================================
# Joram/JMS component
org.objectweb.joram.level = WARN

#org.objectweb.joram.mom.level = DEBUG
#org.objectweb.joram.client.jms.level = DEBUG
#org.objectweb.joram.client.connector.level = DEBUG
#org.objectweb.joram.shared.level = DEBUG

#org.objectweb.joram.client.jms.Session.Message.level = INFO
#org.objectweb.joram.client.jms.Connection.tracker.level = DEBUG
#org.objectweb.joram.client.jms.Session.tracker.level = DEBUG

org.ow2.joram.level = WARN

#org.ow2.joram.jmxconnector.level = DEBUG

#org.objectweb.joram.tools.rest.level = DEBUG
#com.scalagent.joram.mom.dest.rest.level = DEBUG
#org.objectweb.joram.tools.rest.jms.level = DEBUG

# Needed to avoid useless warning message about clock synchronization
# 'TcpConnectionListener.acceptConnection :  -> bad clock synchronization between client and server'
org.objectweb.joram.mom.proxies.tcp.TcpConnectionListener.level = ERROR

#==================================================
# Third-party software
org.apache.sshd.level = WARN
org.eclipse.jetty.level = WARN
org.glassfish.jersey.level = WARN

~~~~

# Joram Logging migration

Until version 5.18 Joram's logging was based on the OW2/Monolog wrapper. This wrapper offered a common interface
to the different logging APIs: Log4J, JUL (Java Util Logging), etc. It also allowed a universal configuration
regardless of the layout used.

The OW2/Monolog project being now poorly supported, it was decided to replace it in version 5.19 of Joram. The
choice fell on the logging component integrated into Java: JUL (Java Util Logging).

The choice of logging component normally has little impact on the Joram user, the main concern is its configuration.
In this document we will detail these changes. A logging configuration file can be divided in 2 parts:
  - The first part dedicated to handlers is strongly impacted by the implementation change. We recommend that you
  rebuild it following the instructions above (or go back to the default configuration).
  - The second part dedicated to loggers has very few changes, there are essentially two:
    - The "logger" prefix must be removed from logger names.
    - You must add the symbol '=' between the name of the logger and the desired logging level.
    - for example, "logger.fr.dyade.aaa.agent.Agent.level DEBUG" becomes "fr.dyade.aaa.agent.Agent.level = DEBUG"

# Use of other logging backend

As mentioned above, Joram's logging now relies on JUL. However, Joram exposes an API (package 
org.objectweb.util.monolog.api) and allows implementations based on other logging backend. The choice of the
implementation used is made through the environment variable "fr.dyade.aaa.DEBUG_LOGGING_FACTORY", the default
value is "org.objectweb.util.monolog.jul.SimpleLoggingFactory".

For example, a contribution allows to use slf4j with Joram. In this case the configuration must be specific and
must be carried out directly on the backend used (slf4j is itself a wrapper). To use this implementation, you must
set the configuration variable of the factory ("fr.dyade.aaa.DEBUG_LOGGING_FACTORY") to the value 
"org.objectweb.util.monolog.slf4j.Slf4jMonologFactory".

Other implementations could be provided.


