mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
📝 add example-first documentation paths
This commit is contained in:
@@ -104,6 +104,26 @@ function buildChangesSidebar(): DefaultTheme.SidebarItem[] {
|
||||
]
|
||||
}
|
||||
|
||||
function buildExamplesSidebar(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{ text: 'Overview', link: '/examples/' },
|
||||
{ text: 'Console And Fields', link: '/examples/console' },
|
||||
{ text: 'File Rotation', link: '/examples/file-rotation' },
|
||||
{ text: 'Configuration', link: '/examples/config' },
|
||||
{ text: 'Async Lifecycle', link: '/examples/async' },
|
||||
]
|
||||
}
|
||||
|
||||
function buildExtendSidebar(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{ text: 'Overview', link: '/extend/' },
|
||||
{ text: 'Queueing', link: '/extend/queue' },
|
||||
{ text: 'Sink Composition', link: '/extend/composition' },
|
||||
{ text: 'Text Formatting', link: '/extend/formatting' },
|
||||
{ text: 'Target Boundaries', link: '/extend/targets' },
|
||||
]
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
title: 'BitLogger',
|
||||
description: 'Structured logging library docs for MoonBit.',
|
||||
@@ -118,6 +138,8 @@ export default defineConfig({
|
||||
siteTitle: 'BitLogger',
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Examples', link: '/examples/' },
|
||||
{ text: 'Extend', link: '/extend/' },
|
||||
{ text: 'API', link: '/api/' },
|
||||
{ text: 'Changes', link: '/changes/' },
|
||||
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
|
||||
@@ -131,6 +153,8 @@ export default defineConfig({
|
||||
text: 'Edit this page on GitHub',
|
||||
},
|
||||
sidebar: {
|
||||
'/examples/': buildExamplesSidebar(),
|
||||
'/extend/': buildExtendSidebar(),
|
||||
'/api/': buildApiSidebar(),
|
||||
'/changes/': buildChangesSidebar(),
|
||||
},
|
||||
|
||||
+29
-23
@@ -9,24 +9,38 @@ BitLogger is a structured logging library for MoonBit projects.
|
||||
|
||||
BitLogger gives you consistent levels, targets, structured fields, configurable text output, native file logging, and an async logging layer.
|
||||
|
||||
## Quick Start
|
||||
## Start With A Working Flow
|
||||
|
||||
```moonbit
|
||||
let logger = build_logger(
|
||||
text_console(
|
||||
min_level=Level::Info,
|
||||
target="demo",
|
||||
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
|
||||
),
|
||||
)
|
||||
The documentation is organized around complete usage flows. Start with an executable logger, then move into file output, configuration, async lifecycle, and extensions. The API reference remains the precise symbol-level reference.
|
||||
|
||||
logger.info("starting", fields=[field("port", "8080")])
|
||||
ignore(logger.flush())
|
||||
### 1. Install
|
||||
|
||||
```bash
|
||||
moon new log-demo
|
||||
cd log-demo
|
||||
moon add Nanaloveyuki/BitLogger@0.7.0
|
||||
```
|
||||
|
||||
Start with `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)`, then add `with_queue(...)` or `with_file_rotation(...)` only when needed.
|
||||
### 2. Import And Log
|
||||
|
||||
Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
Add the package import to your application's `moon.pkg`:
|
||||
|
||||
```moonbit
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @log,
|
||||
}
|
||||
```
|
||||
|
||||
```moonbit
|
||||
fn main {
|
||||
let logger = @log.build_logger(
|
||||
@log.console(min_level=@log.Level::Info, target="app"),
|
||||
)
|
||||
logger.info("server started", fields=[@log.field("port", "8080")])
|
||||
}
|
||||
```
|
||||
|
||||
Continue with the [Examples guide](./examples/index.md): console fields, file rotation, JSON configuration, and async lifecycle are each documented as a complete path.
|
||||
|
||||
## Support Status
|
||||
|
||||
@@ -45,18 +59,10 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
- Composition helpers: queue, filter, patch, fanout, split, callback
|
||||
- Separate async package under `src-async`
|
||||
|
||||
## Examples
|
||||
|
||||
- `examples/console_basic/`: minimal console and JSON console example
|
||||
- `examples/text_formatter/`: text formatting and template example
|
||||
- `examples/style_tags/`: style tags and colored output example
|
||||
- `examples/config_build/`: config-based build example
|
||||
- `examples/presets/`: common preset combinations
|
||||
- `examples/file_rotation/`: native file logging and rotation example
|
||||
- `examples/async_basic/`: async logging example
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Examples](./examples/index.md): complete application-facing usage flows
|
||||
- [Extend](./extend/index.md): queueing, sink composition, formatting, and target boundaries
|
||||
- [API index](./api/index.md): canonical API reference, organized as one public API per file
|
||||
- [src package README](https://github.com/Nanaloveyuki/BitLogger/blob/main/src/README.mbt.md): package-level usage notes and target reminders
|
||||
- [`docs/changes/`](./changes/): versioned release notes and publish-facing change summaries
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Async Logger Lifecycle
|
||||
|
||||
Use the async package when a native application needs logging to run through an async queue. The library supports broader targets, but the shipped `async fn main` example is native-focused.
|
||||
|
||||
## Import Both Packages
|
||||
|
||||
```moonbit
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @log,
|
||||
"Nanaloveyuki/BitLogger/src-async" @async_log,
|
||||
"moonbitlang/async",
|
||||
}
|
||||
```
|
||||
|
||||
## Build, Run, Then Shut Down
|
||||
|
||||
```moonbit
|
||||
async fn main {
|
||||
let raw = "{\"logger\":{\"min_level\":\"info\",\"target\":\"service.async\",\"sink\":{\"kind\":\"text_console\"}},\"async_config\":{\"max_pending\":64,\"overflow\":\"DropOldest\",\"max_batch\":16,\"linger_ms\":5,\"flush\":\"Batch\"}}"
|
||||
let config = @async_log.parse_async_logger_build_config_text(raw) catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
let logger = @async_log.build_async_logger(config)
|
||||
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(allow_failure=true, () => logger.run())
|
||||
logger.info("worker started", fields=[@log.field("mode", "async")])
|
||||
logger.shutdown()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
`run()` owns the worker loop. Start it before producing records, then call `shutdown()` so pending work can follow the configured flush policy.
|
||||
|
||||
## Run The Repository Example
|
||||
|
||||
```bash
|
||||
moon run examples/async_basic --target native
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Inspect queue policy and flush choices in [Queueing](../extend/queue.md).
|
||||
- Reference: [`parse_async_logger_build_config_text(...)`](../api/parse-async-logger-build-config-text.md), [`build_async_logger(...)`](../api/build-async-logger.md), [`run()`](../api/async-logger-run.md), and [`shutdown()`](../api/async-logger-shutdown.md).
|
||||
@@ -0,0 +1,35 @@
|
||||
# Configuration-Driven Logging
|
||||
|
||||
Use configuration when deployment should select levels, output, formatting, or queue behavior without changing application code.
|
||||
|
||||
## Parse, Build, Then Log
|
||||
|
||||
```moonbit
|
||||
let raw = "{\"min_level\":\"info\",\"target\":\"service\",\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"separator\":\" | \"}},\"queue\":{\"max_pending\":64,\"overflow\":\"DropOldest\"}}"
|
||||
|
||||
let config = @log.parse_logger_config_text(raw) catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
println("invalid logger configuration")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let logger = @log.build_logger(config)
|
||||
logger.info("configured logger ready")
|
||||
ignore(logger.flush())
|
||||
```
|
||||
|
||||
Parsing validates the data before runtime construction. Keep the raw configuration outside application code in a real deployment, but handle parsing errors at the boundary where configuration enters the process.
|
||||
|
||||
## Run The Repository Example
|
||||
|
||||
```bash
|
||||
moon run examples/config_build
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Use [Queueing](../extend/queue.md) to choose overflow behavior deliberately.
|
||||
- Use [Async lifecycle](./async.md) when the queue needs a background worker.
|
||||
- Reference: [`parse_logger_config_text(...)`](../api/parse-logger-config-text.md), [`build_logger(...)`](../api/build-logger.md), and [`with_queue(...)`](../api/with-queue.md).
|
||||
@@ -0,0 +1,51 @@
|
||||
# Console And Structured Fields
|
||||
|
||||
Use this flow for command-line tools and services that need a human-readable starting point. A record always has a level, target, message, and optional fields.
|
||||
|
||||
## Install And Import
|
||||
|
||||
```bash
|
||||
moon add Nanaloveyuki/BitLogger@0.7.0
|
||||
```
|
||||
|
||||
```moonbit
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @log,
|
||||
}
|
||||
```
|
||||
|
||||
## Build A Console Logger
|
||||
|
||||
```moonbit
|
||||
fn main {
|
||||
let logger = @log.build_logger(
|
||||
@log.console(min_level=@log.Level::Info, target="service.http"),
|
||||
)
|
||||
|
||||
logger.info("listening", fields=[
|
||||
@log.field("port", "8080"),
|
||||
@log.field("environment", "development"),
|
||||
])
|
||||
logger.warn("slow request", fields=[@log.field("path", "/health")])
|
||||
}
|
||||
```
|
||||
|
||||
`min_level` filters records before they reach the sink. `target` identifies the subsystem, while fields carry queryable context without interpolating it into the message.
|
||||
|
||||
## Switch To JSON
|
||||
|
||||
Keep the logging calls unchanged and replace the preset:
|
||||
|
||||
```moonbit
|
||||
let logger = @log.build_logger(
|
||||
@log.json_console(min_level=@log.Level::Info, target="service.http"),
|
||||
)
|
||||
```
|
||||
|
||||
This is the preferred output for a collector that consumes one structured record per line.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Use [Text formatting](../extend/formatting.md) for a custom terminal format.
|
||||
- Use [Configuration](./config.md) when levels, targets, and sinks come from JSON.
|
||||
- Reference: [`console(...)`](../api/console.md), [`json_console(...)`](../api/json-console.md), [`field(...)`](../api/field.md), and [`build_logger(...)`](../api/build-logger.md).
|
||||
@@ -0,0 +1,51 @@
|
||||
# File Output And Rotation
|
||||
|
||||
File output is for native applications that need local persistence. Treat it as a native-only capability and keep a console fallback for portable code.
|
||||
|
||||
## Check The Capability
|
||||
|
||||
```moonbit
|
||||
if !@log.native_files_supported() {
|
||||
println("file logging is unavailable on this target")
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## Build A Rotating File Logger
|
||||
|
||||
```moonbit
|
||||
let config = @log.with_file_rotation(
|
||||
@log.file(
|
||||
"service.log",
|
||||
min_level=@log.Level::Info,
|
||||
target="service.file",
|
||||
auto_flush=true,
|
||||
) catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
1024 * 1024,
|
||||
max_backups=3,
|
||||
)
|
||||
|
||||
let logger = @log.build_logger(config)
|
||||
logger.info("file logger ready")
|
||||
ignore(logger.flush())
|
||||
ignore(logger.file_close())
|
||||
```
|
||||
|
||||
The rotation limit is the size of the active file in bytes. `max_backups=3` retains the rotated backup chain. Always close a file-backed logger during orderly application shutdown.
|
||||
|
||||
## Run The Repository Example
|
||||
|
||||
```bash
|
||||
moon run examples/file_rotation --target native
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Add a bounded queue with [Queueing](../extend/queue.md) when bursts should not write synchronously.
|
||||
- Read [Target boundaries](../extend/targets.md) before sharing the same logging setup with web targets.
|
||||
- Reference: [`file(...)`](../api/file.md), [`with_file_rotation(...)`](../api/with-file-rotation.md), and [`file_close(...)`](../api/configured-logger-file-close.md).
|
||||
@@ -0,0 +1,24 @@
|
||||
# Examples
|
||||
|
||||
Examples are the primary BitLogger learning path. Each page starts from an application concern, shows the complete minimum code, and then points to the API reference for exact contracts.
|
||||
|
||||
## Choose A Flow
|
||||
|
||||
| Need | Start here | Then extend with |
|
||||
| --- | --- | --- |
|
||||
| Print structured logs to a terminal | [Console and fields](./console.md) | [Text formatting](../extend/formatting.md) |
|
||||
| Keep local logs with bounded disk use | [File rotation](./file-rotation.md) | [Target boundaries](../extend/targets.md) |
|
||||
| Build logging from app configuration | [Configuration](./config.md) | [Queueing](../extend/queue.md) |
|
||||
| Avoid blocking a native application on writes | [Async lifecycle](./async.md) | [Sink composition](../extend/composition.md) |
|
||||
|
||||
## Repository Examples
|
||||
|
||||
The runnable sources live under `examples/`. The guides below explain their intended use instead of duplicating every API detail:
|
||||
|
||||
- `examples/console_basic` for terminal and JSON records
|
||||
- `examples/config_build` for parsed configuration
|
||||
- `examples/file_rotation` for native file output
|
||||
- `examples/async_basic` for the async lifecycle
|
||||
- `examples/presets`, `examples/text_formatter`, and `examples/style_tags` for follow-on customization
|
||||
|
||||
Use the [API reference](../api/index.md) when a guide links to a symbol and you need its full signature or edge-case behavior.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Sink Composition
|
||||
|
||||
Presets cover common console and file configurations. Build a `Logger::new(...)` directly when records need routing or multiple outputs.
|
||||
|
||||
## Send A Record To Two Destinations
|
||||
|
||||
```moonbit
|
||||
let logger = @log.Logger::new(
|
||||
@log.fanout_sink(@log.console_sink(), @log.json_console_sink()),
|
||||
min_level=@log.Level::Info,
|
||||
target="service",
|
||||
)
|
||||
|
||||
logger.info("request complete", fields=[@log.field("status", "200")])
|
||||
```
|
||||
|
||||
`fanout_sink(...)` writes the same record to every child sink. This keeps human-oriented terminal output and machine-oriented JSON output in one application path.
|
||||
|
||||
## Route Warnings Separately
|
||||
|
||||
```moonbit
|
||||
let logger = @log.Logger::new(
|
||||
@log.split_by_level(
|
||||
@log.callback_sink(fn(rec) {
|
||||
println("alert: \{rec.message}")
|
||||
}),
|
||||
@log.console_sink(),
|
||||
min_level=@log.Level::Warn,
|
||||
),
|
||||
min_level=@log.Level::Trace,
|
||||
target="service",
|
||||
)
|
||||
```
|
||||
|
||||
Use direct sink composition only when the routing behavior is part of the application design. A preset remains easier to audit when one sink is sufficient.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [`Logger::new(...)`](../api/logger-new.md)
|
||||
- [`fanout_sink(...)`](../api/fanout-sink.md)
|
||||
- [`split_by_level(...)`](../api/split-by-level.md)
|
||||
- [`callback_sink(...)`](../api/callback-sink.md)
|
||||
@@ -0,0 +1,44 @@
|
||||
# Text Formatting And Style Tags
|
||||
|
||||
Formatting changes presentation while keeping the record's level, target, message, and fields intact. Use text output for people; use JSON output when another system parses the record.
|
||||
|
||||
## Customize The Text Shape
|
||||
|
||||
```moonbit
|
||||
let logger = @log.build_logger(
|
||||
@log.text_console(
|
||||
min_level=@log.Level::Info,
|
||||
target="service",
|
||||
text_formatter=@log.TextFormatterConfig::new(
|
||||
show_timestamp=false,
|
||||
field_separator=",",
|
||||
template="[{level}] {target} {message} :: {fields}",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
logger.info("ready", fields=[@log.field("port", "8080")])
|
||||
```
|
||||
|
||||
The template controls the visible order. Keep fields separate from the message so they can still be rendered consistently across sinks.
|
||||
|
||||
## Add Named Styles
|
||||
|
||||
```moonbit
|
||||
let formatter = @log.text_formatter(
|
||||
show_timestamp=false,
|
||||
color_mode=@log.ColorMode::Always,
|
||||
).with_style_tags(
|
||||
@log.default_style_tag_registry()
|
||||
.set_tag("accent", fg=Some("#4cc9f0"), bold=true),
|
||||
)
|
||||
```
|
||||
|
||||
Style tags are terminal presentation metadata. Do not use them as the only way to encode operational meaning; preserve that meaning in level, target, and fields.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [`text_console(...)`](../api/text-console.md)
|
||||
- [`TextFormatterConfig`](../api/text-formatter-config.md)
|
||||
- [`text_formatter(...)`](../api/text-formatter.md)
|
||||
- [`ColorMode`](../api/color-mode.md)
|
||||
@@ -0,0 +1,14 @@
|
||||
# Extend A Logger
|
||||
|
||||
Start from an [Example flow](../examples/index.md), then add one extension at a time. These pages explain when an abstraction is useful and which operational boundary it introduces.
|
||||
|
||||
## Extension Map
|
||||
|
||||
| Need | Extension | Main trade-off |
|
||||
| --- | --- | --- |
|
||||
| Absorb short output bursts | [Queueing](./queue.md) | You must choose overflow and flush behavior. |
|
||||
| Send records to multiple destinations or route by level | [Sink composition](./composition.md) | More explicit construction than presets. |
|
||||
| Control terminal appearance | [Text formatting](./formatting.md) | Formatting affects presentation, not record structure. |
|
||||
| Share code across native and web targets | [Target boundaries](./targets.md) | File and async runtime behavior differ by backend. |
|
||||
|
||||
For exact function signatures, follow each page's links into the [API reference](../api/index.md).
|
||||
@@ -0,0 +1,30 @@
|
||||
# Queueing And Overflow
|
||||
|
||||
Use a queue when a short burst of log writes should not immediately reach the output sink. Queueing is explicit because loss behavior is an application decision.
|
||||
|
||||
## Add A Synchronous Queue
|
||||
|
||||
```moonbit
|
||||
let config = @log.with_queue(
|
||||
@log.text_console(target="service"),
|
||||
max_pending=128,
|
||||
overflow=@log.QueueOverflowPolicy::DropOldest,
|
||||
)
|
||||
let logger = @log.build_logger(config)
|
||||
|
||||
logger.info("queued record")
|
||||
ignore(logger.flush())
|
||||
```
|
||||
|
||||
`DropOldest` preserves the newest operational information when the queue is full. Use `DropNewest` when preserving earlier records matters more. Calling `flush()` at a shutdown boundary makes the pending-record policy visible in application code.
|
||||
|
||||
## When To Use Async Instead
|
||||
|
||||
The synchronous queue is configuration around a normal runtime logger. When a native async application needs a worker lifecycle and batching, follow [Async logger lifecycle](../examples/async.md) instead.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [`with_queue(...)`](../api/with-queue.md)
|
||||
- [`QueueConfig`](../api/queue-config.md)
|
||||
- [`QueueOverflowPolicy`](../api/queue-overflow-policy.md)
|
||||
- [`flush()`](../api/configured-logger-flush.md)
|
||||
@@ -0,0 +1,33 @@
|
||||
# Target Boundaries
|
||||
|
||||
BitLogger keeps a portable structured logging surface, but native file output and async worker behavior are target-sensitive. Keep those boundaries in application code instead of assuming every sink behaves identically everywhere.
|
||||
|
||||
## Portable Default
|
||||
|
||||
`console(...)`, `json_console(...)`, structured fields, filters, patches, and the ordinary logger surface are the appropriate starting point for code shared across targets.
|
||||
|
||||
## Native-Only File Output
|
||||
|
||||
```moonbit
|
||||
if @log.native_files_supported() {
|
||||
let logger = @log.build_logger(@log.file("service.log") catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
logger.info("file output enabled")
|
||||
}
|
||||
```
|
||||
|
||||
File sinks need native filesystem support. Do not construct a file-only configuration unconditionally in code intended for web targets.
|
||||
|
||||
## Async Library Versus Async Entry Point
|
||||
|
||||
The async library exposes a compatibility surface across declared targets, while an executable `async fn main` still has stricter entry-point support. Keep a native-only executable example separate from portable library code.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [`native_files_supported()`](../api/native-files-supported.md)
|
||||
- [Target verification](../api/target-verification.md)
|
||||
- [Async logger lifecycle](../examples/async.md)
|
||||
+14
-16
@@ -4,31 +4,29 @@ layout: home
|
||||
hero:
|
||||
name: BitLogger
|
||||
text: Structured logging for MoonBit
|
||||
tagline: API reference, examples, and release notes for BitLogger.
|
||||
tagline: Start with a runnable logging flow, then extend it with precise API reference.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Open API Reference
|
||||
link: /api/
|
||||
text: Start Examples
|
||||
link: /examples/
|
||||
- theme: alt
|
||||
text: Read Release Notes
|
||||
link: /changes/
|
||||
text: Extend A Logger
|
||||
link: /extend/
|
||||
- theme: alt
|
||||
text: Mooncake Package
|
||||
link: https://mooncakes.io/docs/Nanaloveyuki/BitLogger
|
||||
|
||||
features:
|
||||
- title: Structured logging
|
||||
details: Levels, targets, message fields, and configurable text or JSON output.
|
||||
- title: Sync and async paths
|
||||
details: API reference covers both the main package and the async package surface.
|
||||
- title: Config and runtime helpers
|
||||
details: Browse builders, presets, file helpers, queue helpers, and verification notes quickly.
|
||||
- title: Examples first
|
||||
details: Follow complete console, file, config, and async flows before looking up individual symbols.
|
||||
- title: Extend deliberately
|
||||
details: Add queues, custom sink graphs, formatting, and target-specific behavior only when the base flow needs them.
|
||||
- title: API as reference
|
||||
details: Use the one-symbol-per-page API reference for exact signatures, contracts, and edge cases.
|
||||
---
|
||||
|
||||
## Start Here
|
||||
|
||||
<HomeDocHub />
|
||||
|
||||
- Use [API Reference](./api/index.md) when you want the canonical one-file-per-public-API docs.
|
||||
- Use [Release Notes](./changes/index.md) when you want version-scoped changes and publish-facing summaries.
|
||||
- Use [Chinese README](https://github.com/Nanaloveyuki/BitLogger/blob/main/README.md) or [English README](./README-en.md) when you want a shorter project overview first.
|
||||
1. Read [Examples](./examples/index.md) for an end-to-end path from installation to a running logger.
|
||||
2. Read [Extend](./extend/index.md) when the base console flow needs queueing, custom routing, formatting, files, or target-aware behavior.
|
||||
3. Use [API Reference](./api/index.md) for exact contracts and [Release Notes](./changes/index.md) for version history.
|
||||
|
||||
Reference in New Issue
Block a user