From 640b717c701ddd45b285685586b74b325b7876ed Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Tue, 12 May 2026 16:13:50 +0800 Subject: [PATCH] :memo: Add record and level API docs --- docs/api/field.md | 77 ++++++++++++++++++++++++++++++++ docs/api/index.md | 8 ++++ docs/api/level-enabled.md | 78 ++++++++++++++++++++++++++++++++ docs/api/level-label.md | 76 +++++++++++++++++++++++++++++++ docs/api/level-priority.md | 79 +++++++++++++++++++++++++++++++++ docs/api/record-new.md | 91 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 409 insertions(+) create mode 100644 docs/api/field.md create mode 100644 docs/api/level-enabled.md create mode 100644 docs/api/level-label.md create mode 100644 docs/api/level-priority.md create mode 100644 docs/api/record-new.md diff --git a/docs/api/field.md b/docs/api/field.md new file mode 100644 index 0000000..58816a6 --- /dev/null +++ b/docs/api/field.md @@ -0,0 +1,77 @@ +--- +name: field +group: api +category: record +update-time: 20260512 +description: Create a single structured field value from a key and value pair. +key-word: + - field + - record + - helper + - public +--- + +## Field + +Create a single `Field` value from a key and value pair. This is the basic helper used throughout logger, patch, and filter APIs whenever structured metadata needs to be attached to a record. + +### Interface + +```moonbit +pub fn field(key : String, value : String) -> Field {} +``` + +#### input + +- `key : String` - Field key name. +- `value : String` - Field value text. + +#### output + +- `Field` - Structured field value ready to be used in a record or field array. + +### Explanation + +Detailed rules explaining key parameters and behaviors + +- The helper constructs a `Field` directly from the provided key and value. +- No deduplication, normalization, or validation is applied. +- This is the standard primitive used by `fields(...)`, `append_fields(...)`, and all log write APIs that accept structured fields. +- The created field preserves the original input strings exactly. + +### How to Use + +Here are some specific examples provided. + +#### When Add One Inline Event Field + +When a single structured field is enough: +```moonbit +logger.info("accepted", fields=[field("user", "alice")]) +``` + +In this example, the event carries one explicit structured attribute. + +#### When Build Shared Context Fields + +When binding one reusable metadata field to a logger: +```moonbit +let logger = Logger::new(console_sink(), target="audit") + .bind([field("service", "billing")]) +``` + +In this example, the field becomes part of every later emitted record. + +### Error Case + +e.g.: +- If `key` is empty, the resulting field still exists and keeps the empty string key. + +- If `value` is empty, the field remains valid and simply stores an empty string value. + +### Notes + +1. Use this helper when only one field is needed and tuple syntax would be less direct. + +2. Prefer stable field keys so filters and downstream tooling remain predictable. + diff --git a/docs/api/index.md b/docs/api/index.md index 75e66d2..b4a1f83 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -52,12 +52,20 @@ BitLogger API navigation. ## Formatter and fields +- [field.md](./field.md) - [text-formatter.md](./text-formatter.md) - [text-formatter-config.md](./text-formatter-config.md) - [text-formatter-config-to-json.md](./text-formatter-config-to-json.md) - [stringify-text-formatter-config.md](./stringify-text-formatter-config.md) - [fields.md](./fields.md) +## Record and level + +- [record-new.md](./record-new.md) +- [level-priority.md](./level-priority.md) +- [level-label.md](./level-label.md) +- [level-enabled.md](./level-enabled.md) + ## Sink and file - [file-sink.md](./file-sink.md) diff --git a/docs/api/level-enabled.md b/docs/api/level-enabled.md new file mode 100644 index 0000000..ccbe120 --- /dev/null +++ b/docs/api/level-enabled.md @@ -0,0 +1,78 @@ +--- +name: level-enabled +group: api +category: level +update-time: 20260512 +description: Check whether a level passes a minimum threshold. +key-word: + - level + - enabled + - threshold + - public +--- + +## Level-enabled + +Check whether a `Level` passes a given minimum threshold. This is the core severity-gating helper used by synchronous and async logger `is_enabled(...)` checks. + +### Interface + +```moonbit +pub fn Level::enabled(self : Level, min_level : Level) -> Bool {} +``` + +#### input + +- `self : Level` - Candidate level being tested. +- `min_level : Level` - Minimum threshold the candidate must meet or exceed. + +#### output + +- `Bool` - `true` when `self.priority() >= min_level.priority()`. + +### Explanation + +Detailed rules explaining key parameters and behaviors + +- The method compares levels by their numeric priority ordering. +- Matching the threshold exactly still counts as enabled. +- This helper is the level-level primitive behind logger threshold checks. +- It is useful when threshold logic should be expressed without requiring a logger instance. + +### How to Use + +Here are some specific examples provided. + +#### When Test A Level Against A Threshold + +When code wants direct threshold logic: +```moonbit +let enabled = Level::Warn.enabled(Level::Info) +``` + +In this example, `enabled` is `true` because `Warn` is above `Info`. + +#### When Reuse Logger-style Gating In Helpers + +When a utility should mirror logger severity checks: +```moonbit +if level.enabled(Level::Debug) { + logger.info("level is active") +} +``` + +In this example, the same ordering rule used by logger APIs is applied directly. + +### Error Case + +e.g.: +- There is no failure path for valid level values. + +- If callers need to know whether a record survives filters or sinks, `enabled()` alone is not sufficient because it only covers severity gating. + +### Notes + +1. Use this helper when threshold logic should stay independent of any logger instance. + +2. Prefer `Logger::is_enabled(...)` when you already have a logger and want to respect its configured minimum level directly. + diff --git a/docs/api/level-label.md b/docs/api/level-label.md new file mode 100644 index 0000000..ad1803f --- /dev/null +++ b/docs/api/level-label.md @@ -0,0 +1,76 @@ +--- +name: level-label +group: api +category: level +update-time: 20260512 +description: Convert a level into its uppercase display label. +key-word: + - level + - label + - helper + - public +--- + +## Level-label + +Convert a `Level` into its uppercase display label. This helper is useful for formatting, diagnostics, and user-visible output that should mirror the built-in level names. + +### Interface + +```moonbit +pub fn Level::label(self : Level) -> String {} +``` + +#### input + +- `self : Level` - Level value whose display label should be returned. + +#### output + +- `String` - Uppercase label such as `TRACE`, `DEBUG`, `INFO`, `WARN`, or `ERROR`. + +### Explanation + +Detailed rules explaining key parameters and behaviors + +- The returned strings are uppercase built-in labels. +- This helper is intended for readable output and formatter logic. +- It is separate from `priority()`, which is for numeric ordering. +- The label reflects the enum variant directly and does not depend on any logger configuration. + +### How to Use + +Here are some specific examples provided. + +#### When Show A Level In Custom Output + +When a callback or formatter wants a readable label: +```moonbit +let label = Level::Warn.label() +``` + +In this example, `label` becomes `"WARN"`. + +#### When Build Simple Human-readable Diagnostics + +When direct console text should include the level name: +```moonbit +let rec = Record::new(Level::Error, "dispatch failed") +println(rec.level.label()) +``` + +In this example, the output uses the built-in uppercase severity name. + +### Error Case + +e.g.: +- There is no failure path for valid `Level` values. + +- If code needs numeric ordering rather than display text, `label()` is the wrong helper. + +### Notes + +1. Use this helper for presentation and diagnostics. + +2. Prefer `priority()` or `enabled()` for threshold logic. + diff --git a/docs/api/level-priority.md b/docs/api/level-priority.md new file mode 100644 index 0000000..22cbe17 --- /dev/null +++ b/docs/api/level-priority.md @@ -0,0 +1,79 @@ +--- +name: level-priority +group: api +category: level +update-time: 20260512 +description: Convert a level into its numeric severity priority. +key-word: + - level + - priority + - helper + - public +--- + +## Level-priority + +Convert a `Level` into its numeric severity priority. This helper provides the ordering used by threshold checks and level comparisons. + +### Interface + +```moonbit +pub fn Level::priority(self : Level) -> Int {} +``` + +#### input + +- `self : Level` - Level value whose numeric severity should be read. + +#### output + +- `Int` - Numeric severity where larger numbers represent more severe levels. + +### Explanation + +Detailed rules explaining key parameters and behaviors + +- The built-in priorities are `Trace=10`, `Debug=20`, `Info=30`, `Warn=40`, and `Error=50`. +- These values define the severity ordering used by `Level::enabled(...)` and helpers such as `level_at_least(...)`. +- Callers should treat the mapping as a stable ordering mechanism rather than as user-facing display text. +- Higher priority means higher severity. + +### How to Use + +Here are some specific examples provided. + +#### When Compare Levels Explicitly + +When code should compare severities numerically: +```moonbit +if Level::Warn.priority() > Level::Info.priority() { + logger.info("warn is more severe than info") +} +``` + +In this example, the numeric mapping drives direct comparison logic. + +#### When Build Custom Threshold Logic + +When a helper wants to reproduce built-in gating: +```moonbit +fn keep(level : Level, min_level : Level) -> Bool { + level.priority() >= min_level.priority() +} +``` + +In this example, the same ordering contract used by the logger API is applied directly. + +### Error Case + +e.g.: +- There is no failure path for valid `Level` values. + +- If code depends on the exact numbers instead of the ordering, future refactoring would be more brittle than necessary. + +### Notes + +1. Prefer `Level::enabled(...)` when you need threshold semantics rather than raw numeric comparison. + +2. `priority()` is primarily an ordering helper, not a display helper. + diff --git a/docs/api/record-new.md b/docs/api/record-new.md new file mode 100644 index 0000000..5a8525a --- /dev/null +++ b/docs/api/record-new.md @@ -0,0 +1,91 @@ +--- +name: record-new +group: api +category: record +update-time: 20260512 +description: Construct a log record explicitly from level, message, timestamp, target, and fields. +key-word: + - record + - constructor + - logging + - public +--- + +## Record-new + +Construct a `Record` explicitly from its core fields. This is the low-level record constructor used beneath the logger APIs and is useful when records need to be created directly for formatting, filtering, sink callbacks, or tests. + +### Interface + +```moonbit +pub fn Record::new( + level : Level, + message : String, + timestamp_ms~ : UInt64 = 0UL, + target~ : String = "", + fields~ : Array[Field] = [], +) -> Record {} +``` + +#### input + +- `level : Level` - Severity level stored in the record. +- `message : String` - Log message text. +- `timestamp_ms : UInt64` - Timestamp in milliseconds, or `0UL` when omitted. +- `target : String` - Target label for the record. +- `fields : Array[Field]` - Structured field list attached to the record. + +#### output + +- `Record` - Structured log record value. + +### Explanation + +Detailed rules explaining key parameters and behaviors + +- The constructor stores the provided values directly without extra processing. +- `timestamp_ms` defaults to `0UL`, which is the same no-timestamp convention used by loggers when timestamping is disabled. +- `target` and `fields` are optional and may be left empty. +- This API is useful when records need to be created outside the normal `Logger::log(...)` flow. + +### How to Use + +Here are some specific examples provided. + +#### When Create A Record For Formatter Tests + +When a formatting path should be exercised directly: +```moonbit +let rec = Record::new( + Level::Info, + "service started", + target="app", + fields=[field("region", "cn")], +) +``` + +In this example, the record can be passed straight to `format_text(...)` or `format_json(...)`. + +#### When Feed A Callback Or Predicate Manually + +When tests or adapters work with records directly: +```moonbit +let rec = Record::new(Level::Warn, "retry scheduled") +let keep = level_at_least(Level::Info)(rec) +``` + +In this example, the record exists independently of any logger instance. + +### Error Case + +e.g.: +- If `target` is empty, the record remains valid and simply carries no target label. + +- If `fields` is empty, the record still works normally with formatters, sinks, and predicates. + +### Notes + +1. Prefer logger write APIs for normal application logging; use `Record::new(...)` when direct record construction is specifically needed. + +2. This constructor is especially useful in tests and low-level integrations. +