Logging
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 Logback, 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.
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:
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.
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.
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
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}/:
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
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:
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:
Setting "additive": false on a custom logger stops its messages from also appearing in the root/runtime log, keeping your custom log files clean and focused.
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
categoriesis 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:
additive must be false on the blackhole logger (and any silencing logger) to prevent matched events from propagating to the root logger and appearing in runtime.log.
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 Lines — 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
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.
Related Documentation
Logging Configuration Reference — Full
boxlang.jsonlogging optionsInterceptors — Intercept and extend BoxLang events
Application.bx — Application lifecycle and configuration
Last updated
Was this helpful?
