Logging
Functions
These functions take a logger explicitly, so they do not need to discover logging state from the current task or consult the module registry. This makes them the preferred interface for hot paths, library internals, and execution contexts that need their own logger.
Explicit logger passing
clog(logger, group, level, msg...; kwargs...)
clogenabled(logger, group, level)::Bool
@clog logger group level msg...function solve(problem, logger)
clog(logger, :solver, 0, "starting")
clogenabled(logger, (:solver, :diagnostics)) && collect_diagnostics!(problem)
@clog logger (:solver, :summary) 1 "objective = $(objective_value(problem))"
endWhy pass a logger explicitly?
The standard-library logging macros (@info, @logmsg, ...) first look up the current task's logger, with the global logger as fallback. When a logger is already available—for example, in a const binding or a Ref—calling clog(logger, ...) bypasses that lookup, keeps the logging policy explicit, and helps keep hot paths very fast.
Task-specific loggers
The module-scoped model does not prevent task- or instance-specific logging. Use separate logger instances and pass them explicitly:
logger_a = ComponentLogger(...)
logger_b = ComponentLogger(...)
Threads.@spawn solve(problem_a, logger_a)
Threads.@spawn solve(problem_b, logger_b)The two tasks now have independent component rules and sinks without requiring task-local logging state.
Avoiding unnecessary work
clogenabled is intended to guard arbitrary work that should only run when a group/level is enabled:
if clogenabled(logger, (:solver, :trace), -1000)
trace = compute_expensive_trace()
clog(logger, (:solver, :trace), -1000, "trace"; trace)
endThe no-level form checks at Info:
clogenabled(logger, group)This also makes clogenabled useful as a lightweight runtime switch; see Hierarchical Runtime Control.
@clog provides lazy message construction and automatically captures caller metadata:
@clog logger :summary 0 begin
stats = compute_expensive_stats()
"stats = $stats"
endForwarding macro
At module top level, @forward_logger logger_expr creates local forwarding methods for clog, clogenabled, set_log_level!, get_log_level, and with_min_level, plus a shorter local @clog form bound to logger_expr:
const logger = ComponentLogger(...)
@forward_logger logger
clog(:core, 0, "hello")
@clog :core 0 "hello"
clogenabled(:core)
set_log_level!(:core, true)
get_log_level(:core)The expression can be a Ref or field expression and is resolved at each call. Its global names are bound in the module that invokes @forward_logger, even if its generated @clog is imported elsewhere.
@clog and level shorthands
@clog provides lazy logging with automatic caller metadata. Its level shorthands take an explicit logger argument at each call site.
Logging macros
The main macro interface mirrors familiar logging operations while adding explicit component groups:
@clog logger :solver 0 "starting"
@cdebug logger :solver "debug message"
@cinfo logger :solver "information"
@cwarn logger :solver "warning"
@cerror logger :solver "error"Hierarchical tuple groups are also supported where accepted by the macro:
@clog logger (:solver, :iteration) 0 "iteration started"Why use @clog?
@clog evaluates message expressions only after logging is enabled and keeps them inline, avoiding closure capture of surrounding local values.
It also automatically captures the emitting call's module, file, and line. The function APIs require that metadata to be supplied manually when it is needed.
When logging is enabled, @clog evaluates all message expressions from left to right. If the final expression evaluates to nothing, the log record is discarded and Logging.handle_message is not called. The @cdebug, @cinfo, @cwarn, and @cerror shorthands have the same behavior.
Caller metadata
Macros automatically capture the caller's module, file, and line number. This is the main ergonomic advantage over the explicit function API when source-location metadata matters.
Performance trade-off
With an explicit logger, @clog uses it directly; a forwarded local @clog evaluates its bound logger expression directly. Neither uses a module registry lookup.
This keeps the macro path suitable for hot code while retaining lazy message evaluation and caller metadata.
Reference
ComponentLogging.clog — Function
clog(logger, group, level, msg...; _module, id, file, line, kwargs...)Emit a log message through the given logger. group is a Symbol or NTuple{N,Symbol}. level may be LogLevel or Integer. msg can be one or more values; tuples are passed through as-is.
Keyword arguments file, line, and arbitrary kwargs... are forwarded to the underlying logger sink.
If @forward_logger is already used, the following forwarding signatures are available:
clog(group, level, msg...; kwargs...)
clog(group, msg...; kwargs...)ComponentLogging.clogenabled — Function
clogenabled(logger, group, level) -> Bool
clogenabled(logger, group) -> BoolReturn whether logging is enabled for the given logger, group, and level. If level is omitted, 0 is used.
If @forward_logger is already used, the following forwarding signatures are available:
clogenabled(group, level) -> Bool
clogenabled(group) -> BoolComponentLogging.@forward_logger — Macro
@forward_logger logger_exprDefine forwarding methods in the current module so you can call clog, clogenabled, set_log_level!, get_log_level, and with_min_level without explicitly passing a logger each time. Also define a local @clog group level msg... bound to logger_expr.
logger_expr may be an AbstractLogger, a Ref holding one, or an expression such as STATE[].logger. Its names are bound in the module that invokes @forward_logger; importing the generated @clog elsewhere does not change which logger expression it uses. The expression is evaluated at each call, so replacing a Ref value affects subsequent calls.
Example:
using ComponentLogging
const pkg_logger = Ref(ComponentLogger(...))
@forward_logger pkg_logger
clog(:core, 0, "hello")
@clog :core 0 "hello"
set_log_level!(:core, 1000)
get_log_level(:core)
with_min_level(2000) do
# Temporarily raise this logger's minimum level (fast early rejection).
clog(:core, 0, "suppressed by the temporary minimum")
endNote: Use this macro at module top-level.
ComponentLogging.@clog — Macro
@clog logger group level msg...Macro version of clog that captures the caller's Module, file, and line for accurate provenance. All positional arguments are required, including at least one message expression. group and level may be runtime expressions.
Message expressions are evaluated only after logging is enabled and remain inline, avoiding closure capture of surrounding local variables. Unlike clog, caller metadata is captured automatically rather than supplied as keywords.
When logging is enabled, all message expressions are evaluated from left to right. If the final expression evaluates to nothing, the log record is discarded and Logging.handle_message is not called.
Example:
@clog logger :core 0 "hello"
@clog logger (:a, :b) 2000 "hello"ComponentLogging.@cdebug — Macro
@cdebug logger group msg...Shorthand for @clog logger group -2000 msg....
ComponentLogging.@cinfo — Macro
@cinfo logger group msg...Shorthand for @clog logger group 0 msg....
ComponentLogging.@cwarn — Macro
@cwarn logger group msg...Shorthand for @clog logger group 1000 msg....
ComponentLogging.@cerror — Macro
@cerror logger group msg...Shorthand for @clog logger group 2000 msg....