Log4jl is a comprehensive and flexible logging framework for Julia programs.
To create logger call @Log4jl.logger
macro after importing Log4jl
module.
This macro call initializes and configures the logging framework. Also it creates logger object which cab be used by any of logging functions or macros to perform logging operations.
using Log4jl
const logger = @Log4jl.logger
error(logger, "Error in my code")
# or
@Log4jl logger = @Log4jl.logger
@error "Error in my code"
See usage in example/simple.jl.
In order to create logger instance, call macro @Log4jl.logger [<name>] [MSG=<message_type>] [URI=<config_location>] [begin <config_code_block> end]
. It accepts following parameters:
name
: a string which specifies a logger name from a configurationMSG=<message_type>
: a message type used for configuring a loggerURI=<config_location>
: a configuration locationbegin <configuration> end
: a configuration program block (must returnConfiguration
object)
If the root logger is required use macro Log4jl.rootlogger
with the same parameters as for Log4jl.logger
with one exception: root logger does not have a name.
# get the root logger
const logger = @Log4jl.rootlogger
# get the configured logger by name (uses FQMN by default)
const logger = @Log4jl.logger
# get the configured logger by name explicitly
const logger = @Log4jl.logger "TestLogger"
# get the configured logger by name that will use parameterized messages
const logger = @Log4jl.logger "TestLogger" MSG=ParameterizedMessage
# get the configured logger by from file specified in the parameter
const logger = @Log4jl.logger URI="myconfig.xml"
# get the configured logger from a programmatic configuration
const logger = @Log4jl.logger begin
Configuration("Custom",
PROPERTIES(),
APPENDERS(),
LOGCONFIGS()
)
end
The default configuration file is log4jl.*
. An extension of the configuration file determines format in which configuration is described.
Configuration file should be located in:
- For stand-alone module: a directory where a source code file of the module is located.
- For package: a package root directory.
Log4jl can be configured with properties which can be set through environmental variables.
Property | Description | Default Value |
---|---|---|
LOG4JL_LINE_SEPARATOR | Default new line separator sequence | [(0x0d - win) 0x0a] |
LOG4JL_DEFAULT_STATUS_LEVEL | Default logger status level to use if not specified in configuration. | ERROR |
LOG4JL_INTERNAL_STATUS_LEVEL | Default status level of internal Log4jl logging to use if not specified in configuration. |
WARN |
LOG4JL_LOG_EVENT | Type of the default logger event generator which converts messages into logging events. | Log4jlEvent |
LOG4JL_CONTEXT_SELECTOR | Type of the default logger context selector. | ModuleContextSelector |
There are many well known use cases where applications may share an environment with other applications and each has a need to have its own, separate logging environment.
There are a few ways to archive logging separation using different ContextSelector
implementations:
-
SingleContextSelector
: This selector creates a singleLoggerContext
using a single configuration that will be shared across all applications. -
ModuleContextSelector
: This selector creates a oneLoggerContext
per module. This allows each module be configured to share the same configuration or can be individually configured. However, if configuration is not provided or has error then the parent module context is used for the current module. If there exist no parent module then new context is created with default configuration.
Context selection can be done by setting environment variable LOG4JL_CONTEXT_SELECTOR
with a name of context selector type.
Log4jl
supports custom log levels. Custom log levels can be defined in code or in configuration. To define a custom log level in code, use the Level.add
function. This function creates a new level for the specified name and generates appropriate convenience functions. After a log level is defined you can log messages at this level by calling corresponding log function:
# This creates the "VERBOSE" level if it does not exist yet.
Log4jl.Level.add(:VERBOSE, 550)
# Create a logger
const logger = @logger
# Use the custom VERBOSE level
Log4jl.verbose(logger, "a verbose message")
When defining a custom log level, the intLevel parameter (550 in the example above) determines where the custom level exists in relation to the standard levels built-in to Log4jl. For reference, the table below shows the intLevel of the built-in log levels.
Standard log levels built-in to Log4jl
Standard Level | intLevel |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | typemax(Int16) |
Custom log levels can also be defined in configuration. This is convenient for using a custom level in a logger filter or an appender filter. Similar to defining log levels in code, a custom level must be defined first, before it can be used. If a logger or appender is configured with an undefined level, that logger or appender will be invalid and will not process any log events.
The customlevels
section of configuration element defines a custom levels. Internally it calls the same Level.add
function discussed above.
Parameter Name | Type | Description |
---|---|---|
name | string | The name of the custom level. The convention is to use all upper-case names. |
intLevel | integer | Determines where the custom level exists in relation to the standard levels built-in to Log4jl (see the table above). |
The following example shows a configuration that defines some custom log levels and uses a custom log level to filter log events sent to the console.
configuration:
status: trace
name: YAMLTest
customlevels:
diag: 350
verbose: 150
appenders:
ColorConsole:
name: STDOUT
loggers:
logger:
-
name: X.Y
level: diag
appenderref:
-
ref: STDOUT
Filters allow logged Event
s to be evaluated to determine if or how they should be published. A Filter
will be called on one of its filter
methods and will return a FilterResult
, which is an Enum
that has one of 3 values:
ACCEPT
: no filters called, accept eventDENY
: ignore event, return to callerNEUTRAL
: pass event to other filters
Filters may be configured in one of four locations:
- Context-wide Filters are configured directly in the configuration. Events that are rejected by these filters will not be passed to loggers for further processing. Once an event has been accepted by a context-wide filter it will not be evaluated by any other context-wide filters nor will the Logger's Level be used to filter the event. The event will be evaluated by logger and appender filters however.
- Logger Filters are configured on a specified
Logger
. These are evaluated after the context-wide filters and the logLevel
for theLogger
. Events that are rejected by these filters will be discarded and the event will not be passed to a parentLogger
regardless of the additivity setting. - Appender Reference Filters are used to determine if a
Logger
should route the event to an appender. - Appender Filters are used to determine if a specific
Appender
should handle the formatting and publication of the event.
Log4jl has similar architecture as Apache Log4j 2 framework.
- Loggers are wrappers around configuration
- Loggers would change behavior if configuration is changed
- Logger hierarchy based on hierarchy of configurations
- Global logger context keeps track of all loggers
- Root logger has no name and additivity, its default level is ERROR
- Logging functions support:
- plaint text
- markers
- objects
LOG4JL_CONFIG_DEFAULT_PREFIX
: prefix of the configuration file. Default value is 'log4jl'.LOG4JL_CONFIG_EXTS
: Map of configuration file extensions.LOG4JL_CONFIG_TYPES
: Map of configuration types.
- 'isenabled' checks if logger allowed to process event at specified level
- On-fly reconfiguration
- Multi-threading/processing support
- Lookups
- Configuration formats: JSON, XML, TOML
- Handle configuration recursion
- For custom formated messages, create two functions with the same name and following signatures:
- <message_type_function>(msg::AbstractString, params...) => Message
- <message_type_function>(msg::Any) => Message
- Module
Log4jl
is referenced - Function
Log4jl.__init__
is called- A logger context selector is initialized as object and assigned to global constant
LOG4JL_CONTEXT_SELECTOR
from an environment variable with the same name. Default context selector type isLog4jl.ModuleContextSelector
. - Default status level is initialized as
LOG4JL_DEFAULT_STATUS_LEVEL
global constant from an environment variable with the same name. Default status level isLog4jl.Level.ERROR
. - A logger event type is is initialized as
LOG4JL_LOG_EVENT
global constant from an environment variable with the same name. Default logger event type isLog4jl.Log4jlEvent
.
- A logger context selector is initialized as object and assigned to global constant
- Macro
Log4jl.logger
is called with(out) parameters- Parameters parsed
- Context selector is used to create a logging context
- Configuration is created a. Programmatic configuration is evaluated b. Configuration file is located, loaded and parsed
- Logging context is initialized with the created configuration
- Logging context is started
- Shutdown hook is created.
- Configuration is started
- Configuration is setup (properties and appenders are created)
- Configuration is configured (loggers are created and referenced to appenders)
- All appenders are started
- Logging context used to create a logger wrapper
- Logger object is returned
- Logger object is used in logging functions.
TODO: proper shutdown when workspace
is called.