githubEdit

file-linesLogging

A complete guide to logging in BoxLang — from quick one-liners to structured JSON logs, named loggers, and interceptors.

BoxLang ships with a production-ready logging system built on top of Logbackarrow-up-right, one of the most trusted logging frameworks in the Java ecosystem. Every piece of BoxLang — the runtime, modules, async engine, caching, and your own applications — funnels through this unified system, giving you a single place to configure, query, and extend logging behavior.

circle-info

BoxLang logging is powered by Logback under the hood, which means all of its rolling-file, encoder, and appender semantics apply directly. You do not need to configure Logback yourself — BoxLang wraps it cleanly — but knowledge of Logback concepts (loggers, appenders, encoders, additivity) will help you understand the more advanced options.

📋 Table of Contents


Quick Start

The easiest way to log anything in BoxLang is writeLog():

Using the tag/component syntax:


Log Levels

BoxLang supports five severity levels, listed from most to least severe:

Level
Constant
When to use

ERROR

"error"

Failures that need immediate attention

WARN

"warning"

Unexpected situations that are recoverable

INFO

"information"

Normal operational events

DEBUG

"debug"

Developer-facing detail for diagnosing issues

TRACE

"trace"

Very fine-grained tracing, highest verbosity

There is also a special value OFF that silences a logger entirely.

A logger set to a given level will capture that level and all more-severe levels. For example, a logger at INFO captures INFO, WARN, and ERROR, but not DEBUG or TRACE.


writeLog() BIF

writeLog() is a global built-in function (BIF) available everywhere in BoxLang scripts.

Argument
Type
Required
Default
Description

text

string

Yes

The message to log

type

string

No

"information"

Log level: "information", "warning", "error", "debug", "trace"

application

boolean

No

true

When true, prepends the application name to the log entry

log

string

No

"application"

Name of the target logger, or an absolute file path

Basic Examples

Logging to a File Path

If the log argument is an absolute path, BoxLang automatically creates a rolling file appender at that location. This is useful for audit trails or legacy compatibility:

Dynamic Logger Creation

If you pass a logger name that does not yet exist in the configuration, BoxLang creates it dynamically as a rolling file appender inside the configured logs directory. The file will be named <loggerName>.log.


bx:log Component

The bx:log component provides the same functionality as writeLog() but in tag/template syntax. It is useful in .bxm template files or when mixing markup and logic.

Attribute
Type
Required
Default
Description

text

string

No

The message to log

log

string

No

"application"

Named logger or absolute file path

type

string

No

"information"

Log level

application

boolean

No

true

Prepend application name

circle-info

The file attribute is accepted for CFML compatibility but is deprecated. Use log instead.

Template Examples

Script Syntax

bx:log can also be used in .bx script files:


Built-In Named Loggers

BoxLang registers several named loggers automatically at startup. Each one writes to its own rotating log file inside {logsDirectory}/:

Logger Name
Default Level
Purpose

runtime

inherits root

Core runtime events, startup, shutdown

application

TRACE

Application-level events; default target for writeLog()

modules

inherits root

Module loading, activation, and deactivation

async

inherits root

Thread pools, futures, scheduled task execution

cache

inherits root

Cache provider activity and evictions

datasource

inherits root

Datasource creation, query logging, connection pool

scheduler

INFO

Scheduled task runs and outcomes

You can target any of these by name:


Configuration Reference

All logging configuration lives in boxlang.json under the logging key. Below is a fully annotated example:

Global Settings

Setting
Default
Description

logsDirectory

${boxlang-home}/logs

Where log files are written. Supports BoxLang home variable interpolation.

maxLogDays

90

Days to retain log files. 0 disables time-based rotation.

maxFileSize

"100MB"

Per-file size limit before rotation. Supports KB, MB, GB suffixes.

totalCapSize

"5GB"

Total disk cap across all log files. Oldest archives are removed first.

rootLevel

"WARN"

Minimum level for the root (catch-all) logger. Automatically raised to DEBUG when BoxLang debug mode is active.

defaultEncoder

"text"

Encoding format for all appenders unless overridden per-logger. "text" or "json".

statusPrinterOnLoad

