Writing an ELCL Logging Configuration

Moving logging choices into ELCL gives the people who run an application control over its output without turning those choices into command-line switches or requiring a new build. The application still receives the same complete configuration snapshot it would create in C++: the file simply provides a readable, reviewable description of that snapshot.

A logging branch has four layers. Format shapes the rendered lines, Queue bounds the manager’s resources, Trace Sections selects the diagnostic groups that may produce trace entries, and the ordered Writers section list describes the destinations and their routes. Every layer is optional, but a useful configuration normally contains at least one writer; otherwise the manager has nowhere to deliver accepted entries.

ELCL names are case-insensitive, and spaces and underscores are equivalent. Maximum Entries therefore names the same field as maximum_entries. The examples use spacious, human-readable names, while enumerated text values such as short_upper and daily use the canonical lowercase spellings listed on this page. Loading Logging Configuration shows how an application selects this branch and turns it into a LogConfiguration during startup.

Start With One Useful Destination

A small command-line tool often needs only one decision: send its log entries to the terminal. The following complete configuration does exactly that. Because it omits Levels and Paths, the writer accepts every level and stream path. Because it omits Format and Queue, the well-defined library defaults supply the line pattern and manager limits.

The console writer is the only built-in writer for which the parser itself needs an application resource. Construct LogConfigurationParser with a terminal before loading this file; without one, the parser cannot create the requested writer and reports an error.

--*[ Writers ]*-------------------------------------------------------------------------------------------------------

Type                        : "console"

Make Operational Choices Explicit

Defaults are convenient while an application is young. In a deployed service or a tool shared across a team, explicit choices are often easier to review: operators can see the queue budget, recognize the line format, and understand which streams reach each destination without consulting the C++ defaults.

The following configuration makes those decisions visible. It selects a format that includes the full stream name, bounds the queue, routes the guild hierarchy to the console, and limits the amount of terminal wrapping. These numbers are an example rather than a universal production recipe; choose capacities that match the application’s burst rate, message sizes, and destination speed. The configured-logging demo parses and executes this exact file, so the example remains synchronized with the implementation.

# Complete root-level configuration for the explorer guild demo.
--[ Format ]----------------------------------------------------------------------------------------------------------
Pattern                     : "{level} [{name}] {message}"
Timestamp Zone              : "utc"
Level                       : "short_upper"
Name                        : "full"
Name Limit                  : 40
Message Truncation          : "none"
Message Limit               : 0
Truncation Mark             : "…"

--[ Queue ]-----------------------------------------------------------------------------------------------------------
Maximum Entries             : 512
Maximum Bytes               : 1'048'576
Reserved Error Entries      : 64
Reserved Error Bytes        : 131'072
Maximum Message Bytes       : 65'536
Shutdown Timeout Ms         : 2'000

--*[ Writers ]*-------------------------------------------------------------------------------------------------------
Type                        : "console"
Levels                      : "trace", "information", "warning", "error"
Paths                       : "guild"
Line Indent                 : 0
First Line Indent           : -1
Wrapped Line Indent         : 4
Maximum Line Wraps          : 2

Logging does not have to occupy the document root. The nested form below uses the same schema beneath Application.Logging and keeps the application’s other settings beside it. It also enables one trace section, showing that a nested branch loses none of the available logging controls:

# Logging can be one branch in a larger application configuration.
--[ Application ]-----------------------------------------------------------------------------------------------------
Name                        : "Pohjoinen tutkimusretki"

--[ Application . Logging ]-------------------------------------------------------------------------------------------
Trace Sections              :
    * "route-search"

--[ . Format ]--------------------------------------------------------------------------------------------------------
Pattern                     : "{level} {name}: {message}"
Name                        : "leaf"

--*[ . Writers ]*-----------------------------------------------------------------------------------------------------
Type                        : "console"
Levels                      : "trace", "information", "warning", "error"
Paths                       : "guild"

Shape Every Log Line

Each log entry carries a timestamp, level, stream path, and message. The Format section decides how writers combine those values into a line; it does not change the entry itself. That distinction allows the manager to retain timestamps in UTC and apply a local-time choice only when the line is rendered.

