mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-05-30 15:42:25 +00:00
8.8 KiB
8.8 KiB
BitLogger
BitLogger is a structured logging library written in MoonBit.
Overview
BitLogger currently provides:
- log levels:
Trace,Debug,Info,Warn,Error - structured key-value fields
- plain console output
- JSON console output
- child target composition via
child(...) - context fields via
with_context_fields(...) - optional timestamps via
with_timestamp() - sink fanout via
fanout_sink(...) - sink routing via
split_sink(...)andsplit_by_level(...) - custom integration via
callback_sink(...) - in-memory buffering via
buffered_sink(...) - record filtering via
filter_sink(...) - reusable filter helpers such as
target_has_prefix(...),message_contains(...),level_at_least(...), andfield_equals(...) - record patching via
with_patch(...)andpatch_sink(...) - patch helpers such as
prefix_message(...),append_fields(...), andredact_fields(...) - context binding via
bind(...)andfields(...) - explicit queued delivery via
queued_sink(...)andwith_queue(...) - bounded backlog with
QueueOverflowPolicy::DropNewestandQueueOverflowPolicy::DropOldest - configurable text formatting via
text_formatter(...),format_text(...),text_console_sink(...), and template-driventemplateoutput - formatter-based callback integration via
formatted_callback_sink(...) - native-only file output via
file_sink(...), with basic size rotation, backup retention, explicitreopen(), and failure counters native_files_supported()for backend capability detection- default global logger helpers
Quick Start
let logger = Logger::new(console_sink(), min_level=Level::Info, target="demo")
.with_timestamp()
.with_context_fields([field("service", "bitlogger")])
logger.info("starting", fields=[field("port", "8080")])
Child target composition:
let worker = Logger::new(console_sink(), target="app").child("worker")
worker.info("job ready")
Custom callback sink:
let hook = Logger::new(
callback_sink(fn(rec) {
println("callback saw [\{rec.target}] \{rec.message}")
}),
target="hook",
)
hook.info("hello")
Basic buffered sink:
let sink = buffered_sink(console_sink(), flush_limit=2)
let logger = Logger::new(sink, target="buffered")
logger.info("one")
logger.info("two")
sink.flush()
Basic filter sink:
let sink = filter_sink(console_sink(), fn(rec) {
rec.target == "kept"
})
let kept = Logger::new(sink, target="kept")
let dropped = Logger::new(sink, target="dropped")
kept.info("visible")
dropped.info("hidden")
Chained logger filter:
let logger = Logger::new(console_sink(), target="service")
.with_filter(all_of([
target_has_prefix("service"),
message_contains("visible"),
]))
logger.info("hidden")
logger.child("api").info("visible")
Record patching:
let logger = Logger::new(console_sink(), target="auth")
.with_patch(compose_patches([
prefix_message("[safe] "),
redact_fields(["token"]),
append_fields([field("service", "bitlogger")]),
]))
logger.info("login", fields=[field("user", "alice"), field("token", "secret")])
Context binding:
let logger = Logger::new(console_sink(), target="audit")
.bind(fields([("service", "bitlogger"), ("scope", "login")]))
logger.info("accepted", fields=[field("user", "alice")])
Explicit queued sink:
let logger = Logger::new(console_sink(), target="queue")
.with_queue(max_pending=2, overflow=QueueOverflowPolicy::DropOldest)
logger.info("one")
logger.info("two")
logger.info("three")
ignore(logger.sink.flush())
Level-based split sink:
let logger = Logger::new(
split_by_level(
callback_sink(fn(rec) {
println("high priority: \{rec.level.label()} \{rec.message}")
}),
console_sink(),
min_level=Level::Warn,
),
min_level=Level::Trace,
target="split",
)
logger.info("normal output")
logger.warn("warning output")
Custom text formatter:
let formatter = text_formatter(
show_timestamp=false,
field_separator=",",
template="[{level}] {target} {message} :: {fields}",
)
let logger = Logger::new(text_console_sink(formatter), target="pretty")
logger.info("hello", fields=[field("mode", "pretty")])
JSON config loading:
let config = parse_logger_config_text(
"{\"min_level\":\"debug\",\"target\":\"config.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"field_separator\":\",\",\"template\":\"[{level}] {target} {message} :: {fields}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}",
)
let logger = build_logger(config)
logger.info("configured from json")
ignore(logger.flush())
Native file sink:
if native_files_supported() {
let logger = Logger::new(
file_sink("bitlogger.log", rotation=Some(file_rotation(128, max_backups=2))),
target="file",
)
logger.info("hello", fields=[field("kind", "file")])
ignore(logger.sink.flush())
ignore(logger.sink.close())
}
Repository Layout
bitlogger/: MoonBit library package, tests, and Mooncake package READMEexamples/basic/: runnable example package
Links
Config Notes
- BitLogger now includes a JSON config layer via
parse_logger_config_text(...),stringify_logger_config(...), andbuild_logger(...). - Supported keys include
min_level,target,timestamp,sink.kind,sink.path,sink.append,sink.auto_flush,sink.rotation,sink.text_formatter, andqueue. sink.rotationcurrently supportsmax_bytesandmax_backupsfor basic size-based rotation and backup retention.file_sink(...)also exposesreopen(),open_failures(),write_failures(),flush_failures(), androtation_failures()for basic observability.file_sink(...)also exposesappend_mode(). Passingappend=...toreopen(...)updates the current append policy used by later reopen calls.file_sink(...)also supportsset_append_mode(...)for explicitly changing the append policy that later reopen calls will use.file_sink(...)also exposespath()andauto_flush_enabled()for reading basic file-sink policy state.file_sink(...)also exposesrotation_enabled()androtation_config()for reading whether rotation is active and which parameters are currently in effect.file_sink(...)also supportsset_auto_flush(...),set_rotation(...), andclear_rotation()for runtime policy updates.ConfiguredLoggerbuilt throughbuild_logger(...)also exposesfile_reopen(),file_flush(),file_close(),file_append_mode(),file_path(),file_auto_flush(),file_rotation_enabled(),file_rotation_config(), plusfile_set_append_mode(...),file_set_auto_flush(...),file_set_rotation(...),file_clear_rotation(), and the corresponding file failure counters, so config-driven file logging keeps a usable control surface.sink.text_formatter.templatecurrently supports fixed tokens:{timestamp},{timestamp_ms},{level},{target},{message}, and{fields}.- Config-driven sink assembly currently supports
console,json_console,text_console, andfile. queueremains a synchronous bounded wrapper around the final sink, not an async runtime.
Async Layer
- A separate
bitlogger_async/package is now included. - It uses
moonbitlang/asyncand providesAsyncLogger,async_logger(...), a backgroundrun()worker, and bounded async queue delivery. - The current async API already supports
with_context_fields(...),with_filter(...),with_patch(...),with_target(...), andchild(...). shutdown()is now the recommended way to stop the async worker. By default it waits for the queue to drain, closes the queue, and then waits for the worker to exit.- Basic lifecycle observability is also available through
is_closed(),is_running(),has_failed(), andlast_error(). - The async worker now supports batched queue draining via
max_batchand basic flush policies throughflush=Never|Batch|Shutdown. - The recommended startup pattern is shown in examples/async_basic/main.mbt.
- This layer currently targets
native/llvmonly and remains isolated from the synchronous logger core.
Async Config
parse_async_logger_config_text(...),stringify_async_logger_config(...),parse_async_logger_build_config_text(...), andbuild_async_logger(...)are now available.- The JSON root is split into
loggerandasync_config. loggerfully reuses the synchronousLoggerConfigschema, whileasync_configcurrently supportsmax_pending,overflow,max_batch, andflush.- The recommended config-driven startup flow is shown in examples/async_basic/main.mbt.