false

Print resolved Logback config at startup. Helpful when diagnosing misconfiguration.

Logger Settings

Each named logger under loggers supports:

Property
Type
Default
Description

level

string

inherits rootLevel

Logger-specific level threshold. Omit to inherit from root.

appender

string

"file"

Output target. Currently "file" and "console". Coming soon: "smtp", "socket", "db", "syslog".

appenderArguments

object

{}

Appender-specific key-value configuration.

encoder

string

defaultEncoder

Override the encoder for this logger: "text" or "json".

additive

boolean

true

When true the logger propagates messages to its parent loggers (including root). Set to false to isolate this logger's output.

categories

array

[]

List of Java package or class names whose log output should be routed to this logger. Entries are matched by logger name prefix. Leading/trailing whitespace is trimmed automatically.


Custom Named Loggers

You can define your own loggers in boxlang.json and then target them by name in your code. This is the recommended approach for separating concerns across different parts of your application:

circle-check

Logger Categories

Logger categories let you route third-party Java library log output into a BoxLang-managed logger. Any Java package or class name listed in a logger's categories array will be redirected to that logger — using its configured level and appender — instead of being handled by the default Logback root configuration.

This is especially useful for silencing chatty libraries or capturing their output in a dedicated file.

How It Works

  • Each entry in categories is a Java package or class name (e.g. "com.zaxxer.hikari").

  • Log events originating from that package or any sub-package are captured by the target logger.

  • The category logger inherits the target logger's level threshold and appender.

  • Routing is non-additive: matched events do not bubble up further.

  • Leading/trailing whitespace in category names is trimmed automatically.

  • Loggers with "level": "OFF" skip appender creation entirely, making them a zero-overhead sink.

Routing Noisy Libraries

Configure a dedicated logger to capture (or suppress) output from a specific library:

All log events from com.zaxxer.hikari.* will now be written to hikari.log at WARN level or above, and will not appear in runtime.log.

The Built-In Blackhole Logger

BoxLang ships with a pre-configured blackhole logger in the default boxlang.json. Its level is set to OFF, so any categories assigned to it are completely silenced with no I/O overhead:

To suppress a noisy library, add its package to the blackhole categories list:

circle-exclamation

Multiple Categories Across Loggers

You can spread categories across multiple loggers. For example, route database connection pool noise to a dedicated file while silencing framework internals entirely:


JSON Structured Logging

Switch a logger's encoder to "json" to emit JSON Linesarrow-up-right — one JSON object per log entry. This is ideal for log aggregators (Splunk, Datadog, Loki, OpenSearch, etc.) that parse structured data.

Or selectively for a single logger:

Each line in the log file will look like:


LoggingService API

For advanced scenarios you can interact with the logging subsystem directly through the BoxLang LoggingService. Retrieve it from the BoxLang runtime:

Registering a Logger at Runtime

If you need a logger that isn't in boxlang.json and you want more control than the auto-creation provided by writeLog(), you can register one programmatically:


Logging Interceptors

BoxLang fires an interception event every time a log message is processed. You can intercept these events to add custom sinks (e.g. send errors to Slack, record to a database, stream to a monitoring service) without modifying your application code.

The event name is logMessage.

Registering an Interceptor in Application.bx

Standalone Interceptor Class

circle-info

See Interceptors for a full guide to the interception framework, and Logging Events for the complete list of logging interception points.


Best Practices

Choose the Right Level

Isolate Concerns with Named Loggers

Avoid dumping everything into application.log. Create dedicated loggers for distinct subsystems so that logs are easy to find, tail, and rotate independently:

Use JSON Encoding for Machine-Readable Logs

If you route logs to a SIEM, APM, or log aggregator, switch to JSON encoding so fields are parsed correctly:

Do Not Build Expensive Strings at TRACE/DEBUG Unless Necessary

Use Additive = false for Custom Loggers

Setting "additive": false stops entries from bubbling up into the root logger and appearing in the runtime.log. This keeps your focused logs clean:

Rotate and Cap Aggressively in Production

Smaller caps prevent runaway disk usage, especially at DEBUG or TRACE levels.


Last updated

Was this helpful?