Omitting the section uses LogLineFormat defaults. When you add it, begin with Pattern: its placeholders decide which entry values readers can see. The remaining fields refine how those placeholders appear and how exceptionally long names or messages are shortened.

Field

Default

Values and purpose

Pattern

{time} {level} - {message}

Nonempty text with {time}, {level}, {name}, and {message}; doubled braces are literals.

Timestamp Zone

utc

utc or local for rendering retained UTC timestamps.

Level

short_upper

short_upper, short_lower, full_lower, or full_upper.

Name

full

full, leaf, head_and_leaf, or left_truncated.

Name Limit

40

A nonnegative code-point limit used by left_truncated.

Message Truncation

none

none, first_line, characters, or total_line.

Message Limit

0

A nonnegative code-point limit used by the selected truncation mode.

Truncation Mark

Text appended after a rendering-time truncation.

The formatting limits count Unicode code points and act while a writer renders a line. They are intentionally separate from Maximum Message Bytes in the queue section, which protects producers and counts encoded bytes. For example, a service can accept a detailed message into the manager, keep it intact for a file writer, and use a bounded console layout where space is scarce. Formatting Log Lines demonstrates every representation and truncation mode in isolation.

Give the Manager a Clear Resource Budget

Logging sits between producer threads and destinations that may briefly be slower. The Queue section defines how much memory and how many entries the manager may retain during that gap. These are safeguards, not throughput targets: normal operation should leave enough headroom that short bursts do not immediately cause drops.

Omitting Queue uses LogManagerOptions defaults. Entry and byte reservations are portions of the corresponding total capacity, not additional capacity. They preserve room for warning and error entries after ordinary trace and information traffic has filled the unreserved part of the queue. Byte fields are plain integer byte counts; ELCL digit separators make larger values much easier to review.

Field

Default

Values and purpose

Maximum Entries

4'096

A positive total number of queued entries.

Maximum Bytes

8'388'608

A positive total number of sanitized message bytes in the queue.

Reserved Error Entries

256

A nonnegative part of the total entry capacity reserved for warnings and errors.

Reserved Error Bytes

1'048'576

A nonnegative part of the total byte capacity reserved for warnings and errors.

Maximum Message Bytes

262'144

A positive producer-side byte limit for one sanitized message.

Shutdown Timeout Ms

2'000

A nonnegative graceful shutdown deadline in milliseconds.

When you reduce a maximum and omit its reservation, the parser clamps the default reservation to the new maximum. This makes compact configurations safe. An explicitly configured reservation expresses a deliberate relationship and must not exceed its corresponding total when the configuration is installed.

Maximum Message Bytes is enforced before a message joins the queue, whereas Maximum Bytes accounts for all sanitized messages currently retained. Shutdown Timeout Ms sets how long shutdown waits for writers to drain pending work; zero is valid when the application deliberately requires an immediate deadline. The consequences of each limit, including drop statistics, are developed in Configuring Log Manager Limits.

Enable the Trace Detail You Need

Trace sections let an application compile detailed diagnostic logging into normal code while keeping individual groups quiet until they are needed. The Trace Sections value list names the groups enabled by this startup configuration. Omit it to disable every group, which is the usual steady-state choice for a production service.

Unlike ELCL field names, trace-section identifiers are case-sensitive application identifiers. Each may contain ASCII letters, digits, underscores, and hyphens, and must begin with a letter or underscore. Enabling a section permits its guarded code to produce trace entries; those entries become visible only when at least one writer route also accepts the trace level and the stream path. Keeping this configuration stable after startup avoids changing the meaning of cached trace guards while work is in flight. Using Trace Sections explains the recommended grouping and guard pattern.

Connect Entries to Their Destinations

The Writers section is a section list because an application commonly needs more than one destination. Each entry creates one built-in writer together with a filter. Levels answers which severities the destination should receive; Paths answers which parts of the stream hierarchy belong there. Omitting either filter dimension accepts all values for that dimension.

Routes may overlap. A warning from guild/routes can go to the console, a rotating file, and a last-error buffer at the same time when it matches all three definitions. Writer entries retain their document order, but routing is not a first-match decision: every matching writer receives the entry. Paths are lowercase slash-delimited roots matched on complete segments, so guild accepts guild/routes but does not accidentally accept guildhall.

Field

Default

Values and purpose

Type

Required

console, file, or syslog.

Levels

All levels

A list containing trace, information or info, warning or warn, and error.

Paths

All paths

A list of lowercase log path roots matched on complete segments.

Keep Terminal Output Comfortable to Read

A console writer is the natural destination for interactive tools and foreground service runs. It understands the terminal width and lays each formatted source line out as a paragraph. The following fields let an application reserve space for surrounding terminal output and keep long messages visually connected to their first line.

Field

Default

Values and purpose

Line Indent

0

A nonnegative leading column count for every line.

First Line Indent

-1

A nonnegative first-line indent, or -1 to reuse Line Indent.

Wrapped Line Indent

-1

A nonnegative continuation indent, or -1 to reuse Line Indent.

Maximum Line Wraps

0

A nonnegative wrap limit per source line; zero allows unlimited wrapping.

For a specialized indent, -1 tells the writer to inherit Line Indent; it is not a negative terminal column. A zero wrap limit means unlimited wrapping rather than no wrapping. Choose a finite limit when untrusted or exceptionally long messages could otherwise dominate an interactive screen. Writing Logs to the Console shows the paragraph layout and styles in action.

Preserve an Operational History in Files

A file writer gives an application durable history that remains available after the terminal session has ended. Its configuration begins with the active Path, then decides how the first open treats an existing file and when the writer moves the active content into an archive. Rotation is useful only when it is paired with a retention policy that matches the available storage and the period operators may need to investigate.

Field

Default

Values and purpose

Path

Required

The nonempty active file path.

Mode

append

append or overwrite for the first successful open.

Rotation

none

none, hourly, daily, weekly, or size.

Maximum Size

10'485'760

A positive byte threshold used by size rotation.

Retention

7

A nonnegative number of rotated archives to keep.

Mode governs the first successful open; it does not turn every later retry into an overwrite. Maximum Size is used only by size rotation, while hourly, daily, and weekly rotation use entry timestamps to choose their boundaries. Retention counts rotated archives, not the active file, and zero removes archives once they are no longer active. Writing Logs to Files covers archive names, external file replacement, retry behavior, and failure accounting.

Send Service Logs to a Syslog Collector

Syslog is useful when a service’s logs must leave the local machine and join a central operational view. The writer encodes entries as RFC 5424 messages and delivers them over UDP, TCP, or TLS. The transport is therefore the first operational choice: UDP has no connection setup but cannot confirm delivery, while TCP and TLS maintain a connection and can accumulate pending bytes during an interruption.

Field

Default

Values and purpose

Endpoint

Required

A host and nonzero port; UDP requires a numeric IP address.

Transport

udp

udp, tcp, or tls.

Facility

1

An RFC 5424 facility number from zero through 23.

Host Name

-

The RFC 5424 HOSTNAME field or - for NILVALUE.

Application Name

erbsland-core

The RFC 5424 APP-NAME field or - for NILVALUE.

Process Id

-

The RFC 5424 PROCID field or - for NILVALUE.

Message Id

-

The RFC 5424 MSGID field or - for NILVALUE.

Tls Label

log/syslog

A nonempty network/TLS configuration label used by TLS transport.

Maximum Pending Bytes

1'048'576

A positive limit for encoded syslog data awaiting delivery.

The RFC identity fields are deliberately explicit. Use meaningful stable values when the collector relies on them for search or routing, and retain - when a field has no trustworthy value. Tls Label selects the application’s network/TLS configuration rather than embedding certificates or trust choices in the logging branch. Maximum Pending Bytes bounds local memory while a connection is unavailable; once that bound is reached, additional encoded messages are dropped and counted rather than allowing logging to grow without limit. Sending Logs to Syslog discusses framing, transport behavior, and deployment guidance in detail.