mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
Compare commits
5 Commits
bcfb35d7ae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b05ee0c94 | |||
| f9227890bd | |||
| 06db039a03 | |||
| 5ed02ec563 | |||
| a58a0747bb |
@@ -67,9 +67,15 @@ jobs:
|
||||
moon check --deny-warn
|
||||
moon test --deny-warn
|
||||
moon check --target native --deny-warn
|
||||
moon check --target wasm --deny-warn
|
||||
moon check --target wasm-gc --deny-warn
|
||||
moon check --target js --deny-warn
|
||||
|
||||
- name: Test wasm path
|
||||
shell: bash
|
||||
run: |
|
||||
moon test --target wasm --deny-warn
|
||||
|
||||
- name: Run sync example
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -105,15 +111,17 @@ jobs:
|
||||
run: |
|
||||
moon update
|
||||
|
||||
- name: Check async package on wasm-gc and js
|
||||
- name: Check async package on wasm, wasm-gc and js
|
||||
shell: bash
|
||||
run: |
|
||||
moon check src-async --target wasm --deny-warn
|
||||
moon check src-async --target wasm-gc --deny-warn
|
||||
moon check src-async --target js --deny-warn
|
||||
|
||||
- name: Test async package on wasm-gc and js
|
||||
- name: Test async package on wasm, wasm-gc and js
|
||||
shell: bash
|
||||
run: |
|
||||
moon test src-async --target wasm --deny-warn
|
||||
moon test src-async --target wasm-gc --deny-warn
|
||||
moon test src-async --target js --deny-warn
|
||||
|
||||
|
||||
@@ -26,5 +26,8 @@ docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
docs/.vitepress/generated/
|
||||
|
||||
# Benchmark package has no public API; moon info emits an empty snapshot.
|
||||
benchmarks/pkg.generated.mbti
|
||||
|
||||
# vibecoding
|
||||
AGENTS/
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Agent Notes
|
||||
|
||||
## Version Releases
|
||||
|
||||
Do not update package versions and installation snippets by hand. Run the
|
||||
repository release helper from the root instead:
|
||||
|
||||
```powershell
|
||||
.\scripts\update-version.ps1 <x.x.x>
|
||||
```
|
||||
|
||||
For a normal patch release, the script updates `moon.mod`, all current
|
||||
installation examples, creates the next `docs/changes/<release>(<version>).md`,
|
||||
and adds it to `docs/changes/index.md`. Review and replace any generated `TODO`
|
||||
entries in the release note before committing.
|
||||
|
||||
For a major or minor package-version change, supply the public release number
|
||||
explicitly so it is not inferred:
|
||||
|
||||
```powershell
|
||||
.\scripts\update-version.ps1 <x.x.x> -ReleaseVersion <x.x.x>
|
||||
```
|
||||
|
||||
Use `-WhatIf` to preview the target files. Pass `-Change`, `-Verification`,
|
||||
and `-Note` to populate release-note sections directly. Validate the edited
|
||||
repository before committing with a `gitmoji + short desc` message.
|
||||
@@ -17,7 +17,7 @@ BitLogger 的文档按使用路径组织:先完成一个可运行的日志输
|
||||
```bash
|
||||
moon new log-demo
|
||||
cd log-demo
|
||||
moon add Nanaloveyuki/BitLogger@0.7.1
|
||||
moon add Nanaloveyuki/BitLogger@0.7.3
|
||||
```
|
||||
|
||||
### 2. 写入第一条结构化日志
|
||||
@@ -54,9 +54,8 @@ fn main {
|
||||
|
||||
## 支持情况
|
||||
|
||||
- `BitLogger` 当前在 CI 中检查/验证的目标是 `native`、`js`、`wasm-gc`
|
||||
- `bitlogger_async` 当前在 CI 中检查 `native`、`js`、`wasm-gc`,测试覆盖 `native`、`js`、`wasm-gc`
|
||||
- `wasm` 目标在源码 `moon.pkg` 中保留声明,但当前未纳入 CI 验证口径
|
||||
- `BitLogger` 当前在 CI 中检查/验证的目标是 `native`、`wasm`、`js`、`wasm-gc`
|
||||
- `bitlogger_async` 当前在 CI 中检查和测试 `native`、`wasm`、`js`、`wasm-gc`
|
||||
- `llvm` 目前按实验性目标处理,当前环境未完成验证
|
||||
- 文件输出是 native 能力;跨端代码里建议先判断 `native_files_supported()`
|
||||
- `src-async` 可用,但示例 `examples/async_basic` 目前仍按 native 入口提供
|
||||
@@ -69,6 +68,7 @@ fn main {
|
||||
- 配置驱动构建:`build_logger(...)`、`build_async_logger(...)`
|
||||
- 组合能力:queue、filter、patch、fanout、split、callback
|
||||
- 异步日志:独立 `src-async` package
|
||||
- Trace context:标准 `trace_id`、`span_id`、`trace_flags`、`trace_state` 字段绑定
|
||||
|
||||
## 文档
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# BitLogger Benchmarks
|
||||
|
||||
Run the native baseline from the repository root:
|
||||
|
||||
```powershell
|
||||
moon bench benchmarks --target native --release --deny-warn
|
||||
```
|
||||
|
||||
The suite measures callback emission, trace-context field binding, synchronous queue enqueue-and-flush, and native file writes. It intentionally has no pass/fail threshold: compare results only on the same machine, toolchain, target, and power profile.
|
||||
|
||||
Async throughput remains lifecycle-sensitive and is exercised by the native integration test rather than mixed into these synchronous microbenchmarks. Record its end-to-end measurements separately when profiling an application workload.
|
||||
@@ -0,0 +1,61 @@
|
||||
///|
|
||||
let benchmark_record : @log.Record = @log.Record::new(
|
||||
@log.Level::Info,
|
||||
"benchmark record",
|
||||
target="bench",
|
||||
fields=[@log.field("component", "bitlogger")],
|
||||
)
|
||||
|
||||
///|
|
||||
test "bench callback logger emission" (it : @bench.T) {
|
||||
let writes : Ref[Int] = Ref(0)
|
||||
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 }))
|
||||
it.bench(fn() {
|
||||
logger.info("benchmark record", fields=[
|
||||
@log.field("component", "bitlogger"),
|
||||
])
|
||||
it.keep(writes.val)
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
test "bench trace context field binding" (it : @bench.T) {
|
||||
let writes : Ref[Int] = Ref(0)
|
||||
let context = @log.trace_context("trace-bench", "span-bench")
|
||||
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 })).with_trace_context(
|
||||
context,
|
||||
)
|
||||
it.bench(fn() {
|
||||
logger.info("benchmark record", fields=[@log.field("event", "request")])
|
||||
it.keep(writes.val)
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
test "bench queued logger enqueue and flush" (it : @bench.T) {
|
||||
let writes : Ref[Int] = Ref(0)
|
||||
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 })).with_queue(
|
||||
max_pending=8,
|
||||
)
|
||||
it.bench(fn() {
|
||||
logger.info("benchmark record")
|
||||
it.keep(logger.sink.flush())
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
test "bench native file sink write" (it : @bench.T) {
|
||||
let sink = @log.file_sink(
|
||||
"logs/bitlogger-benchmark.log",
|
||||
append=false,
|
||||
auto_flush=false,
|
||||
formatter=fn(rec) { rec.message },
|
||||
)
|
||||
if sink.is_available() {
|
||||
it.bench(fn() {
|
||||
sink.write(benchmark_record)
|
||||
it.keep(sink.write_failures())
|
||||
})
|
||||
ignore(sink.close())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @log,
|
||||
"moonbitlang/core/bench",
|
||||
}
|
||||
@@ -121,6 +121,7 @@ function buildExtendSidebar(): DefaultTheme.SidebarItem[] {
|
||||
{ text: 'Sink Composition', link: '/extend/composition' },
|
||||
{ text: 'Text Formatting', link: '/extend/formatting' },
|
||||
{ text: 'Target Boundaries', link: '/extend/targets' },
|
||||
{ text: 'Trace Context', link: '/extend/observability' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -141,6 +142,7 @@ function buildChineseExtendSidebar(): DefaultTheme.SidebarItem[] {
|
||||
{ text: 'Sink 组合', link: '/zh/extend/composition' },
|
||||
{ text: '文本格式与样式', link: '/zh/extend/formatting' },
|
||||
{ text: '目标平台边界', link: '/zh/extend/targets' },
|
||||
{ text: 'Trace Context', link: '/zh/extend/observability' },
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -18,7 +18,7 @@ The documentation is organized around complete usage flows. Start with an execut
|
||||
```bash
|
||||
moon new log-demo
|
||||
cd log-demo
|
||||
moon add Nanaloveyuki/BitLogger@0.7.1
|
||||
moon add Nanaloveyuki/BitLogger@0.7.3
|
||||
```
|
||||
|
||||
### 2. Import And Log
|
||||
@@ -44,7 +44,7 @@ Continue with the [Examples guide](./examples/index.md): console fields, file ro
|
||||
|
||||
## Support Status
|
||||
|
||||
- Current local verification covers `native`, `js`, `wasm`, and `wasm-gc` for the main `src` package and `src-async`
|
||||
- CI checks and tests `native`, `wasm`, `js`, and `wasm-gc` for the main `src` package and `src-async`
|
||||
- `llvm` is still treated as experimental in the current release context and was not locally re-verified in the current environment
|
||||
- The source packages still declare `wasm` support alongside `native`, `llvm`, `js`, and `wasm-gc`; see [`target-verification.md`](./api/target-verification.md) for the current release-facing verification boundary
|
||||
- File output is a native capability; check `native_files_supported()` in cross-target code
|
||||
@@ -58,6 +58,7 @@ Continue with the [Examples guide](./examples/index.md): console fields, file ro
|
||||
- Config-based builders: `build_logger(...)` and `build_async_logger(...)`
|
||||
- Composition helpers: queue, filter, patch, fanout, split, callback
|
||||
- Separate async package under `src-async`
|
||||
- Trace-context binding through standard `trace_id`, `span_id`, `trace_flags`, and `trace_state` fields
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ key-word:
|
||||
|
||||
## Async-logger-build-config-to-json
|
||||
|
||||
Convert `AsyncLoggerBuildConfig` into a `JsonValue`. This helper exports both the base synchronous logger config and the async runtime config as one structured payload.
|
||||
Convert `AsyncLoggerBuildConfig` into a `Json`. This helper exports both the base synchronous logger config and the async runtime config as one structured payload.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn async_logger_build_config_to_json(
|
||||
config : AsyncLoggerBuildConfig,
|
||||
) -> @json_parser.JsonValue {}
|
||||
) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -29,7 +29,7 @@ pub fn async_logger_build_config_to_json(
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the full async build config.
|
||||
- `Json` - Structured JSON representation of the full async build config.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Async-logger-config-to-json
|
||||
|
||||
Convert a typed `AsyncLoggerConfig` into a `JsonValue`. This helper exports async queue capacity, overflow policy, batch sizing, linger timing, and flush behavior in a structured form.
|
||||
Convert a typed `AsyncLoggerConfig` into a `Json`. This helper exports async queue capacity, overflow policy, batch sizing, linger timing, and flush behavior in a structured form.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> @json_parser.JsonValue {}
|
||||
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> @json_parser.J
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the async config.
|
||||
- `Json` - Structured JSON representation of the async config.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Async-logger-state-to-json
|
||||
|
||||
Convert `AsyncLoggerState` into a `JsonValue`. This helper is the structured export path for async logger runtime snapshots or manually constructed state values when callers want machine-readable diagnostics instead of a plain string.
|
||||
Convert `AsyncLoggerState` into a `Json`. This helper is the structured export path for async logger runtime snapshots or manually constructed state values when callers want machine-readable diagnostics instead of a plain string.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.JsonValue {}
|
||||
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the async logger snapshot.
|
||||
- `Json` - Structured JSON representation of the async logger snapshot.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -62,7 +62,7 @@ In this example, callers receive a structured value that can be composed into la
|
||||
When another serializer or pipeline expects a JSON value:
|
||||
```moonbit
|
||||
let payload = async_logger_state_to_json(logger.state())
|
||||
println(@json_parser.stringify(payload))
|
||||
println(payload.stringify())
|
||||
```
|
||||
|
||||
In this example, the helper stays useful even outside the built-in stringify wrapper.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: async-logger-with-trace-context
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260719
|
||||
description: Bind TraceContext fields before records enter an AsyncLogger queue.
|
||||
key-word:
|
||||
- async
|
||||
- trace
|
||||
- context
|
||||
---
|
||||
|
||||
## Async-logger-with-trace-context
|
||||
|
||||
```moonbit
|
||||
let contextual = logger.with_trace_context(
|
||||
@bitlogger.trace_context("trace-1", "span-1"),
|
||||
)
|
||||
```
|
||||
|
||||
The helper stores the mapped fields on the returned async logger and preserves its queue, lifecycle, target, and threshold state. Context is added before records are enqueued.
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Async-runtime-state-to-json
|
||||
|
||||
Convert `AsyncRuntimeState` into a `JsonValue`. This helper exports the async runtime mode and background worker capability in a structured form.
|
||||
Convert `AsyncRuntimeState` into a `Json`. This helper exports the async runtime mode and background worker capability in a structured form.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> @json_parser.JsonValue {}
|
||||
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> @json_parser.Js
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the runtime state.
|
||||
- `Json` - Structured JSON representation of the runtime state.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## File-rotation-config-to-json
|
||||
|
||||
Convert `FileRotation` into a `JsonValue`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
Convert `FileRotation` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn file_rotation_config_to_json(config : FileRotation) -> @json_parser.JsonValue {}
|
||||
pub fn file_rotation_config_to_json(config : FileRotation) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn file_rotation_config_to_json(config : FileRotation) -> @json_parser.JsonV
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the rotation policy.
|
||||
- `Json` - Structured JSON representation of the rotation policy.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -82,7 +82,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when downstream code expects `JsonValue` rather than a typed `FileRotation` value.
|
||||
1. Use this helper when downstream code expects `Json` rather than a typed `FileRotation` value.
|
||||
|
||||
2. Pair it with `file_rotation(...)` when building rotation policy in code before export.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## File-sink-policy-to-json
|
||||
|
||||
Convert `FileSinkPolicy` into a `JsonValue`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
Convert `FileSinkPolicy` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> @json_parser.JsonValue {}
|
||||
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> @json_parser.JsonVal
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the file policy.
|
||||
- `Json` - Structured JSON representation of the file policy.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -73,7 +73,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when downstream code expects `JsonValue` rather than text.
|
||||
1. Use this helper when downstream code expects `Json` rather than text.
|
||||
|
||||
2. It pairs naturally with `FileSink::policy()`, `RuntimeSink::file_policy()`, and related default-policy accessors.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## File-sink-state-to-json
|
||||
|
||||
Convert `FileSinkState` into a `JsonValue`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
Convert `FileSinkState` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {}
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the file sink state.
|
||||
- `Json` - Structured JSON representation of the file sink state.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -72,7 +72,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when diagnostics consumers expect `JsonValue`.
|
||||
1. Use this helper when diagnostics consumers expect `Json`.
|
||||
|
||||
2. It pairs naturally with `FileSink::state()`, `RuntimeSink::file_state()`, and `ConfiguredLogger::file_state()`.
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ BitLogger API navigation.
|
||||
- [logger-with-timestamp.md](./logger-with-timestamp.md)
|
||||
- [logger-with-context-fields.md](./logger-with-context-fields.md)
|
||||
- [logger-bind.md](./logger-bind.md)
|
||||
- [logger-with-trace-context.md](./logger-with-trace-context.md)
|
||||
- [logger-with-filter.md](./logger-with-filter.md)
|
||||
- [logger-with-patch.md](./logger-with-patch.md)
|
||||
- [logger-with-queue.md](./logger-with-queue.md)
|
||||
@@ -99,6 +100,10 @@ BitLogger API navigation.
|
||||
- [fields.md](./fields.md)
|
||||
- [text-formatter-config-type.md](./text-formatter-config-type.md)
|
||||
|
||||
## Observability
|
||||
|
||||
- [trace-context.md](./trace-context.md)
|
||||
|
||||
## Record and level
|
||||
|
||||
- [record.md](./record.md)
|
||||
@@ -266,6 +271,7 @@ BitLogger API navigation.
|
||||
- [async-logger-with-min-level.md](./async-logger-with-min-level.md)
|
||||
- [async-logger-with-timestamp.md](./async-logger-with-timestamp.md)
|
||||
- [async-logger-with-context-fields.md](./async-logger-with-context-fields.md)
|
||||
- [async-logger-with-trace-context.md](./async-logger-with-trace-context.md)
|
||||
- [async-logger-with-filter.md](./async-logger-with-filter.md)
|
||||
- [async-logger-with-patch.md](./async-logger-with-patch.md)
|
||||
|
||||
@@ -322,6 +328,7 @@ BitLogger API navigation.
|
||||
- [library-logger-child.md](./library-logger-child.md)
|
||||
- [library-logger-with-context-fields.md](./library-logger-with-context-fields.md)
|
||||
- [library-logger-bind.md](./library-logger-bind.md)
|
||||
- [library-logger-with-trace-context.md](./library-logger-with-trace-context.md)
|
||||
- [library-logger-is-enabled.md](./library-logger-is-enabled.md)
|
||||
- [library-logger-log.md](./library-logger-log.md)
|
||||
- [library-logger-info.md](./library-logger-info.md)
|
||||
@@ -341,6 +348,7 @@ BitLogger API navigation.
|
||||
- [library-async-logger-child.md](./library-async-logger-child.md)
|
||||
- [library-async-logger-with-context-fields.md](./library-async-logger-with-context-fields.md)
|
||||
- [library-async-logger-bind.md](./library-async-logger-bind.md)
|
||||
- [library-async-logger-with-trace-context.md](./library-async-logger-with-trace-context.md)
|
||||
- [library-async-logger-is-enabled.md](./library-async-logger-is-enabled.md)
|
||||
- [library-async-logger-log.md](./library-async-logger-log.md)
|
||||
- [library-async-logger-info.md](./library-async-logger-info.md)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: library-async-logger-with-trace-context
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260719
|
||||
description: Bind TraceContext fields through the restricted LibraryAsyncLogger facade.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
- trace
|
||||
---
|
||||
|
||||
## Library-async-logger-with-trace-context
|
||||
|
||||
`LibraryAsyncLogger::with_trace_context(...)` returns a derived library facade with `trace_id`, `span_id`, `trace_flags`, and optional `trace_state` attached to each later record. It does not start a worker or change lifecycle state.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: library-logger-with-trace-context
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260719
|
||||
description: Bind TraceContext fields through the restricted LibraryLogger facade.
|
||||
key-word:
|
||||
- library
|
||||
- trace
|
||||
- context
|
||||
---
|
||||
|
||||
## Library-logger-with-trace-context
|
||||
|
||||
```moonbit
|
||||
let logger = LibraryLogger::new(console_sink())
|
||||
.with_trace_context(trace_context("trace-1", "span-1"))
|
||||
```
|
||||
|
||||
The derived facade preserves its library-facing API while wrapping the sink with context fields. Use `to_logger()` only when broader composition is required.
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Logger-config-to-json
|
||||
|
||||
Convert a typed `LoggerConfig` into a `JsonValue`. This helper is the structured export path when config should be persisted, inspected, or embedded into larger JSON payloads.
|
||||
Convert a typed `LoggerConfig` into a `Json`. This helper is the structured export path when config should be persisted, inspected, or embedded into larger JSON payloads.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {}
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {}
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - JSON representation of the logger config.
|
||||
- `Json` - JSON representation of the logger config.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -73,5 +73,5 @@ e.g.:
|
||||
|
||||
1. Use this helper when you need a reusable JSON value rather than a final JSON string.
|
||||
|
||||
2. Use `stringify_logger_config(...)` when the next consumer expects JSON text instead of `JsonValue`.
|
||||
2. Use `stringify_logger_config(...)` when the next consumer expects JSON text instead of `Json`.
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: logger-with-trace-context
|
||||
group: api
|
||||
category: logger
|
||||
update-time: 20260719
|
||||
description: Bind a TraceContext to every record from a synchronous Logger.
|
||||
key-word:
|
||||
- logger
|
||||
- trace
|
||||
- context
|
||||
---
|
||||
|
||||
## Logger-with-trace-context
|
||||
|
||||
```moonbit
|
||||
pub fn[S] Logger::with_trace_context(
|
||||
self : Logger[S],
|
||||
context : TraceContext,
|
||||
) -> Logger[ContextSink[S]]
|
||||
```
|
||||
|
||||
Returns a derived logger that prepends the context fields to every record. The original logger is unchanged. This is equivalent to `with_context_fields(context.as_fields())`, while making trace intent explicit.
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Queue-config-to-json
|
||||
|
||||
Convert a typed `QueueConfig` into a `JsonValue`. This helper is the structured export path for synchronous queue wrapper configuration when callers want machine-readable config output.
|
||||
Convert a typed `QueueConfig` into a `Json`. This helper is the structured export path for synchronous queue wrapper configuration when callers want machine-readable config output.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {}
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {}
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the queue config.
|
||||
- `Json` - Structured JSON representation of the queue config.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -74,5 +74,5 @@ e.g.:
|
||||
|
||||
1. Use this helper when you need a reusable JSON value rather than a final JSON string.
|
||||
|
||||
2. Use `stringify_queue_config(...)` when the next consumer expects text instead of `JsonValue`.
|
||||
2. Use `stringify_queue_config(...)` when the next consumer expects text instead of `Json`.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Runtime-file-state-to-json
|
||||
|
||||
Convert `RuntimeFileState` into a `JsonValue`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
Convert `RuntimeFileState` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> @json_parser.JsonValue {}
|
||||
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn runtime_file_state_to_json(state : RuntimeFileState) -> @json_parser.Json
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the runtime file state.
|
||||
- `Json` - Structured JSON representation of the runtime file state.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Sink-config-to-json
|
||||
|
||||
Convert `SinkConfig` into a `JsonValue`. This helper is used directly for sink export and indirectly when exporting `LoggerConfig`.
|
||||
Convert `SinkConfig` into a `Json`. This helper is used directly for sink export and indirectly when exporting `LoggerConfig`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {}
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {}
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - JSON representation of the sink configuration.
|
||||
- `Json` - JSON representation of the sink configuration.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON.
|
||||
- `pretty=true` returns indented JSON for human inspection.
|
||||
- This helper is built on top of `async_logger_build_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured async build-config export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured async build-config export helper.
|
||||
- The output keeps `logger` and `async_config` as separate sections, matching supported parser input.
|
||||
- The serialized `logger` section preserves the full `LoggerConfig` shape, even though `build_async_text_logger(...)` later consumes only the selected text-oriented subset of that logger config.
|
||||
- That includes preserving whatever `logger.sink.kind` text is present in the config, even though the later text-specific builder path still ignores that sink-kind branch and constructs `FormattedConsoleSink` from `logger.sink.text_formatter`.
|
||||
@@ -80,7 +80,7 @@ In this example, compact JSON is returned without extra formatting.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If callers need a `JsonValue` for further composition, they should use `async_logger_build_config_to_json(...)` instead.
|
||||
- If callers need a `Json` for further composition, they should use `async_logger_build_config_to_json(...)` instead.
|
||||
|
||||
- If only one layer of config is required, this helper may be broader than necessary.
|
||||
|
||||
@@ -96,7 +96,7 @@ e.g.:
|
||||
|
||||
4. In particular, serialized `logger.sink.kind` text is descriptive config data, not a guarantee that the text-specific builder path will branch on that sink kind later.
|
||||
|
||||
5. Use `async_logger_build_config_to_json(...)` when the next consumer still needs a `JsonValue` for composition before final stringification.
|
||||
5. Use `async_logger_build_config_to_json(...)` when the next consumer still needs a `Json` for composition before final stringification.
|
||||
|
||||
6. After parsing, the same text can also feed the application or library facade builders; string export preserves one shared build-config shape, not a direct-builder-only route.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON suitable for transport and snapshots.
|
||||
- `pretty=true` returns indented JSON for humans.
|
||||
- This helper is built on top of `async_logger_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured async-config export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured async-config export helper.
|
||||
- The exported text follows the supported async config schema rather than internal queue implementation details.
|
||||
- Canonical policy labels such as `DropNewest` and `Never` are emitted even though the parser also accepts aliases like `DropLatest` and `None`.
|
||||
|
||||
@@ -68,7 +68,7 @@ In this example, compact JSON is returned without extra formatting.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If callers need a `JsonValue` for composition, they should use `async_logger_config_to_json(...)` instead.
|
||||
- If callers need a `Json` for composition, they should use `async_logger_config_to_json(...)` instead.
|
||||
|
||||
- If invalid constructor inputs were normalized earlier, the resulting text contains the normalized config values.
|
||||
|
||||
@@ -78,5 +78,5 @@ e.g.:
|
||||
|
||||
2. If negative `max_pending` semantics matter, remember that the serialized text preserves the config value while runtime queue creation later clamps the queue limit to `0`.
|
||||
|
||||
3. Use `async_logger_config_to_json(...)` when the next consumer still needs a `JsonValue` for composition before final stringification.
|
||||
3. Use `async_logger_config_to_json(...)` when the next consumer still needs a `Json` for composition before final stringification.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON.
|
||||
- `pretty=true` returns indented JSON suitable for human diagnostics.
|
||||
- This helper is built on top of `async_logger_state_to_json(...)`.
|
||||
- Internally it stringifies the same JSON snapshot shape returned by the public export helper, using `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`.
|
||||
- Internally it stringifies the same JSON snapshot shape returned by the public export helper, using `value.stringify(...)` or `value.stringify(indent=2)`.
|
||||
- The compact form matches snapshots such as `{"runtime":{"mode":"native_worker","background_worker":true},"phase":"ready","pending_count":0,...}`.
|
||||
- String output is convenient for logs, CLI output, and startup diagnostics.
|
||||
- The serializer does not care whether the input came from a live logger read or a synthetic `AsyncLoggerState::new(...)` call.
|
||||
@@ -83,7 +83,7 @@ e.g.:
|
||||
|
||||
2. The compact output shape is already locked by the async runtime snapshot tests, so it is suitable for stable diagnostics and assertions.
|
||||
|
||||
3. Use `async_logger_state_to_json(...)` when the next consumer still needs a `JsonValue` for composition before final stringification.
|
||||
3. Use `async_logger_state_to_json(...)` when the next consumer still needs a `Json` for composition before final stringification.
|
||||
|
||||
4. Pair it with `AsyncLogger::state()` for current logger diagnostics, or with `AsyncLoggerState::new(...)` when exporting a manual snapshot built by tests or adapters.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON.
|
||||
- `pretty=true` returns indented JSON for human diagnostics.
|
||||
- This helper is built on top of `async_runtime_state_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured runtime-state export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured runtime-state export helper.
|
||||
- The compact form matches the tested snapshot shape such as `{"mode":"native_worker","background_worker":true}`.
|
||||
- It is well-suited for startup banners, support reports, and target capability logs.
|
||||
|
||||
@@ -79,5 +79,5 @@ e.g.:
|
||||
|
||||
2. This helper is the direct text form of the same two-field runtime snapshot exported by `async_runtime_state_to_json(...)`.
|
||||
|
||||
3. Use `async_runtime_state_to_json(...)` when the next consumer still needs a `JsonValue` for composition before final stringification.
|
||||
3. Use `async_runtime_state_to_json(...)` when the next consumer still needs a `Json` for composition before final stringification.
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ In this example, compact JSON is produced without extra formatting logic.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If callers need a `JsonValue` for composition rather than text, `file_sink_policy_to_json(...)` is the better API.
|
||||
- If callers need a `Json` for composition rather than text, `file_sink_policy_to_json(...)` is the better API.
|
||||
|
||||
- If rotation is disabled, the output still includes `rotation` as `null`.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` produces compact JSON.
|
||||
- `pretty=true` produces indented human-readable JSON.
|
||||
- This helper builds on top of `logger_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured logger-config export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured logger-config export helper.
|
||||
- Output is stable and suited for roundtrip config workflows.
|
||||
|
||||
### How to Use
|
||||
@@ -71,7 +71,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when the next consumer expects JSON text instead of `JsonValue`.
|
||||
1. Use this helper when the next consumer expects JSON text instead of `Json`.
|
||||
|
||||
2. Use `logger_config_to_json(...)` when you still need to embed the config inside a larger JSON object before final stringification.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON suitable for logs and snapshots.
|
||||
- `pretty=true` returns indented JSON for human inspection.
|
||||
- This helper is built on top of `queue_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured export helper.
|
||||
- The resulting text follows the same queue schema accepted by config parsing flows.
|
||||
|
||||
### How to Use
|
||||
@@ -67,13 +67,13 @@ In this example, compact JSON is returned without extra formatting.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If callers need a `JsonValue` for further composition, this API is the wrong layer and `queue_config_to_json(...)` should be used instead.
|
||||
- If callers need a `Json` for further composition, this API is the wrong layer and `queue_config_to_json(...)` should be used instead.
|
||||
|
||||
- If queue policy is too aggressive for workload burst size, serialization still succeeds because this helper only exports config.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when the next consumer expects JSON text instead of `JsonValue`.
|
||||
1. Use this helper when the next consumer expects JSON text instead of `Json`.
|
||||
|
||||
2. Use `queue_config_to_json(...)` when you still need to embed the queue config inside a larger JSON object before final stringification.
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ In this example, compact JSON is returned without extra formatting logic.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If callers need a `JsonValue` rather than text, `runtime_file_state_to_json(...)` is the better API.
|
||||
- If callers need a `Json` rather than text, `runtime_file_state_to_json(...)` is the better API.
|
||||
|
||||
- If queue wrapping is not relevant, `stringify_file_sink_state(...)` may be the simpler API.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` gives compact JSON.
|
||||
- `pretty=true` gives indented output.
|
||||
- This helper builds on top of `sink_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured sink export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured sink export helper.
|
||||
- It is useful when examples or generated docs want to show only sink-specific config.
|
||||
|
||||
### How to Use
|
||||
@@ -71,7 +71,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when the next consumer expects JSON text instead of `JsonValue`.
|
||||
1. Use this helper when the next consumer expects JSON text instead of `Json`.
|
||||
|
||||
2. Use `sink_config_to_json(...)` when you still need to embed the sink config inside a larger JSON object before final stringification.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=false` returns compact JSON.
|
||||
- `pretty=true` returns indented JSON for humans.
|
||||
- This helper is built on top of `text_formatter_config_to_json(...)`.
|
||||
- Internally it serializes the `JsonValue` result with `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`, so the text form stays aligned with the structured formatter export helper.
|
||||
- Internally it serializes the `Json` result with `value.stringify(...)` or `value.stringify(indent=2)`, so the text form stays aligned with the structured formatter export helper.
|
||||
- The output preserves the supported formatter config schema instead of any runtime-only formatter instance details.
|
||||
|
||||
### How to Use
|
||||
@@ -76,7 +76,7 @@ e.g.:
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when the next consumer expects JSON text instead of `JsonValue`.
|
||||
1. Use this helper when the next consumer expects JSON text instead of `Json`.
|
||||
|
||||
2. Use `text_formatter_config_to_json(...)` when you still need to embed formatter config inside a larger JSON object before final stringification.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ key-word:
|
||||
|
||||
## Text-formatter-config-to-json
|
||||
|
||||
Convert a typed `TextFormatterConfig` into a `JsonValue`. This helper exports formatter toggles, separators, color settings, markup behavior, and optional style tags in a machine-readable form.
|
||||
Convert a typed `TextFormatterConfig` into a `Json`. This helper exports formatter toggles, separators, color settings, markup behavior, and optional style tags in a machine-readable form.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn text_formatter_config_to_json(config : TextFormatterConfig) -> @json_parser.JsonValue {}
|
||||
pub fn text_formatter_config_to_json(config : TextFormatterConfig) -> Json {}
|
||||
```
|
||||
|
||||
#### input
|
||||
@@ -27,7 +27,7 @@ pub fn text_formatter_config_to_json(config : TextFormatterConfig) -> @json_pars
|
||||
|
||||
#### output
|
||||
|
||||
- `JsonValue` - Structured JSON representation of the formatter config.
|
||||
- `Json` - Structured JSON representation of the formatter config.
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: trace-context
|
||||
group: api
|
||||
category: observability
|
||||
update-time: 20260719
|
||||
description: Represent and bind distributed trace metadata using stable structured-log fields.
|
||||
key-word:
|
||||
- trace
|
||||
- context
|
||||
- observability
|
||||
---
|
||||
|
||||
## Trace-context
|
||||
|
||||
`TraceContext` carries the standard log fields used to correlate records with distributed traces: `trace_id`, `span_id`, `trace_flags`, and optional `trace_state`.
|
||||
|
||||
```moonbit
|
||||
let context = trace_context(
|
||||
"4bf92f3577b34da6a3ce929d0e0e4736",
|
||||
"00f067aa0ba902b7",
|
||||
trace_state="vendor=value",
|
||||
)
|
||||
```
|
||||
|
||||
Use `context.as_fields()` when integrating with a custom carrier, or pass the value to a logger's `with_trace_context(...)` helper. BitLogger does not parse HTTP headers or export OTLP data; an application-owned tracing library remains responsible for propagation and sampling.
|
||||
@@ -0,0 +1,21 @@
|
||||
## BitLogger Update Changes
|
||||
|
||||
version 1.1.2(0.7.2)
|
||||
|
||||
### JSON
|
||||
|
||||
- refactor: replace `maria/json_parser` with the official `moonbitlang/core/json` implementation
|
||||
- refactor: preserve existing serialized JSON field shapes while JSON conversion APIs now return the built-in `Json` type
|
||||
|
||||
### Docs
|
||||
|
||||
- docs: update installation snippets to `Nanaloveyuki/BitLogger@0.7.2`
|
||||
- docs: add this 1.1.2(0.7.2) release entry to the change index
|
||||
|
||||
### Verification
|
||||
|
||||
- verify: pass formatting, warning-denied checks, and default, native, JavaScript, WebAssembly, and Wasm-GC test targets
|
||||
|
||||
### Notes
|
||||
|
||||
- LLVM was not tested because the local MoonBit toolchain is missing the experimental LLVM core bundle required by that target
|
||||
@@ -0,0 +1,19 @@
|
||||
## BitLogger Update Changes
|
||||
|
||||
version 1.1.3(0.7.3)
|
||||
|
||||
### Changes
|
||||
|
||||
- Add wasm CI checks and tests for the sync and async packages.
|
||||
- Add TraceContext binding for sync and async logger facades.
|
||||
- Add native async file lifecycle integration coverage and reproducible benchmarks.
|
||||
|
||||
### Verification
|
||||
|
||||
- Pass moon fmt --check, warning-denied checks, and tests across native, wasm, wasm-gc, and js.
|
||||
- Build the VitePress documentation site and run the native benchmark suite.
|
||||
|
||||
### Notes
|
||||
|
||||
- LLVM remains experimental and was not verified locally.
|
||||
- TraceContext maps structured fields only; HTTP propagation and OTLP export remain application-owned.
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Versioned BitLogger change summaries.
|
||||
|
||||
- [1.1.3](./1.1.3(0.7.3).md)
|
||||
- [1.1.2](./1.1.2(0.7.2).md)
|
||||
- [1.1.1](./1.1.1(0.7.1).md)
|
||||
- [1.1.0](./1.1.0(0.7.0).md)
|
||||
- [1.0.1](./1.0.1(0.6.1).md)
|
||||
|
||||
@@ -5,7 +5,7 @@ Use this flow for command-line tools and services that need a human-readable sta
|
||||
## Install And Import
|
||||
|
||||
```bash
|
||||
moon add Nanaloveyuki/BitLogger@0.7.1
|
||||
moon add Nanaloveyuki/BitLogger@0.7.3
|
||||
```
|
||||
|
||||
```moonbit
|
||||
|
||||
@@ -10,5 +10,6 @@ Start from an [Example flow](../examples/index.md), then add one extension at a
|
||||
| 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. |
|
||||
| Correlate logs with distributed requests | [Trace context](./observability.md) | Propagation and export stay application-owned. |
|
||||
|
||||
For exact function signatures, follow each page's links into the [API reference](../api/index.md).
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Trace Context
|
||||
|
||||
BitLogger keeps trace interoperability in its structured record model rather than owning HTTP propagation or a telemetry transport. Use the tracing library already selected by the application to extract a request context, then bind its identifiers to the logger.
|
||||
|
||||
```moonbit
|
||||
let request_logger = logger.with_trace_context(
|
||||
@log.trace_context(
|
||||
trace_id,
|
||||
span_id,
|
||||
trace_flags="01",
|
||||
trace_state=trace_state,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
Every record from `request_logger` receives `trace_id`, `span_id`, `trace_flags`, and, when supplied, `trace_state`. The base logger is unchanged, so a child request cannot leak identifiers into unrelated work.
|
||||
|
||||
Use `context.as_fields()` for a custom logger facade. BitLogger intentionally does not parse `traceparent` headers, create spans, make sampling decisions, or export OTLP data; those concerns remain with the application tracing implementation.
|
||||
@@ -7,7 +7,7 @@
|
||||
```bash
|
||||
moon new log-demo
|
||||
cd log-demo
|
||||
moon add Nanaloveyuki/BitLogger@0.7.1
|
||||
moon add Nanaloveyuki/BitLogger@0.7.3
|
||||
```
|
||||
|
||||
在应用的 `moon.pkg` 中加入:
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
| 同时发送到多个位置或按 level 路由 | [Sink 组合](./composition.md) | 比 preset 更需要显式构造。 |
|
||||
| 调整终端可读性 | [文本格式](./formatting.md) | 格式只改变呈现,不改变记录结构。 |
|
||||
| 在 native 与 web 目标之间共享代码 | [目标平台边界](./targets.md) | 文件与异步运行时行为因后端而异。 |
|
||||
| 将日志关联到分布式请求 | [Trace context](./observability.md) | 传播与导出仍由应用负责。 |
|
||||
|
||||
需要精确函数签名时,沿页面链接进入[英文 API 参考](../../api/index.md)。
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Trace Context
|
||||
|
||||
BitLogger 通过结构化字段承载 trace 上下文,不负责 HTTP 传播或遥测传输。应用应继续使用已选定的 tracing 库提取请求上下文,再把标识绑定到 logger。
|
||||
|
||||
```moonbit
|
||||
let request_logger = logger.with_trace_context(
|
||||
@log.trace_context(trace_id, span_id, trace_flags="01"),
|
||||
)
|
||||
```
|
||||
|
||||
派生 logger 的每条记录都会带有 `trace_id`、`span_id`、`trace_flags`,以及传入时的 `trace_state`。原 logger 不会被修改,避免请求上下文泄漏到无关任务。
|
||||
|
||||
`context.as_fields()` 可用于自定义 facade。`traceparent` 解析、span 创建、采样与 OTLP 导出仍由应用选用的 tracing 实现负责。
|
||||
@@ -1,9 +1,8 @@
|
||||
name = "Nanaloveyuki/BitLogger"
|
||||
|
||||
version = "0.7.1"
|
||||
version = "0.7.3"
|
||||
|
||||
import {
|
||||
"maria/json_parser@0.1.1",
|
||||
"moonbitlang/async@0.20.2",
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates BitLogger's package version, installation snippets, and release-note entry.
|
||||
|
||||
.DESCRIPTION
|
||||
For patch releases, the public release version is inferred from the latest
|
||||
changes entry. For example, after 1.1.2(0.7.2), running:
|
||||
|
||||
.\scripts\update-version.ps1 0.7.3
|
||||
|
||||
creates 1.1.3(0.7.3). A major or minor package-version change requires an
|
||||
explicit -ReleaseVersion so the public release sequence is never guessed.
|
||||
|
||||
The generated release note uses supplied -Change, -Verification, and -Note
|
||||
entries. Without them it contains TODO markers that must be completed before
|
||||
the release is committed.
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory, Position = 0)]
|
||||
[ValidatePattern('^\d+\.\d+\.\d+$')]
|
||||
[string]$Version,
|
||||
|
||||
[ValidatePattern('^\d+\.\d+\.\d+$')]
|
||||
[string]$ReleaseVersion,
|
||||
|
||||
[string[]]$Change,
|
||||
[string[]]$Verification,
|
||||
[string[]]$Note
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Read-TextFile([string]$Path) {
|
||||
[System.IO.File]::ReadAllText($Path)
|
||||
}
|
||||
|
||||
function Write-TextFile([string]$Path, [string]$Content) {
|
||||
$encoding = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($Path, $Content, $encoding)
|
||||
}
|
||||
|
||||
function Replace-ExactlyOnce(
|
||||
[string]$Content,
|
||||
[string]$Pattern,
|
||||
[string]$Replacement,
|
||||
[string]$Path
|
||||
) {
|
||||
$matches = [regex]::Matches($Content, $Pattern)
|
||||
if ($matches.Count -ne 1) {
|
||||
throw "Expected exactly one match for '$Pattern' in $Path, found $($matches.Count)."
|
||||
}
|
||||
[regex]::Replace($Content, $Pattern, $Replacement, 1)
|
||||
}
|
||||
|
||||
function Format-BulletList([string[]]$Items, [string]$Fallback) {
|
||||
$entries = if ($null -eq $Items -or $Items.Count -eq 0) {
|
||||
@($Fallback)
|
||||
} else {
|
||||
$Items
|
||||
}
|
||||
($entries | ForEach-Object { "- $_" }) -join [Environment]::NewLine
|
||||
}
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$moonModPath = Join-Path $repoRoot 'moon.mod'
|
||||
$changesIndexPath = Join-Path $repoRoot 'docs/changes/index.md'
|
||||
|
||||
foreach ($path in @($moonModPath, $changesIndexPath)) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "Required repository file is missing: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$moonMod = Read-TextFile $moonModPath
|
||||
$currentVersionMatch = [regex]::Match($moonMod, '(?m)^version\s*=\s*"(?<version>\d+\.\d+\.\d+)"\s*$')
|
||||
if (-not $currentVersionMatch.Success) {
|
||||
throw "Could not read the package version from $moonModPath."
|
||||
}
|
||||
$currentVersion = [version]$currentVersionMatch.Groups['version'].Value
|
||||
$targetVersion = [version]$Version
|
||||
if ($targetVersion -le $currentVersion) {
|
||||
throw "Target version $Version must be greater than the current version $currentVersion."
|
||||
}
|
||||
|
||||
$changesIndex = Read-TextFile $changesIndexPath
|
||||
$latestReleaseMatch = [regex]::Match(
|
||||
$changesIndex,
|
||||
'(?m)^- \[(?<release>\d+\.\d+\.\d+)\]\(\./(?<file>\d+\.\d+\.\d+\(\d+\.\d+\.\d+\)\.md)\)\s*$'
|
||||
)
|
||||
if (-not $latestReleaseMatch.Success) {
|
||||
throw "Could not read the latest release entry from $changesIndexPath."
|
||||
}
|
||||
|
||||
$latestReleaseVersion = [version]$latestReleaseMatch.Groups['release'].Value
|
||||
$latestPackageVersionMatch = [regex]::Match(
|
||||
$latestReleaseMatch.Groups['file'].Value,
|
||||
'\((?<version>\d+\.\d+\.\d+)\)\.md$'
|
||||
)
|
||||
if (-not $latestPackageVersionMatch.Success -or $latestPackageVersionMatch.Groups['version'].Value -ne $currentVersion.ToString()) {
|
||||
throw 'The latest changes entry must describe the current moon.mod package version before starting a new release.'
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($ReleaseVersion)) {
|
||||
if ($targetVersion.Major -ne $currentVersion.Major -or $targetVersion.Minor -ne $currentVersion.Minor) {
|
||||
throw 'A major or minor package-version change requires -ReleaseVersion, for example: -ReleaseVersion 1.2.0.'
|
||||
}
|
||||
$ReleaseVersion = "$($latestReleaseVersion.Major).$($latestReleaseVersion.Minor).$($targetVersion.Build)"
|
||||
}
|
||||
|
||||
$releaseFileName = "$ReleaseVersion($Version).md"
|
||||
$releasePath = Join-Path $repoRoot "docs/changes/$releaseFileName"
|
||||
if (Test-Path -LiteralPath $releasePath) {
|
||||
throw "Release note already exists: $releasePath"
|
||||
}
|
||||
if ($changesIndex.Contains("./$releaseFileName")) {
|
||||
throw "Release index already contains: $releaseFileName"
|
||||
}
|
||||
|
||||
$updatedMoonMod = Replace-ExactlyOnce `
|
||||
$moonMod `
|
||||
'(?m)^version\s*=\s*"\d+\.\d+\.\d+"\s*$' `
|
||||
"version = `"$Version`"" `
|
||||
$moonModPath
|
||||
|
||||
$installFiles = @(
|
||||
'README.md',
|
||||
'docs/README-en.md',
|
||||
'docs/examples/console.md',
|
||||
'docs/zh/examples/console.md'
|
||||
)
|
||||
$updatedFiles = @{}
|
||||
foreach ($relativePath in $installFiles) {
|
||||
$path = Join-Path $repoRoot $relativePath
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "Required installation example is missing: $path"
|
||||
}
|
||||
$content = Read-TextFile $path
|
||||
$updatedFiles[$path] = Replace-ExactlyOnce `
|
||||
$content `
|
||||
'Nanaloveyuki/BitLogger@\d+\.\d+\.\d+' `
|
||||
"Nanaloveyuki/BitLogger@$Version" `
|
||||
$path
|
||||
}
|
||||
|
||||
$updatedChangesIndex = $changesIndex.Replace(
|
||||
$latestReleaseMatch.Value,
|
||||
"- [$ReleaseVersion](./$releaseFileName)$([Environment]::NewLine)$($latestReleaseMatch.Value)"
|
||||
)
|
||||
|
||||
$releaseNote = @"
|
||||
## BitLogger Update Changes
|
||||
|
||||
version $ReleaseVersion($Version)
|
||||
|
||||
### Changes
|
||||
|
||||
$(Format-BulletList $Change 'TODO: summarize the user-visible changes in this release.')
|
||||
|
||||
### Verification
|
||||
|
||||
$(Format-BulletList $Verification 'TODO: record the commands and targets verified for this release.')
|
||||
|
||||
### Notes
|
||||
|
||||
$(Format-BulletList $Note 'TODO: record release limitations or compatibility notes when applicable.')
|
||||
"@
|
||||
|
||||
$writeTargets = @($moonModPath, $changesIndexPath, $releasePath) + $updatedFiles.Keys
|
||||
if ($PSCmdlet.ShouldProcess(($writeTargets -join ', '), "update to $Version")) {
|
||||
Write-TextFile $moonModPath $updatedMoonMod
|
||||
foreach ($path in $updatedFiles.Keys) {
|
||||
Write-TextFile $path $updatedFiles[$path]
|
||||
}
|
||||
Write-TextFile $changesIndexPath $updatedChangesIndex
|
||||
Write-TextFile $releasePath $releaseNote
|
||||
}
|
||||
|
||||
if ($WhatIfPreference) {
|
||||
Write-Output "Previewed release $ReleaseVersion($Version)."
|
||||
} else {
|
||||
Write-Output "Prepared release $ReleaseVersion($Version)."
|
||||
}
|
||||
Write-Output "Release notes: $releasePath"
|
||||
@@ -455,6 +455,30 @@ async test "async logger with_context_fields prepends shared fields without muta
|
||||
inspect(written_fields.val[2].value, content="test")
|
||||
}
|
||||
|
||||
///|
|
||||
test "async facades bind trace context through standard fields" {
|
||||
let context = @bitlogger.trace_context("trace-async", "span-async")
|
||||
let base = async_logger(
|
||||
@bitlogger.callback_sink(fn(_) { }),
|
||||
config=AsyncLoggerConfig::new(max_pending=4),
|
||||
min_level=@bitlogger.Level::Info,
|
||||
)
|
||||
let derived = base.with_trace_context(context)
|
||||
inspect(base.context_fields.length(), content="0")
|
||||
inspect(derived.context_fields.length(), content="3")
|
||||
inspect(derived.context_fields[0].key, content="trace_id")
|
||||
inspect(derived.context_fields[0].value, content="trace-async")
|
||||
inspect(derived.context_fields[1].key, content="span_id")
|
||||
inspect(derived.context_fields[2].value, content="01")
|
||||
|
||||
let library = LibraryAsyncLogger::new(@bitlogger.callback_sink(fn(_) { })).with_trace_context(
|
||||
context,
|
||||
)
|
||||
let full = library.to_async_logger()
|
||||
inspect(full.context_fields.length(), content="3")
|
||||
inspect(full.context_fields[1].value, content="span-async")
|
||||
}
|
||||
|
||||
///|
|
||||
async test "async logger child composes target and preserves other state" {
|
||||
let written_target : Ref[String] = Ref("")
|
||||
@@ -831,29 +855,29 @@ test "async json helpers export stable structured shapes" {
|
||||
flush=AsyncFlushPolicy::Batch,
|
||||
),
|
||||
)
|
||||
let config_obj = config_json.as_object().unwrap()
|
||||
let config_obj = config_json.expect_json_object()
|
||||
inspect(
|
||||
@json_parser.stringify(config_json),
|
||||
config_json.stringify(),
|
||||
content="{\"max_pending\":8,\"max_batch\":3,\"linger_ms\":25,\"overflow\":\"DropOldest\",\"flush\":\"Batch\"}",
|
||||
)
|
||||
inspect(
|
||||
config_obj.get("max_pending").unwrap().as_number().unwrap().to_int(),
|
||||
config_obj.get("max_pending").unwrap().expect_json_number().to_int(),
|
||||
content="8",
|
||||
)
|
||||
inspect(
|
||||
config_obj.get("max_batch").unwrap().as_number().unwrap().to_int(),
|
||||
config_obj.get("max_batch").unwrap().expect_json_number().to_int(),
|
||||
content="3",
|
||||
)
|
||||
inspect(
|
||||
config_obj.get("linger_ms").unwrap().as_number().unwrap().to_int(),
|
||||
config_obj.get("linger_ms").unwrap().expect_json_number().to_int(),
|
||||
content="25",
|
||||
)
|
||||
inspect(
|
||||
config_obj.get("overflow").unwrap().as_string().unwrap(),
|
||||
config_obj.get("overflow").unwrap().expect_json_string(),
|
||||
content="DropOldest",
|
||||
)
|
||||
inspect(
|
||||
config_obj.get("flush").unwrap().as_string().unwrap(),
|
||||
config_obj.get("flush").unwrap().expect_json_string(),
|
||||
content="Batch",
|
||||
)
|
||||
|
||||
@@ -874,85 +898,84 @@ test "async json helpers export stable structured shapes" {
|
||||
),
|
||||
),
|
||||
)
|
||||
let build_obj = build_json.as_object().unwrap()
|
||||
let logger_obj = build_obj.get("logger").unwrap().as_object().unwrap()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().as_object().unwrap()
|
||||
let build_obj = build_json.expect_json_object()
|
||||
let logger_obj = build_obj.get("logger").unwrap().expect_json_object()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().expect_json_object()
|
||||
let formatter_obj = sink_obj
|
||||
.get("text_formatter")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
let async_obj = build_obj.get("async_config").unwrap().as_object().unwrap()
|
||||
.expect_json_object()
|
||||
let async_obj = build_obj.get("async_config").unwrap().expect_json_object()
|
||||
|
||||
inspect(
|
||||
logger_obj.get("min_level").unwrap().as_string().unwrap(),
|
||||
logger_obj.get("min_level").unwrap().expect_json_string(),
|
||||
content="WARN",
|
||||
)
|
||||
inspect(
|
||||
logger_obj.get("target").unwrap().as_string().unwrap(),
|
||||
logger_obj.get("target").unwrap().expect_json_string(),
|
||||
content="async.roundtrip",
|
||||
)
|
||||
inspect(
|
||||
logger_obj.get("timestamp").unwrap().as_bool().unwrap(),
|
||||
logger_obj.get("timestamp").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(logger_obj.get("queue") is None, content="true")
|
||||
inspect(
|
||||
sink_obj.get("kind").unwrap().as_string().unwrap(),
|
||||
sink_obj.get("kind").unwrap().expect_json_string(),
|
||||
content="text_console",
|
||||
)
|
||||
inspect(sink_obj.get("path").unwrap().as_string().unwrap(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().as_bool().unwrap(), content="true")
|
||||
inspect(sink_obj.get("path").unwrap().expect_json_string(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().expect_json_bool(), content="true")
|
||||
inspect(
|
||||
sink_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
sink_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_timestamp").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_timestamp").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("field_separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("field_separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("template").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("template").unwrap().expect_json_string(),
|
||||
content="",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_mode").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_mode").unwrap().expect_json_string(),
|
||||
content="never",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_support").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_support").unwrap().expect_json_string(),
|
||||
content="truecolor",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("style_markup").unwrap().expect_json_string(),
|
||||
content="full",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("max_pending").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("max_pending").unwrap().expect_json_number().to_int(),
|
||||
content="2",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("max_batch").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("max_batch").unwrap().expect_json_number().to_int(),
|
||||
content="5",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("linger_ms").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("linger_ms").unwrap().expect_json_number().to_int(),
|
||||
content="40",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("overflow").unwrap().as_string().unwrap(),
|
||||
async_obj.get("overflow").unwrap().expect_json_string(),
|
||||
content="DropNewest",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("flush").unwrap().as_string().unwrap(),
|
||||
async_obj.get("flush").unwrap().expect_json_string(),
|
||||
content="Shutdown",
|
||||
)
|
||||
}
|
||||
@@ -961,103 +984,102 @@ test "async json helpers export stable structured shapes" {
|
||||
test "async build config json export materializes parsed omitted defaults" {
|
||||
let parsed = parse_async_logger_build_config_text("{}")
|
||||
let build_json = async_logger_build_config_to_json(parsed)
|
||||
let build_obj = build_json.as_object().unwrap()
|
||||
let logger_obj = build_obj.get("logger").unwrap().as_object().unwrap()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().as_object().unwrap()
|
||||
let build_obj = build_json.expect_json_object()
|
||||
let logger_obj = build_obj.get("logger").unwrap().expect_json_object()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().expect_json_object()
|
||||
let formatter_obj = sink_obj
|
||||
.get("text_formatter")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
let async_obj = build_obj.get("async_config").unwrap().as_object().unwrap()
|
||||
.expect_json_object()
|
||||
let async_obj = build_obj.get("async_config").unwrap().expect_json_object()
|
||||
|
||||
inspect(
|
||||
@json_parser.stringify(build_json),
|
||||
build_json.stringify(),
|
||||
content="{\"logger\":{\"min_level\":\"INFO\",\"target\":\"\",\"timestamp\":false,\"sink\":{\"kind\":\"console\",\"path\":\"\",\"append\":true,\"auto_flush\":true,\"text_formatter\":{\"show_timestamp\":true,\"show_level\":true,\"show_target\":true,\"show_fields\":true,\"separator\":\" \",\"field_separator\":\" \",\"template\":\"\",\"color_mode\":\"never\",\"color_support\":\"truecolor\",\"style_markup\":\"full\",\"target_style_markup\":\"disabled\",\"fields_style_markup\":\"disabled\"}}},\"async_config\":{\"max_pending\":0,\"max_batch\":1,\"linger_ms\":0,\"overflow\":\"Blocking\",\"flush\":\"Never\"}}",
|
||||
)
|
||||
inspect(
|
||||
logger_obj.get("min_level").unwrap().as_string().unwrap(),
|
||||
logger_obj.get("min_level").unwrap().expect_json_string(),
|
||||
content="INFO",
|
||||
)
|
||||
inspect(logger_obj.get("target").unwrap().as_string().unwrap(), content="")
|
||||
inspect(logger_obj.get("target").unwrap().expect_json_string(), content="")
|
||||
inspect(
|
||||
logger_obj.get("timestamp").unwrap().as_bool().unwrap(),
|
||||
logger_obj.get("timestamp").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(logger_obj.get("queue") is None, content="true")
|
||||
inspect(sink_obj.get("kind").unwrap().as_string().unwrap(), content="console")
|
||||
inspect(sink_obj.get("path").unwrap().as_string().unwrap(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().as_bool().unwrap(), content="true")
|
||||
inspect(sink_obj.get("kind").unwrap().expect_json_string(), content="console")
|
||||
inspect(sink_obj.get("path").unwrap().expect_json_string(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().expect_json_bool(), content="true")
|
||||
inspect(
|
||||
sink_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
sink_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_timestamp").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_timestamp").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_level").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_level").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_target").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_target").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_fields").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_fields").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("field_separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("field_separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("template").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("template").unwrap().expect_json_string(),
|
||||
content="",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_mode").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_mode").unwrap().expect_json_string(),
|
||||
content="never",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_support").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_support").unwrap().expect_json_string(),
|
||||
content="truecolor",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("style_markup").unwrap().expect_json_string(),
|
||||
content="full",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("target_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("target_style_markup").unwrap().expect_json_string(),
|
||||
content="disabled",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("fields_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("fields_style_markup").unwrap().expect_json_string(),
|
||||
content="disabled",
|
||||
)
|
||||
inspect(formatter_obj.get("style_tags") is None, content="true")
|
||||
inspect(
|
||||
async_obj.get("max_pending").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("max_pending").unwrap().expect_json_number().to_int(),
|
||||
content="0",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("max_batch").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("max_batch").unwrap().expect_json_number().to_int(),
|
||||
content="1",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("linger_ms").unwrap().as_number().unwrap().to_int(),
|
||||
async_obj.get("linger_ms").unwrap().expect_json_number().to_int(),
|
||||
content="0",
|
||||
)
|
||||
inspect(
|
||||
async_obj.get("overflow").unwrap().as_string().unwrap(),
|
||||
async_obj.get("overflow").unwrap().expect_json_string(),
|
||||
content="Blocking",
|
||||
)
|
||||
inspect(async_obj.get("flush").unwrap().as_string().unwrap(), content="Never")
|
||||
inspect(async_obj.get("flush").unwrap().expect_json_string(), content="Never")
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -1068,7 +1090,7 @@ test "async config parsers reject malformed input" {
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(invalid_json_error.contains("UnexpectedToken"), content="true")
|
||||
inspect(invalid_json_error != "no error", content="true")
|
||||
|
||||
let wrong_type_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_config_text("{\"max_pending\":\"many\"}"))
|
||||
@@ -1144,7 +1166,7 @@ test "async runtime capability helpers stay consistent" {
|
||||
)
|
||||
inspect(state.background_worker == worker_supported, content="true")
|
||||
inspect(
|
||||
@json_parser.stringify(async_runtime_state_to_json(state)),
|
||||
async_runtime_state_to_json(state).stringify(),
|
||||
content=if worker_supported {
|
||||
"{\"mode\":\"native_worker\",\"background_worker\":true}"
|
||||
} else {
|
||||
@@ -1225,7 +1247,7 @@ test "async logger state snapshot reflects current counters and runtime" {
|
||||
content="Shutdown",
|
||||
)
|
||||
inspect(
|
||||
@json_parser.stringify(async_logger_state_to_json(state)),
|
||||
async_logger_state_to_json(state).stringify(),
|
||||
content=if async_runtime_supports_background_worker() {
|
||||
"{\"runtime\":{\"mode\":\"native_worker\",\"background_worker\":true},\"phase\":\"ready\",\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"backlog_retained\":false,\"can_rerun\":true,\"terminal\":false,\"last_error\":\"\",\"flush_policy\":\"Shutdown\"}"
|
||||
} else {
|
||||
|
||||
@@ -48,9 +48,7 @@ pub fn async_runtime_state() -> AsyncRuntimeState {
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_runtime_state_to_json(
|
||||
state : AsyncRuntimeState,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> Json {
|
||||
@utils.async_runtime_state_to_json(state)
|
||||
}
|
||||
|
||||
@@ -63,9 +61,7 @@ pub fn stringify_async_runtime_state(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_logger_state_to_json(
|
||||
state : AsyncLoggerState,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> Json {
|
||||
@utils.async_logger_state_to_json(state)
|
||||
}
|
||||
|
||||
@@ -88,9 +84,7 @@ pub fn parse_async_logger_config_text(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_logger_config_to_json(
|
||||
config : AsyncLoggerConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> Json {
|
||||
@utils.async_logger_config_to_json(config)
|
||||
}
|
||||
|
||||
@@ -115,7 +109,7 @@ pub fn parse_async_logger_build_config_text(
|
||||
///|
|
||||
pub fn async_logger_build_config_to_json(
|
||||
config : AsyncLoggerBuildConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
) -> Json {
|
||||
@utils.async_logger_build_config_to_json(config)
|
||||
}
|
||||
|
||||
@@ -280,6 +274,14 @@ pub fn[S] AsyncLogger::with_context_fields(
|
||||
{ ..self, context_fields: fields }
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] AsyncLogger::with_trace_context(
|
||||
self : AsyncLogger[S],
|
||||
context : @bitlogger.TraceContext,
|
||||
) -> AsyncLogger[S] {
|
||||
self.with_context_fields(context.as_fields())
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] AsyncLogger::with_filter(
|
||||
self : AsyncLogger[S],
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
///|
|
||||
async test "native async file lifecycle flushes rotates and restarts" {
|
||||
if !@bitlogger.native_files_supported() {
|
||||
inspect(@bitlogger.native_files_supported(), content="false")
|
||||
} else {
|
||||
let path = "logs/bitlogger-async-native-e2e.log"
|
||||
let first_flushes : Ref[Int] = Ref(0)
|
||||
let first_sink = @bitlogger.file_sink(
|
||||
path,
|
||||
append=false,
|
||||
auto_flush=false,
|
||||
rotation=Some(@bitlogger.file_rotation(8, max_backups=1)),
|
||||
formatter=fn(rec) { rec.message },
|
||||
)
|
||||
let first = async_logger(
|
||||
first_sink,
|
||||
config=AsyncLoggerConfig::new(
|
||||
max_pending=4,
|
||||
overflow=AsyncOverflowPolicy::Blocking,
|
||||
max_batch=4,
|
||||
linger_ms=0,
|
||||
flush=AsyncFlushPolicy::Batch,
|
||||
),
|
||||
min_level=@bitlogger.Level::Info,
|
||||
target="async.file.e2e",
|
||||
flush=fn(sink) {
|
||||
first_flushes.val += 1
|
||||
ignore(sink.flush())
|
||||
},
|
||||
)
|
||||
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(() => first.run())
|
||||
first.info("first-record")
|
||||
first.shutdown()
|
||||
})
|
||||
|
||||
inspect(first.is_closed(), content="true")
|
||||
inspect(first.has_failed(), content="false")
|
||||
inspect(first.pending_count(), content="0")
|
||||
inspect(first_flushes.val > 0, content="true")
|
||||
inspect(first_sink.path(), content="logs/bitlogger-async-native-e2e.log")
|
||||
inspect(first_sink.rotation_enabled(), content="true")
|
||||
inspect(first_sink.rotation_failures(), content="0")
|
||||
inspect(first_sink.write_failures(), content="0")
|
||||
inspect(first_sink.flush_failures(), content="0")
|
||||
inspect(first_sink.close(), content="true")
|
||||
|
||||
let restart_flushes : Ref[Int] = Ref(0)
|
||||
let restarted_sink = @bitlogger.file_sink(
|
||||
path,
|
||||
append=true,
|
||||
auto_flush=false,
|
||||
rotation=Some(@bitlogger.file_rotation(8, max_backups=1)),
|
||||
formatter=fn(rec) { rec.message },
|
||||
)
|
||||
let restarted = async_logger(
|
||||
restarted_sink,
|
||||
config=AsyncLoggerConfig::new(
|
||||
max_pending=4,
|
||||
overflow=AsyncOverflowPolicy::Blocking,
|
||||
max_batch=4,
|
||||
linger_ms=0,
|
||||
flush=AsyncFlushPolicy::Shutdown,
|
||||
),
|
||||
min_level=@bitlogger.Level::Info,
|
||||
target="async.file.e2e",
|
||||
flush=fn(sink) {
|
||||
restart_flushes.val += 1
|
||||
ignore(sink.flush())
|
||||
},
|
||||
)
|
||||
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(() => restarted.run())
|
||||
restarted.info("second-record")
|
||||
restarted.shutdown()
|
||||
})
|
||||
|
||||
inspect(restarted.is_closed(), content="true")
|
||||
inspect(restarted.has_failed(), content="false")
|
||||
inspect(restarted.pending_count(), content="0")
|
||||
inspect(restart_flushes.val, content="1")
|
||||
inspect(restarted_sink.rotation_enabled(), content="true")
|
||||
inspect(restarted_sink.rotation_failures(), content="0")
|
||||
inspect(restarted_sink.write_failures(), content="0")
|
||||
inspect(restarted_sink.flush_failures(), content="0")
|
||||
inspect(restarted_sink.close(), content="true")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
///|
|
||||
fn Json::expect_json_object(self : Json) -> Map[String, Json] {
|
||||
match self {
|
||||
Json::Object(value) => value
|
||||
_ => abort("Expected JSON object")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_number(self : Json) -> Double {
|
||||
match self {
|
||||
Json::Number(value, ..) => value
|
||||
_ => abort("Expected JSON number")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_string(self : Json) -> String {
|
||||
match self {
|
||||
Json::String(value) => value
|
||||
_ => abort("Expected JSON string")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_bool(self : Json) -> Bool {
|
||||
match self {
|
||||
Json::True => true
|
||||
Json::False => false
|
||||
_ => abort("Expected JSON bool")
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,14 @@ pub fn[S] LibraryAsyncLogger::bind(
|
||||
self.with_context_fields(fields)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] LibraryAsyncLogger::with_trace_context(
|
||||
self : LibraryAsyncLogger[S],
|
||||
context : @bitlogger.TraceContext,
|
||||
) -> LibraryAsyncLogger[S] {
|
||||
library_async_logger(self.inner.with_trace_context(context))
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] LibraryAsyncLogger::is_enabled(
|
||||
self : LibraryAsyncLogger[S],
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @bitlogger,
|
||||
"Nanaloveyuki/BitLogger/src-async/utils",
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/json",
|
||||
"moonbitlang/async",
|
||||
"moonbitlang/async/aqueue",
|
||||
"moonbitlang/core/env",
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/core",
|
||||
"Nanaloveyuki/BitLogger/src/runtime",
|
||||
"Nanaloveyuki/BitLogger/src/sink_graph",
|
||||
"maria/json_parser",
|
||||
"moonbitlang/async/aqueue",
|
||||
"moonbitlang/core/ref",
|
||||
}
|
||||
@@ -14,11 +13,11 @@ import {
|
||||
// Values
|
||||
pub fn[S] async_logger(S, config? : @utils.AsyncLoggerConfig, min_level? : @core.Level, target? : String, flush? : (S) -> Unit raise) -> AsyncLogger[S]
|
||||
|
||||
pub fn async_logger_build_config_to_json(@utils.AsyncLoggerBuildConfig) -> @json_parser.JsonValue
|
||||
pub fn async_logger_build_config_to_json(@utils.AsyncLoggerBuildConfig) -> Json
|
||||
|
||||
pub fn async_logger_config_to_json(@utils.AsyncLoggerConfig) -> @json_parser.JsonValue
|
||||
pub fn async_logger_config_to_json(@utils.AsyncLoggerConfig) -> Json
|
||||
|
||||
pub fn async_logger_state_to_json(@utils.AsyncLoggerState) -> @json_parser.JsonValue
|
||||
pub fn async_logger_state_to_json(@utils.AsyncLoggerState) -> Json
|
||||
|
||||
pub fn async_runtime_mode() -> @utils.AsyncRuntimeMode
|
||||
|
||||
@@ -26,7 +25,7 @@ pub fn async_runtime_mode_label(@utils.AsyncRuntimeMode) -> String
|
||||
|
||||
pub fn async_runtime_state() -> @utils.AsyncRuntimeState
|
||||
|
||||
pub fn async_runtime_state_to_json(@utils.AsyncRuntimeState) -> @json_parser.JsonValue
|
||||
pub fn async_runtime_state_to_json(@utils.AsyncRuntimeState) -> Json
|
||||
|
||||
pub fn async_runtime_supports_background_worker() -> Bool
|
||||
|
||||
@@ -115,6 +114,7 @@ pub fn[S] AsyncLogger::with_min_level(Self[S], @core.Level) -> Self[S]
|
||||
pub fn[S] AsyncLogger::with_patch(Self[S], (@core.Record) -> @core.Record) -> Self[S]
|
||||
pub fn[S] AsyncLogger::with_target(Self[S], String) -> Self[S]
|
||||
pub fn[S] AsyncLogger::with_timestamp(Self[S], enabled? : Bool) -> Self[S]
|
||||
pub fn[S] AsyncLogger::with_trace_context(Self[S], @core.TraceContext) -> Self[S]
|
||||
|
||||
pub struct LibraryAsyncLogger[S] {
|
||||
inner : AsyncLogger[S]
|
||||
@@ -132,6 +132,7 @@ pub fn[S] LibraryAsyncLogger::to_async_logger(Self[S]) -> AsyncLogger[S]
|
||||
pub async fn[S] LibraryAsyncLogger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
|
||||
pub fn[S] LibraryAsyncLogger::with_context_fields(Self[S], Array[@core.Field]) -> Self[S]
|
||||
pub fn[S] LibraryAsyncLogger::with_target(Self[S], String) -> Self[S]
|
||||
pub fn[S] LibraryAsyncLogger::with_trace_context(Self[S], @core.TraceContext) -> Self[S]
|
||||
|
||||
// Type aliases
|
||||
pub type ApplicationAsyncLogger = AsyncLogger[@runtime.RuntimeSink]
|
||||
|
||||
@@ -154,12 +154,10 @@ pub fn AsyncLoggerState::new(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_runtime_state_to_json(
|
||||
state : AsyncRuntimeState,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
"mode": @json_parser.JsonValue::String(async_runtime_mode_label(state.mode)),
|
||||
"background_worker": @json_parser.JsonValue::Bool(state.background_worker),
|
||||
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> Json {
|
||||
Json::object({
|
||||
"mode": Json::string(async_runtime_mode_label(state.mode)),
|
||||
"background_worker": Json::boolean(state.background_worker),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,9 +168,9 @@ pub fn stringify_async_runtime_state(
|
||||
) -> String {
|
||||
let value = async_runtime_state_to_json(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,37 +184,25 @@ fn async_flush_policy_label(policy : AsyncFlushPolicy) -> String {
|
||||
}
|
||||
|
||||
///|
|
||||
fn async_logger_state_to_json_value(
|
||||
state : AsyncLoggerState,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
fn async_logger_state_to_json_value(state : AsyncLoggerState) -> Json {
|
||||
Json::object({
|
||||
"runtime": async_runtime_state_to_json(state.runtime),
|
||||
"phase": @json_parser.JsonValue::String(
|
||||
async_lifecycle_phase_label(state.phase),
|
||||
),
|
||||
"pending_count": @json_parser.JsonValue::Number(
|
||||
state.pending_count.to_double(),
|
||||
),
|
||||
"dropped_count": @json_parser.JsonValue::Number(
|
||||
state.dropped_count.to_double(),
|
||||
),
|
||||
"is_closed": @json_parser.JsonValue::Bool(state.is_closed),
|
||||
"is_running": @json_parser.JsonValue::Bool(state.is_running),
|
||||
"has_failed": @json_parser.JsonValue::Bool(state.has_failed),
|
||||
"backlog_retained": @json_parser.JsonValue::Bool(state.backlog_retained),
|
||||
"can_rerun": @json_parser.JsonValue::Bool(state.can_rerun),
|
||||
"terminal": @json_parser.JsonValue::Bool(state.terminal),
|
||||
"last_error": @json_parser.JsonValue::String(state.last_error),
|
||||
"flush_policy": @json_parser.JsonValue::String(
|
||||
async_flush_policy_label(state.flush_policy),
|
||||
),
|
||||
"phase": Json::string(async_lifecycle_phase_label(state.phase)),
|
||||
"pending_count": Json::number(state.pending_count.to_double()),
|
||||
"dropped_count": Json::number(state.dropped_count.to_double()),
|
||||
"is_closed": Json::boolean(state.is_closed),
|
||||
"is_running": Json::boolean(state.is_running),
|
||||
"has_failed": Json::boolean(state.has_failed),
|
||||
"backlog_retained": Json::boolean(state.backlog_retained),
|
||||
"can_rerun": Json::boolean(state.can_rerun),
|
||||
"terminal": Json::boolean(state.terminal),
|
||||
"last_error": Json::string(state.last_error),
|
||||
"flush_policy": Json::string(async_flush_policy_label(state.flush_policy)),
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_logger_state_to_json(
|
||||
state : AsyncLoggerState,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> Json {
|
||||
async_logger_state_to_json_value(state)
|
||||
}
|
||||
|
||||
@@ -227,9 +213,9 @@ pub fn stringify_async_logger_state(
|
||||
) -> String {
|
||||
let value = async_logger_state_to_json_value(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,53 +279,38 @@ fn parse_async_flush(name : String) -> AsyncFlushPolicy raise {
|
||||
pub fn parse_async_logger_config_text(
|
||||
input : String,
|
||||
) -> AsyncLoggerConfig raise {
|
||||
let root = @json_parser.parse(input)
|
||||
let obj = match root.as_object() {
|
||||
Some(obj) => obj
|
||||
None => raise Failure::Failure("Expected object for async logger config")
|
||||
let root = @json.parse(input)
|
||||
let obj = match root {
|
||||
Json::Object(obj) => obj
|
||||
_ => raise Failure::Failure("Expected object for async logger config")
|
||||
}
|
||||
let max_pending = match obj.get("max_pending") {
|
||||
Some(value) =>
|
||||
match value.as_number() {
|
||||
Some(number) => number.to_int()
|
||||
None =>
|
||||
raise Failure::Failure("Expected number at async_config.max_pending")
|
||||
}
|
||||
Some(Json::Number(number, ..)) => number.to_int()
|
||||
Some(_) =>
|
||||
raise Failure::Failure("Expected number at async_config.max_pending")
|
||||
None => 0
|
||||
}
|
||||
let overflow = match obj.get("overflow") {
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => parse_async_overflow(text)
|
||||
None =>
|
||||
raise Failure::Failure("Expected string at async_config.overflow")
|
||||
}
|
||||
Some(Json::String(text)) => parse_async_overflow(text)
|
||||
Some(_) =>
|
||||
raise Failure::Failure("Expected string at async_config.overflow")
|
||||
None => AsyncOverflowPolicy::Blocking
|
||||
}
|
||||
let max_batch = match obj.get("max_batch") {
|
||||
Some(value) =>
|
||||
match value.as_number() {
|
||||
Some(number) => number.to_int()
|
||||
None =>
|
||||
raise Failure::Failure("Expected number at async_config.max_batch")
|
||||
}
|
||||
Some(Json::Number(number, ..)) => number.to_int()
|
||||
Some(_) =>
|
||||
raise Failure::Failure("Expected number at async_config.max_batch")
|
||||
None => 1
|
||||
}
|
||||
let linger_ms = match obj.get("linger_ms") {
|
||||
Some(value) =>
|
||||
match value.as_number() {
|
||||
Some(number) => number.to_int()
|
||||
None =>
|
||||
raise Failure::Failure("Expected number at async_config.linger_ms")
|
||||
}
|
||||
Some(Json::Number(number, ..)) => number.to_int()
|
||||
Some(_) =>
|
||||
raise Failure::Failure("Expected number at async_config.linger_ms")
|
||||
None => 0
|
||||
}
|
||||
let flush = match obj.get("flush") {
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => parse_async_flush(text)
|
||||
None => raise Failure::Failure("Expected string at async_config.flush")
|
||||
}
|
||||
Some(Json::String(text)) => parse_async_flush(text)
|
||||
Some(_) => raise Failure::Failure("Expected string at async_config.flush")
|
||||
None => AsyncFlushPolicy::Never
|
||||
}
|
||||
AsyncLoggerConfig::new(
|
||||
@@ -352,23 +323,19 @@ pub fn parse_async_logger_config_text(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn async_logger_config_to_json(
|
||||
config : AsyncLoggerConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
"max_pending": @json_parser.JsonValue::Number(
|
||||
config.max_pending.to_double(),
|
||||
),
|
||||
"max_batch": @json_parser.JsonValue::Number(config.max_batch.to_double()),
|
||||
"linger_ms": @json_parser.JsonValue::Number(config.linger_ms.to_double()),
|
||||
"overflow": @json_parser.JsonValue::String(
|
||||
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> Json {
|
||||
Json::object({
|
||||
"max_pending": Json::number(config.max_pending.to_double()),
|
||||
"max_batch": Json::number(config.max_batch.to_double()),
|
||||
"linger_ms": Json::number(config.linger_ms.to_double()),
|
||||
"overflow": Json::string(
|
||||
match config.overflow {
|
||||
AsyncOverflowPolicy::Blocking => "Blocking"
|
||||
AsyncOverflowPolicy::DropOldest => "DropOldest"
|
||||
AsyncOverflowPolicy::DropNewest => "DropNewest"
|
||||
},
|
||||
),
|
||||
"flush": @json_parser.JsonValue::String(
|
||||
"flush": Json::string(
|
||||
match config.flush {
|
||||
AsyncFlushPolicy::Never => "Never"
|
||||
AsyncFlushPolicy::Batch => "Batch"
|
||||
@@ -385,9 +352,9 @@ pub fn stringify_async_logger_config(
|
||||
) -> String {
|
||||
let value = async_logger_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,21 +376,20 @@ pub fn AsyncLoggerBuildConfig::new(
|
||||
pub fn parse_async_logger_build_config_text(
|
||||
input : String,
|
||||
) -> AsyncLoggerBuildConfig raise {
|
||||
let root = @json_parser.parse(input)
|
||||
let obj = match root.as_object() {
|
||||
Some(obj) => obj
|
||||
None =>
|
||||
let root = @json.parse(input)
|
||||
let obj = match root {
|
||||
Json::Object(obj) => obj
|
||||
_ =>
|
||||
raise Failure::Failure(
|
||||
"Expected object at async logger build config root",
|
||||
)
|
||||
}
|
||||
let logger = match obj.get("logger") {
|
||||
Some(value) =>
|
||||
@bitlogger.parse_logger_config_text(@json_parser.stringify(value))
|
||||
Some(value) => @bitlogger.parse_logger_config_text(value.stringify())
|
||||
None => @bitlogger.default_logger_config()
|
||||
}
|
||||
let async_config = match obj.get("async_config") {
|
||||
Some(value) => parse_async_logger_config_text(@json_parser.stringify(value))
|
||||
Some(value) => parse_async_logger_config_text(value.stringify())
|
||||
None => AsyncLoggerConfig::new()
|
||||
}
|
||||
AsyncLoggerBuildConfig::new(logger~, async_config~)
|
||||
@@ -432,8 +398,8 @@ pub fn parse_async_logger_build_config_text(
|
||||
///|
|
||||
pub fn async_logger_build_config_to_json(
|
||||
config : AsyncLoggerBuildConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
) -> Json {
|
||||
Json::object({
|
||||
"logger": @bitlogger.logger_config_to_json(config.logger),
|
||||
"async_config": async_logger_config_to_json(config.async_config),
|
||||
})
|
||||
@@ -446,8 +412,8 @@ pub fn stringify_async_logger_build_config(
|
||||
) -> String {
|
||||
let value = async_logger_build_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src" @bitlogger,
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/json",
|
||||
}
|
||||
|
||||
@@ -3,19 +3,18 @@ package "Nanaloveyuki/BitLogger/src-async/utils"
|
||||
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src/config_model",
|
||||
"maria/json_parser",
|
||||
}
|
||||
|
||||
// Values
|
||||
pub fn async_logger_build_config_to_json(AsyncLoggerBuildConfig) -> @json_parser.JsonValue
|
||||
pub fn async_logger_build_config_to_json(AsyncLoggerBuildConfig) -> Json
|
||||
|
||||
pub fn async_logger_config_to_json(AsyncLoggerConfig) -> @json_parser.JsonValue
|
||||
pub fn async_logger_config_to_json(AsyncLoggerConfig) -> Json
|
||||
|
||||
pub fn async_logger_state_to_json(AsyncLoggerState) -> @json_parser.JsonValue
|
||||
pub fn async_logger_state_to_json(AsyncLoggerState) -> Json
|
||||
|
||||
pub fn async_runtime_mode_label(AsyncRuntimeMode) -> String
|
||||
|
||||
pub fn async_runtime_state_to_json(AsyncRuntimeState) -> @json_parser.JsonValue
|
||||
pub fn async_runtime_state_to_json(AsyncRuntimeState) -> Json
|
||||
|
||||
pub fn compatibility_async_runtime_mode() -> AsyncRuntimeMode
|
||||
|
||||
|
||||
+5
-9
@@ -39,7 +39,7 @@ pub fn parse_logger_config_text(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> Json {
|
||||
@config_model.queue_config_to_json(queue)
|
||||
}
|
||||
|
||||
@@ -52,9 +52,7 @@ pub fn stringify_queue_config(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn text_formatter_config_to_json(
|
||||
config : TextFormatterConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn text_formatter_config_to_json(config : TextFormatterConfig) -> Json {
|
||||
@config_model.text_formatter_config_to_json(config)
|
||||
}
|
||||
|
||||
@@ -67,14 +65,12 @@ pub fn stringify_text_formatter_config(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_rotation_config_to_json(
|
||||
config : FileRotation,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn file_rotation_config_to_json(config : FileRotation) -> Json {
|
||||
@file_model.file_rotation_config_to_json(config)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> Json {
|
||||
@config_model.sink_config_to_json(config)
|
||||
}
|
||||
|
||||
@@ -87,7 +83,7 @@ pub fn stringify_sink_config(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> Json {
|
||||
@config_model.logger_config_to_json(config)
|
||||
}
|
||||
|
||||
|
||||
+88
-116
@@ -170,102 +170,84 @@ pub fn default_logger_config() -> LoggerConfig {
|
||||
|
||||
///|
|
||||
fn expect_object(
|
||||
value : @json_parser.JsonValue,
|
||||
value : Json,
|
||||
context : String,
|
||||
) -> Map[String, @json_parser.JsonValue] raise ConfigError {
|
||||
match value.as_object() {
|
||||
Some(obj) => obj
|
||||
None => raise ConfigError::InvalidConfig("Expected object at " + context)
|
||||
) -> Map[String, Json] raise ConfigError {
|
||||
match value {
|
||||
Json::Object(obj) => obj
|
||||
_ => raise ConfigError::InvalidConfig("Expected object at " + context)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
obj : Map[String, Json],
|
||||
key : String,
|
||||
default? : String = "",
|
||||
) -> String raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => text
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
Some(Json::String(text)) => text
|
||||
Some(_) => raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_optional_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
obj : Map[String, Json],
|
||||
key : String,
|
||||
) -> String? raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => None
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => Some(text)
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
Some(Json::String(text)) => Some(text)
|
||||
Some(_) => raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_bool(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
obj : Map[String, Json],
|
||||
key : String,
|
||||
default~ : Bool,
|
||||
) -> Bool raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_bool() {
|
||||
Some(flag) => flag
|
||||
None => raise ConfigError::InvalidConfig("Expected bool at key " + key)
|
||||
}
|
||||
Some(Json::True) => true
|
||||
Some(Json::False) => false
|
||||
Some(_) => raise ConfigError::InvalidConfig("Expected bool at key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_int(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
obj : Map[String, Json],
|
||||
key : String,
|
||||
default~ : Int,
|
||||
) -> Int raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_number() {
|
||||
Some(number) => number.to_int()
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected number at key " + key)
|
||||
}
|
||||
Some(Json::Number(number, ..)) => number.to_int()
|
||||
Some(_) => raise ConfigError::InvalidConfig("Expected number at key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_optional_int64_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
obj : Map[String, Json],
|
||||
key : String,
|
||||
) -> Int64? raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => None
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) =>
|
||||
Some(
|
||||
@string.parse_int64(text) catch {
|
||||
_ =>
|
||||
raise ConfigError::InvalidConfig(
|
||||
"Expected int64 string at key " + key,
|
||||
)
|
||||
},
|
||||
)
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
Some(Json::String(text)) =>
|
||||
Some(
|
||||
@string.parse_int64(text) catch {
|
||||
_ =>
|
||||
raise ConfigError::InvalidConfig(
|
||||
"Expected int64 string at key " + key,
|
||||
)
|
||||
},
|
||||
)
|
||||
Some(_) => raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +337,7 @@ fn sink_kind_label(kind : SinkKind) -> String {
|
||||
|
||||
///|
|
||||
fn parse_text_formatter_config(
|
||||
value : @json_parser.JsonValue,
|
||||
value : Json,
|
||||
) -> TextFormatterConfig raise ConfigError {
|
||||
let obj = expect_object(value, "text_formatter")
|
||||
TextFormatterConfig::new(
|
||||
@@ -388,7 +370,7 @@ fn parse_text_formatter_config(
|
||||
|
||||
///|
|
||||
fn parse_text_style_config(
|
||||
value : @json_parser.JsonValue,
|
||||
value : Json,
|
||||
context : String,
|
||||
) -> @formatting.TextStyle raise ConfigError {
|
||||
let obj = expect_object(value, context)
|
||||
@@ -404,7 +386,7 @@ fn parse_text_style_config(
|
||||
|
||||
///|
|
||||
fn parse_style_tags_config(
|
||||
value : @json_parser.JsonValue,
|
||||
value : Json,
|
||||
) -> Map[String, @formatting.TextStyle] raise ConfigError {
|
||||
let obj = expect_object(value, "text_formatter.style_tags")
|
||||
let style_tags : Map[String, @formatting.TextStyle] = Map([])
|
||||
@@ -418,9 +400,7 @@ fn parse_style_tags_config(
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_queue_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> QueueConfig raise ConfigError {
|
||||
fn parse_queue_config(value : Json) -> QueueConfig raise ConfigError {
|
||||
let obj = expect_object(value, "queue")
|
||||
QueueConfig::new(
|
||||
get_int(obj, "max_pending", default=0),
|
||||
@@ -430,7 +410,7 @@ fn parse_queue_config(
|
||||
|
||||
///|
|
||||
fn parse_file_rotation_config(
|
||||
value : @json_parser.JsonValue,
|
||||
value : Json,
|
||||
) -> @file_model.FileRotation raise ConfigError {
|
||||
let obj = expect_object(value, "sink.rotation")
|
||||
let max_backups = get_int(obj, "max_backups", default=1)
|
||||
@@ -446,9 +426,7 @@ fn parse_file_rotation_config(
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_sink_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> SinkConfig raise ConfigError {
|
||||
fn parse_sink_config(value : Json) -> SinkConfig raise ConfigError {
|
||||
let obj = expect_object(value, "sink")
|
||||
let kind = parse_sink_kind(get_string(obj, "kind", default="console"))
|
||||
let formatter = match obj.get("text_formatter") {
|
||||
@@ -480,7 +458,7 @@ fn parse_sink_config(
|
||||
pub fn parse_logger_config_text(
|
||||
input : String,
|
||||
) -> LoggerConfig raise ConfigError {
|
||||
let root = @json_parser.parse(input) catch {
|
||||
let root = @json.parse(input) catch {
|
||||
e => raise ConfigError::InvalidConfig("Invalid JSON: " + e.to_string())
|
||||
}
|
||||
let obj = expect_object(root, "root")
|
||||
@@ -500,10 +478,10 @@ pub fn parse_logger_config_text(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
"max_pending": @json_parser.JsonValue::Number(queue.max_pending.to_double()),
|
||||
"overflow": @json_parser.JsonValue::String(
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> Json {
|
||||
Json::object({
|
||||
"max_pending": Json::number(queue.max_pending.to_double()),
|
||||
"overflow": Json::string(
|
||||
match queue.overflow {
|
||||
@queue_model.QueueOverflowPolicy::DropNewest => "DropNewest"
|
||||
@queue_model.QueueOverflowPolicy::DropOldest => "DropOldest"
|
||||
@@ -519,76 +497,70 @@ pub fn stringify_queue_config(
|
||||
) -> String {
|
||||
let value = queue_config_to_json(queue)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn text_formatter_config_to_json(
|
||||
config : TextFormatterConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"show_timestamp": @json_parser.JsonValue::Bool(config.show_timestamp),
|
||||
"show_level": @json_parser.JsonValue::Bool(config.show_level),
|
||||
"show_target": @json_parser.JsonValue::Bool(config.show_target),
|
||||
"show_fields": @json_parser.JsonValue::Bool(config.show_fields),
|
||||
"separator": @json_parser.JsonValue::String(config.separator),
|
||||
"field_separator": @json_parser.JsonValue::String(config.field_separator),
|
||||
"template": @json_parser.JsonValue::String(config.template),
|
||||
"color_mode": @json_parser.JsonValue::String(
|
||||
@formatting.color_mode_label(config.color_mode),
|
||||
),
|
||||
"color_support": @json_parser.JsonValue::String(
|
||||
pub fn text_formatter_config_to_json(config : TextFormatterConfig) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"show_timestamp": Json::boolean(config.show_timestamp),
|
||||
"show_level": Json::boolean(config.show_level),
|
||||
"show_target": Json::boolean(config.show_target),
|
||||
"show_fields": Json::boolean(config.show_fields),
|
||||
"separator": Json::string(config.separator),
|
||||
"field_separator": Json::string(config.field_separator),
|
||||
"template": Json::string(config.template),
|
||||
"color_mode": Json::string(@formatting.color_mode_label(config.color_mode)),
|
||||
"color_support": Json::string(
|
||||
@formatting.color_support_label(config.color_support),
|
||||
),
|
||||
"style_markup": @json_parser.JsonValue::String(
|
||||
"style_markup": Json::string(
|
||||
@formatting.style_markup_mode_label(config.style_markup),
|
||||
),
|
||||
"target_style_markup": @json_parser.JsonValue::String(
|
||||
"target_style_markup": Json::string(
|
||||
@formatting.style_markup_mode_label(config.target_style_markup),
|
||||
),
|
||||
"fields_style_markup": @json_parser.JsonValue::String(
|
||||
"fields_style_markup": Json::string(
|
||||
@formatting.style_markup_mode_label(config.fields_style_markup),
|
||||
),
|
||||
}
|
||||
if config.style_tags.length() != 0 {
|
||||
obj["style_tags"] = style_tags_config_to_json(config.style_tags)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
fn text_style_config_to_json(
|
||||
style : @formatting.TextStyle,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"bold": @json_parser.JsonValue::Bool(style.bold),
|
||||
"dim": @json_parser.JsonValue::Bool(style.dim),
|
||||
"italic": @json_parser.JsonValue::Bool(style.italic),
|
||||
"underline": @json_parser.JsonValue::Bool(style.underline),
|
||||
fn text_style_config_to_json(style : @formatting.TextStyle) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"bold": Json::boolean(style.bold),
|
||||
"dim": Json::boolean(style.dim),
|
||||
"italic": Json::boolean(style.italic),
|
||||
"underline": Json::boolean(style.underline),
|
||||
}
|
||||
match style.fg {
|
||||
Some(value) => obj["fg"] = @json_parser.JsonValue::String(value)
|
||||
Some(value) => obj["fg"] = Json::string(value)
|
||||
None => ()
|
||||
}
|
||||
match style.bg {
|
||||
Some(value) => obj["bg"] = @json_parser.JsonValue::String(value)
|
||||
Some(value) => obj["bg"] = Json::string(value)
|
||||
None => ()
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
fn style_tags_config_to_json(
|
||||
style_tags : Map[String, @formatting.TextStyle],
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = Map([])
|
||||
) -> Json {
|
||||
let obj : Map[String, Json] = Map([])
|
||||
for name, style in style_tags {
|
||||
obj[name] = text_style_config_to_json(style)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -598,19 +570,19 @@ pub fn stringify_text_formatter_config(
|
||||
) -> String {
|
||||
let value = text_formatter_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"kind": @json_parser.JsonValue::String(sink_kind_label(config.kind)),
|
||||
"path": @json_parser.JsonValue::String(config.path),
|
||||
"append": @json_parser.JsonValue::Bool(config.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(config.auto_flush),
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"kind": Json::string(sink_kind_label(config.kind)),
|
||||
"path": Json::string(config.path),
|
||||
"append": Json::boolean(config.append),
|
||||
"auto_flush": Json::boolean(config.auto_flush),
|
||||
"text_formatter": text_formatter_config_to_json(config.text_formatter),
|
||||
}
|
||||
match config.rotation {
|
||||
@@ -618,7 +590,7 @@ pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
|
||||
Some(rotation) =>
|
||||
obj["rotation"] = @file_model.file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -628,25 +600,25 @@ pub fn stringify_sink_config(
|
||||
) -> String {
|
||||
let value = sink_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"min_level": @json_parser.JsonValue::String(config.min_level.label()),
|
||||
"target": @json_parser.JsonValue::String(config.target),
|
||||
"timestamp": @json_parser.JsonValue::Bool(config.timestamp),
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"min_level": Json::string(config.min_level.label()),
|
||||
"target": Json::string(config.target),
|
||||
"timestamp": Json::boolean(config.timestamp),
|
||||
"sink": sink_config_to_json(config.sink),
|
||||
}
|
||||
match config.queue {
|
||||
None => ()
|
||||
Some(queue) => obj["queue"] = queue_config_to_json(queue)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -656,8 +628,8 @@ pub fn stringify_logger_config(
|
||||
) -> String {
|
||||
let value = logger_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/file_model",
|
||||
"Nanaloveyuki/BitLogger/src/formatting",
|
||||
"Nanaloveyuki/BitLogger/src/queue_model",
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/json",
|
||||
"moonbitlang/core/string",
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/file_model",
|
||||
"Nanaloveyuki/BitLogger/src/formatting",
|
||||
"Nanaloveyuki/BitLogger/src/queue_model",
|
||||
"maria/json_parser",
|
||||
}
|
||||
|
||||
// Values
|
||||
@@ -16,13 +15,13 @@ pub fn default_sink_config() -> SinkConfig
|
||||
|
||||
pub fn default_text_formatter_config() -> TextFormatterConfig
|
||||
|
||||
pub fn logger_config_to_json(LoggerConfig) -> @json_parser.JsonValue
|
||||
pub fn logger_config_to_json(LoggerConfig) -> Json
|
||||
|
||||
pub fn parse_logger_config_text(String) -> LoggerConfig raise ConfigError
|
||||
|
||||
pub fn queue_config_to_json(QueueConfig) -> @json_parser.JsonValue
|
||||
pub fn queue_config_to_json(QueueConfig) -> Json
|
||||
|
||||
pub fn sink_config_to_json(SinkConfig) -> @json_parser.JsonValue
|
||||
pub fn sink_config_to_json(SinkConfig) -> Json
|
||||
|
||||
pub fn stringify_logger_config(LoggerConfig, pretty? : Bool) -> String
|
||||
|
||||
@@ -32,7 +31,7 @@ pub fn stringify_sink_config(SinkConfig, pretty? : Bool) -> String
|
||||
|
||||
pub fn stringify_text_formatter_config(TextFormatterConfig, pretty? : Bool) -> String
|
||||
|
||||
pub fn text_formatter_config_to_json(TextFormatterConfig) -> @json_parser.JsonValue
|
||||
pub fn text_formatter_config_to_json(TextFormatterConfig) -> Json
|
||||
|
||||
// Errors
|
||||
pub(all) suberror ConfigError {
|
||||
|
||||
@@ -39,6 +39,15 @@ pub fn Record::with_fields(Self, Array[Field]) -> Self
|
||||
pub fn Record::with_message(Self, String) -> Self
|
||||
pub fn Record::with_target(Self, String) -> Self
|
||||
|
||||
pub struct TraceContext {
|
||||
trace_id : String
|
||||
span_id : String
|
||||
trace_flags : String
|
||||
trace_state : String?
|
||||
}
|
||||
pub fn TraceContext::as_fields(Self) -> Array[Field]
|
||||
pub fn TraceContext::new(String, String, trace_flags? : String, trace_state? : String) -> Self
|
||||
|
||||
// Type aliases
|
||||
|
||||
// Traits
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
///|
|
||||
pub struct TraceContext {
|
||||
trace_id : String
|
||||
span_id : String
|
||||
trace_flags : String
|
||||
trace_state : String?
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn TraceContext::new(
|
||||
trace_id : String,
|
||||
span_id : String,
|
||||
trace_flags? : String = "01",
|
||||
trace_state? : String,
|
||||
) -> TraceContext {
|
||||
{ trace_id, span_id, trace_flags, trace_state }
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn TraceContext::as_fields(self : TraceContext) -> Array[Field] {
|
||||
let fields = [
|
||||
field("trace_id", self.trace_id),
|
||||
field("span_id", self.span_id),
|
||||
field("trace_flags", self.trace_flags),
|
||||
]
|
||||
match self.trace_state {
|
||||
Some(value) => fields.push(field("trace_state", value))
|
||||
None => ()
|
||||
}
|
||||
fields
|
||||
}
|
||||
@@ -1,34 +1,27 @@
|
||||
///|
|
||||
pub fn file_rotation_config_to_json(
|
||||
config : FileRotation,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"max_bytes": @json_parser.JsonValue::Number(config.max_bytes.to_double()),
|
||||
"max_backups": @json_parser.JsonValue::Number(
|
||||
config.max_backups.to_double(),
|
||||
),
|
||||
pub fn file_rotation_config_to_json(config : FileRotation) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"max_bytes": Json::number(config.max_bytes.to_double()),
|
||||
"max_backups": Json::number(config.max_backups.to_double()),
|
||||
}
|
||||
match config.native_wide_max_bytes {
|
||||
Some(value) =>
|
||||
obj["max_bytes_i64"] = @json_parser.JsonValue::String(value.to_string())
|
||||
Some(value) => obj["max_bytes_i64"] = Json::string(value.to_string())
|
||||
None => ()
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_policy_to_json(
|
||||
policy : FileSinkPolicy,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"append": @json_parser.JsonValue::Bool(policy.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(policy.auto_flush),
|
||||
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"append": Json::boolean(policy.append),
|
||||
"auto_flush": Json::boolean(policy.auto_flush),
|
||||
}
|
||||
match policy.rotation {
|
||||
None => obj["rotation"] = @json_parser.JsonValue::Null
|
||||
None => obj["rotation"] = Json::null()
|
||||
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -38,37 +31,29 @@ pub fn stringify_file_sink_policy(
|
||||
) -> String {
|
||||
let value = file_sink_policy_to_json(policy)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"path": @json_parser.JsonValue::String(state.path),
|
||||
"available": @json_parser.JsonValue::Bool(state.available),
|
||||
"append": @json_parser.JsonValue::Bool(state.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(state.auto_flush),
|
||||
"open_failures": @json_parser.JsonValue::Number(
|
||||
state.open_failures.to_double(),
|
||||
),
|
||||
"write_failures": @json_parser.JsonValue::Number(
|
||||
state.write_failures.to_double(),
|
||||
),
|
||||
"flush_failures": @json_parser.JsonValue::Number(
|
||||
state.flush_failures.to_double(),
|
||||
),
|
||||
"rotation_failures": @json_parser.JsonValue::Number(
|
||||
state.rotation_failures.to_double(),
|
||||
),
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> Json {
|
||||
let obj : Map[String, Json] = {
|
||||
"path": Json::string(state.path),
|
||||
"available": Json::boolean(state.available),
|
||||
"append": Json::boolean(state.append),
|
||||
"auto_flush": Json::boolean(state.auto_flush),
|
||||
"open_failures": Json::number(state.open_failures.to_double()),
|
||||
"write_failures": Json::number(state.write_failures.to_double()),
|
||||
"flush_failures": Json::number(state.flush_failures.to_double()),
|
||||
"rotation_failures": Json::number(state.rotation_failures.to_double()),
|
||||
}
|
||||
match state.rotation {
|
||||
None => obj["rotation"] = @json_parser.JsonValue::Null
|
||||
None => obj["rotation"] = Json::null()
|
||||
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
Json::object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -78,8 +63,8 @@ pub fn stringify_file_sink_state(
|
||||
) -> String {
|
||||
let value = file_sink_state_to_json(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import {
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/json",
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
// Generated using `moon info`, DON'T EDIT IT
|
||||
package "Nanaloveyuki/BitLogger/src/file_model"
|
||||
|
||||
import {
|
||||
"maria/json_parser",
|
||||
}
|
||||
|
||||
// Values
|
||||
pub fn file_rotation(Int, max_backups? : Int) -> FileRotation
|
||||
|
||||
pub fn file_rotation_config_to_json(FileRotation) -> @json_parser.JsonValue
|
||||
pub fn file_rotation_config_to_json(FileRotation) -> Json
|
||||
|
||||
pub fn file_rotation_i64(Int64, max_backups? : Int) -> FileRotation
|
||||
|
||||
pub fn file_sink_policy_to_json(FileSinkPolicy) -> @json_parser.JsonValue
|
||||
pub fn file_sink_policy_to_json(FileSinkPolicy) -> Json
|
||||
|
||||
pub fn file_sink_state_to_json(FileSinkState) -> @json_parser.JsonValue
|
||||
pub fn file_sink_state_to_json(FileSinkState) -> Json
|
||||
|
||||
pub fn runtime_file_state_to_json(RuntimeFileState) -> @json_parser.JsonValue
|
||||
pub fn runtime_file_state_to_json(RuntimeFileState) -> Json
|
||||
|
||||
pub fn stringify_file_sink_policy(FileSinkPolicy, pretty? : Bool) -> String
|
||||
|
||||
|
||||
@@ -17,18 +17,12 @@ pub fn RuntimeFileState::new(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn runtime_file_state_to_json(
|
||||
state : RuntimeFileState,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> Json {
|
||||
Json::object({
|
||||
"file": file_sink_state_to_json(state.file),
|
||||
"queued": @json_parser.JsonValue::Bool(state.queued),
|
||||
"pending_count": @json_parser.JsonValue::Number(
|
||||
state.pending_count.to_double(),
|
||||
),
|
||||
"dropped_count": @json_parser.JsonValue::Number(
|
||||
state.dropped_count.to_double(),
|
||||
),
|
||||
"queued": Json::boolean(state.queued),
|
||||
"pending_count": Json::number(state.pending_count.to_double()),
|
||||
"dropped_count": Json::number(state.dropped_count.to_double()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,8 +33,8 @@ pub fn stringify_runtime_file_state(
|
||||
) -> String {
|
||||
let value = runtime_file_state_to_json(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
value.stringify(indent=2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
value.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
///|
|
||||
fn Json::expect_json_object(self : Json) -> Map[String, Json] {
|
||||
match self {
|
||||
Json::Object(value) => value
|
||||
_ => abort("Expected JSON object")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_number(self : Json) -> Double {
|
||||
match self {
|
||||
Json::Number(value, ..) => value
|
||||
_ => abort("Expected JSON number")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_string(self : Json) -> String {
|
||||
match self {
|
||||
Json::String(value) => value
|
||||
_ => abort("Expected JSON string")
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn Json::expect_json_bool(self : Json) -> Bool {
|
||||
match self {
|
||||
Json::True => true
|
||||
Json::False => false
|
||||
_ => abort("Expected JSON bool")
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,14 @@ pub fn[S] LibraryLogger::bind(
|
||||
self.with_context_fields(fields)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] LibraryLogger::with_trace_context(
|
||||
self : LibraryLogger[S],
|
||||
context : TraceContext,
|
||||
) -> LibraryLogger[ContextSink[S]] {
|
||||
library_logger(self.inner.with_trace_context(context))
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] LibraryLogger::is_enabled(
|
||||
self : LibraryLogger[S],
|
||||
|
||||
@@ -57,6 +57,14 @@ pub fn[S] Logger::bind(
|
||||
self.with_context_fields(fields)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] Logger::with_trace_context(
|
||||
self : Logger[S],
|
||||
context : TraceContext,
|
||||
) -> Logger[ContextSink[S]] {
|
||||
self.with_context_fields(context.as_fields())
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn[S] Logger::with_filter(
|
||||
self : Logger[S],
|
||||
|
||||
@@ -43,11 +43,7 @@ test "config subtype json helpers stringify stable shapes" {
|
||||
content="{\"kind\":\"file\",\"path\":\"logs/demo.log\",\"append\":false,\"auto_flush\":false,\"text_formatter\":{\"show_timestamp\":false,\"show_level\":true,\"show_target\":true,\"show_fields\":true,\"separator\":\" \",\"field_separator\":\" \",\"template\":\"\",\"color_mode\":\"auto\",\"color_support\":\"truecolor\",\"style_markup\":\"full\",\"target_style_markup\":\"disabled\",\"fields_style_markup\":\"disabled\"},\"rotation\":{\"max_bytes\":128,\"max_backups\":2}}",
|
||||
)
|
||||
inspect(
|
||||
@json_parser.stringify(
|
||||
file_rotation_config_to_json(
|
||||
file_rotation_i64(4294967296L, max_backups=2),
|
||||
),
|
||||
),
|
||||
file_rotation_config_to_json(file_rotation_i64(4294967296L, max_backups=2)).stringify(),
|
||||
content="{\"max_bytes\":2147483647,\"max_backups\":2,\"max_bytes_i64\":\"4294967296\"}",
|
||||
)
|
||||
}
|
||||
@@ -57,17 +53,17 @@ test "config json helpers export stable structured shapes" {
|
||||
let queue_json = queue_config_to_json(
|
||||
QueueConfig::new(8, overflow=QueueOverflowPolicy::DropOldest),
|
||||
)
|
||||
let queue_obj = queue_json.as_object().unwrap()
|
||||
let queue_obj = queue_json.expect_json_object()
|
||||
inspect(
|
||||
@json_parser.stringify(queue_json),
|
||||
queue_json.stringify(),
|
||||
content="{\"max_pending\":8,\"overflow\":\"DropOldest\"}",
|
||||
)
|
||||
inspect(
|
||||
queue_obj.get("max_pending").unwrap().as_number().unwrap().to_int(),
|
||||
queue_obj.get("max_pending").unwrap().expect_json_number().to_int(),
|
||||
content="8",
|
||||
)
|
||||
inspect(
|
||||
queue_obj.get("overflow").unwrap().as_string().unwrap(),
|
||||
queue_obj.get("overflow").unwrap().expect_json_string(),
|
||||
content="DropOldest",
|
||||
)
|
||||
|
||||
@@ -88,55 +84,54 @@ test "config json helpers export stable structured shapes" {
|
||||
style_tags={ "accent": text_style(fg=Some("#4cc9f0"), bold=true) },
|
||||
),
|
||||
)
|
||||
let formatter_obj = formatter_json.as_object().unwrap()
|
||||
let formatter_obj = formatter_json.expect_json_object()
|
||||
let style_tags_obj = formatter_obj
|
||||
.get("style_tags")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
let accent_obj = style_tags_obj.get("accent").unwrap().as_object().unwrap()
|
||||
.expect_json_object()
|
||||
let accent_obj = style_tags_obj.get("accent").unwrap().expect_json_object()
|
||||
inspect(
|
||||
formatter_obj.get("show_timestamp").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_timestamp").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_target").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_target").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("separator").unwrap().expect_json_string(),
|
||||
content=" | ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("field_separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("field_separator").unwrap().expect_json_string(),
|
||||
content=",",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("template").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("template").unwrap().expect_json_string(),
|
||||
content="[{level}] {message} :: {fields}",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_mode").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_mode").unwrap().expect_json_string(),
|
||||
content="always",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_support").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_support").unwrap().expect_json_string(),
|
||||
content="basic",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("style_markup").unwrap().expect_json_string(),
|
||||
content="builtin",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("target_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("target_style_markup").unwrap().expect_json_string(),
|
||||
content="builtin",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("fields_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("fields_style_markup").unwrap().expect_json_string(),
|
||||
content="disabled",
|
||||
)
|
||||
inspect(accent_obj.get("bold").unwrap().as_bool().unwrap(), content="true")
|
||||
inspect(accent_obj.get("fg").unwrap().as_string().unwrap(), content="#4cc9f0")
|
||||
inspect(accent_obj.get("bold").unwrap().expect_json_bool(), content="true")
|
||||
inspect(accent_obj.get("fg").unwrap().expect_json_string(), content="#4cc9f0")
|
||||
|
||||
let sink_json = sink_config_to_json(
|
||||
SinkConfig::new(
|
||||
@@ -151,37 +146,36 @@ test "config json helpers export stable structured shapes" {
|
||||
),
|
||||
),
|
||||
)
|
||||
let sink_obj = sink_json.as_object().unwrap()
|
||||
let sink_obj = sink_json.expect_json_object()
|
||||
let sink_formatter_obj = sink_obj
|
||||
.get("text_formatter")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
let rotation_obj = sink_obj.get("rotation").unwrap().as_object().unwrap()
|
||||
inspect(sink_obj.get("kind").unwrap().as_string().unwrap(), content="file")
|
||||
.expect_json_object()
|
||||
let rotation_obj = sink_obj.get("rotation").unwrap().expect_json_object()
|
||||
inspect(sink_obj.get("kind").unwrap().expect_json_string(), content="file")
|
||||
inspect(
|
||||
sink_obj.get("path").unwrap().as_string().unwrap(),
|
||||
sink_obj.get("path").unwrap().expect_json_string(),
|
||||
content="logs/demo.log",
|
||||
)
|
||||
inspect(sink_obj.get("append").unwrap().as_bool().unwrap(), content="false")
|
||||
inspect(sink_obj.get("append").unwrap().expect_json_bool(), content="false")
|
||||
inspect(
|
||||
sink_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
sink_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(
|
||||
sink_formatter_obj.get("show_timestamp").unwrap().as_bool().unwrap(),
|
||||
sink_formatter_obj.get("show_timestamp").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(
|
||||
sink_formatter_obj.get("color_mode").unwrap().as_string().unwrap(),
|
||||
sink_formatter_obj.get("color_mode").unwrap().expect_json_string(),
|
||||
content="auto",
|
||||
)
|
||||
inspect(
|
||||
rotation_obj.get("max_bytes").unwrap().as_number().unwrap().to_int(),
|
||||
rotation_obj.get("max_bytes").unwrap().expect_json_number().to_int(),
|
||||
content="128",
|
||||
)
|
||||
inspect(
|
||||
rotation_obj.get("max_backups").unwrap().as_number().unwrap().to_int(),
|
||||
rotation_obj.get("max_backups").unwrap().expect_json_number().to_int(),
|
||||
content="2",
|
||||
)
|
||||
inspect(rotation_obj.get("max_bytes_i64") is None, content="true")
|
||||
@@ -189,17 +183,17 @@ test "config json helpers export stable structured shapes" {
|
||||
let wide_rotation_json = file_rotation_config_to_json(
|
||||
file_rotation_i64(4294967296L, max_backups=3),
|
||||
)
|
||||
let wide_rotation_obj = wide_rotation_json.as_object().unwrap()
|
||||
let wide_rotation_obj = wide_rotation_json.expect_json_object()
|
||||
inspect(
|
||||
wide_rotation_obj.get("max_bytes").unwrap().as_number().unwrap().to_int(),
|
||||
wide_rotation_obj.get("max_bytes").unwrap().expect_json_number().to_int(),
|
||||
content="2147483647",
|
||||
)
|
||||
inspect(
|
||||
wide_rotation_obj.get("max_backups").unwrap().as_number().unwrap().to_int(),
|
||||
wide_rotation_obj.get("max_backups").unwrap().expect_json_number().to_int(),
|
||||
content="3",
|
||||
)
|
||||
inspect(
|
||||
wide_rotation_obj.get("max_bytes_i64").unwrap().as_string().unwrap(),
|
||||
wide_rotation_obj.get("max_bytes_i64").unwrap().expect_json_string(),
|
||||
content="4294967296",
|
||||
)
|
||||
|
||||
@@ -212,28 +206,28 @@ test "config json helpers export stable structured shapes" {
|
||||
queue=Some(QueueConfig::new(8, overflow=QueueOverflowPolicy::DropNewest)),
|
||||
),
|
||||
)
|
||||
let logger_obj = logger_json.as_object().unwrap()
|
||||
let logger_sink_obj = logger_obj.get("sink").unwrap().as_object().unwrap()
|
||||
let logger_queue_obj = logger_obj.get("queue").unwrap().as_object().unwrap()
|
||||
let logger_obj = logger_json.expect_json_object()
|
||||
let logger_sink_obj = logger_obj.get("sink").unwrap().expect_json_object()
|
||||
let logger_queue_obj = logger_obj.get("queue").unwrap().expect_json_object()
|
||||
inspect(
|
||||
logger_obj.get("min_level").unwrap().as_string().unwrap(),
|
||||
logger_obj.get("min_level").unwrap().expect_json_string(),
|
||||
content="WARN",
|
||||
)
|
||||
inspect(logger_obj.get("target").unwrap().as_string().unwrap(), content="api")
|
||||
inspect(logger_obj.get("target").unwrap().expect_json_string(), content="api")
|
||||
inspect(
|
||||
logger_obj.get("timestamp").unwrap().as_bool().unwrap(),
|
||||
logger_obj.get("timestamp").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
logger_sink_obj.get("kind").unwrap().as_string().unwrap(),
|
||||
logger_sink_obj.get("kind").unwrap().expect_json_string(),
|
||||
content="text_console",
|
||||
)
|
||||
inspect(
|
||||
logger_queue_obj.get("max_pending").unwrap().as_number().unwrap().to_int(),
|
||||
logger_queue_obj.get("max_pending").unwrap().expect_json_number().to_int(),
|
||||
content="8",
|
||||
)
|
||||
inspect(
|
||||
logger_queue_obj.get("overflow").unwrap().as_string().unwrap(),
|
||||
logger_queue_obj.get("overflow").unwrap().expect_json_string(),
|
||||
content="DropNewest",
|
||||
)
|
||||
}
|
||||
@@ -242,81 +236,80 @@ test "config json helpers export stable structured shapes" {
|
||||
test "logger config json export materializes parsed omitted defaults" {
|
||||
let parsed = parse_logger_config_text("{}")
|
||||
let logger_json = logger_config_to_json(parsed)
|
||||
let logger_obj = logger_json.as_object().unwrap()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().as_object().unwrap()
|
||||
let logger_obj = logger_json.expect_json_object()
|
||||
let sink_obj = logger_obj.get("sink").unwrap().expect_json_object()
|
||||
let formatter_obj = sink_obj
|
||||
.get("text_formatter")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
|
||||
inspect(
|
||||
@json_parser.stringify(logger_json),
|
||||
logger_json.stringify(),
|
||||
content="{\"min_level\":\"INFO\",\"target\":\"\",\"timestamp\":false,\"sink\":{\"kind\":\"console\",\"path\":\"\",\"append\":true,\"auto_flush\":true,\"text_formatter\":{\"show_timestamp\":true,\"show_level\":true,\"show_target\":true,\"show_fields\":true,\"separator\":\" \",\"field_separator\":\" \",\"template\":\"\",\"color_mode\":\"never\",\"color_support\":\"truecolor\",\"style_markup\":\"full\",\"target_style_markup\":\"disabled\",\"fields_style_markup\":\"disabled\"}}}",
|
||||
)
|
||||
inspect(
|
||||
logger_obj.get("min_level").unwrap().as_string().unwrap(),
|
||||
logger_obj.get("min_level").unwrap().expect_json_string(),
|
||||
content="INFO",
|
||||
)
|
||||
inspect(logger_obj.get("target").unwrap().as_string().unwrap(), content="")
|
||||
inspect(logger_obj.get("target").unwrap().expect_json_string(), content="")
|
||||
inspect(
|
||||
logger_obj.get("timestamp").unwrap().as_bool().unwrap(),
|
||||
logger_obj.get("timestamp").unwrap().expect_json_bool(),
|
||||
content="false",
|
||||
)
|
||||
inspect(logger_obj.get("queue") is None, content="true")
|
||||
inspect(sink_obj.get("kind").unwrap().as_string().unwrap(), content="console")
|
||||
inspect(sink_obj.get("path").unwrap().as_string().unwrap(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().as_bool().unwrap(), content="true")
|
||||
inspect(sink_obj.get("kind").unwrap().expect_json_string(), content="console")
|
||||
inspect(sink_obj.get("path").unwrap().expect_json_string(), content="")
|
||||
inspect(sink_obj.get("append").unwrap().expect_json_bool(), content="true")
|
||||
inspect(
|
||||
sink_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
sink_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_timestamp").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_timestamp").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_level").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_level").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_target").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_target").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("show_fields").unwrap().as_bool().unwrap(),
|
||||
formatter_obj.get("show_fields").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("field_separator").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("field_separator").unwrap().expect_json_string(),
|
||||
content=" ",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("template").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("template").unwrap().expect_json_string(),
|
||||
content="",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_mode").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_mode").unwrap().expect_json_string(),
|
||||
content="never",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("color_support").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("color_support").unwrap().expect_json_string(),
|
||||
content="truecolor",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("style_markup").unwrap().expect_json_string(),
|
||||
content="full",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("target_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("target_style_markup").unwrap().expect_json_string(),
|
||||
content="disabled",
|
||||
)
|
||||
inspect(
|
||||
formatter_obj.get("fields_style_markup").unwrap().as_string().unwrap(),
|
||||
formatter_obj.get("fields_style_markup").unwrap().expect_json_string(),
|
||||
content="disabled",
|
||||
)
|
||||
inspect(formatter_obj.get("style_tags") is None, content="true")
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/queue_model",
|
||||
"Nanaloveyuki/BitLogger/src/runtime",
|
||||
"Nanaloveyuki/BitLogger/src/sink_graph",
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/array",
|
||||
"moonbitlang/core/builtin",
|
||||
"moonbitlang/core/env",
|
||||
|
||||
+14
-9
@@ -10,7 +10,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/queue_model",
|
||||
"Nanaloveyuki/BitLogger/src/runtime",
|
||||
"Nanaloveyuki/BitLogger/src/sink_graph",
|
||||
"maria/json_parser",
|
||||
}
|
||||
|
||||
// Values
|
||||
@@ -70,15 +69,15 @@ pub fn file(String, min_level? : @core.Level, target? : String, timestamp? : Boo
|
||||
|
||||
pub fn file_rotation(Int, max_backups? : Int) -> @file_model.FileRotation
|
||||
|
||||
pub fn file_rotation_config_to_json(@file_model.FileRotation) -> @json_parser.JsonValue
|
||||
pub fn file_rotation_config_to_json(@file_model.FileRotation) -> Json
|
||||
|
||||
pub fn file_rotation_i64(Int64, max_backups? : Int) -> @file_model.FileRotation
|
||||
|
||||
pub fn file_sink(String, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, formatter? : (@core.Record) -> String) -> @file_runtime.FileSink
|
||||
|
||||
pub fn file_sink_policy_to_json(@file_model.FileSinkPolicy) -> @json_parser.JsonValue
|
||||
pub fn file_sink_policy_to_json(@file_model.FileSinkPolicy) -> Json
|
||||
|
||||
pub fn file_sink_state_to_json(@file_model.FileSinkState) -> @json_parser.JsonValue
|
||||
pub fn file_sink_state_to_json(@file_model.FileSinkState) -> Json
|
||||
|
||||
pub fn[S] filter_sink(S, (@core.Record) -> Bool) -> @sink_graph.FilterSink[S]
|
||||
|
||||
@@ -106,7 +105,7 @@ pub fn level_at_least(@core.Level) -> (@core.Record) -> Bool
|
||||
|
||||
pub fn log(@core.Level, String, fields? : Array[@core.Field]) -> Unit
|
||||
|
||||
pub fn logger_config_to_json(@config_model.LoggerConfig) -> @json_parser.JsonValue
|
||||
pub fn logger_config_to_json(@config_model.LoggerConfig) -> Json
|
||||
|
||||
pub fn message_contains(String) -> (@core.Record) -> Bool
|
||||
|
||||
@@ -126,7 +125,7 @@ pub fn[S] patch_sink(S, (@core.Record) -> @core.Record) -> @sink_graph.PatchSink
|
||||
|
||||
pub fn prefix_message(String) -> (@core.Record) -> @core.Record
|
||||
|
||||
pub fn queue_config_to_json(@config_model.QueueConfig) -> @json_parser.JsonValue
|
||||
pub fn queue_config_to_json(@config_model.QueueConfig) -> Json
|
||||
|
||||
pub fn[S] queued_sink(S, max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> @sink_graph.QueuedSink[S]
|
||||
|
||||
@@ -136,7 +135,7 @@ pub fn redact_fields(Array[String], placeholder? : String) -> (@core.Record) ->
|
||||
|
||||
pub fn reset_global_style_tag_registry() -> Unit
|
||||
|
||||
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> @json_parser.JsonValue
|
||||
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> Json
|
||||
|
||||
pub fn set_default_min_level(@core.Level) -> Unit
|
||||
|
||||
@@ -146,7 +145,7 @@ pub fn set_global_style_tag_registry(@formatting.StyleTagRegistry) -> Unit
|
||||
|
||||
pub fn set_target(String) -> (@core.Record) -> @core.Record
|
||||
|
||||
pub fn sink_config_to_json(@config_model.SinkConfig) -> @json_parser.JsonValue
|
||||
pub fn sink_config_to_json(@config_model.SinkConfig) -> Json
|
||||
|
||||
pub fn[A, B] split_by_level(A, B, min_level? : @core.Level) -> @sink_graph.SplitSink[A, B]
|
||||
|
||||
@@ -182,12 +181,14 @@ pub fn text_console_sink(@formatting.TextFormatter) -> @sink_graph.FormattedCons
|
||||
|
||||
pub fn text_formatter(show_timestamp? : Bool, show_level? : Bool, show_target? : Bool, show_fields? : Bool, separator? : String, field_separator? : String, template? : String, color_mode? : @formatting.ColorMode, color_support? : @formatting.ColorSupport, style_markup? : @formatting.StyleMarkupMode, target_style_markup? : @formatting.StyleMarkupMode, fields_style_markup? : @formatting.StyleMarkupMode, style_tags? : @formatting.StyleTagRegistry?) -> @formatting.TextFormatter
|
||||
|
||||
pub fn text_formatter_config_to_json(@config_model.TextFormatterConfig) -> @json_parser.JsonValue
|
||||
pub fn text_formatter_config_to_json(@config_model.TextFormatterConfig) -> Json
|
||||
|
||||
pub fn text_style(fg? : String?, bg? : String?, bold? : Bool, dim? : Bool, italic? : Bool, underline? : Bool) -> @formatting.TextStyle
|
||||
|
||||
pub fn trace(String, fields? : Array[@core.Field]) -> Unit
|
||||
|
||||
pub fn trace_context(String, String, trace_flags? : String, trace_state? : String) -> @core.TraceContext
|
||||
|
||||
pub fn warn(String, fields? : Array[@core.Field]) -> Unit
|
||||
|
||||
pub fn with_file_rotation(@config_model.LoggerConfig, Int, max_backups? : Int) -> @config_model.LoggerConfig
|
||||
@@ -213,6 +214,7 @@ pub fn[S] LibraryLogger::to_logger(Self[S]) -> Logger[S]
|
||||
pub fn[S : @sink_graph.Sink] LibraryLogger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
|
||||
pub fn[S] LibraryLogger::with_context_fields(Self[S], Array[@core.Field]) -> Self[@sink_graph.ContextSink[S]]
|
||||
pub fn[S] LibraryLogger::with_target(Self[S], String) -> Self[S]
|
||||
pub fn[S] LibraryLogger::with_trace_context(Self[S], @core.TraceContext) -> Self[@sink_graph.ContextSink[S]]
|
||||
|
||||
pub struct Logger[S] {
|
||||
min_level : @core.Level
|
||||
@@ -277,6 +279,7 @@ pub fn[S] Logger::with_patch(Self[S], (@core.Record) -> @core.Record) -> Self[@s
|
||||
pub fn[S] Logger::with_queue(Self[S], max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> Self[@sink_graph.QueuedSink[S]]
|
||||
pub fn[S] Logger::with_target(Self[S], String) -> Self[S]
|
||||
pub fn[S] Logger::with_timestamp(Self[S], enabled? : Bool) -> Self[S]
|
||||
pub fn[S] Logger::with_trace_context(Self[S], @core.TraceContext) -> Self[@sink_graph.ContextSink[S]]
|
||||
|
||||
// Type aliases
|
||||
pub type ApplicationLogger = Logger[@runtime.RuntimeSink]
|
||||
@@ -359,6 +362,8 @@ pub using @config_model {type TextFormatterConfig}
|
||||
|
||||
pub using @formatting {type TextStyle}
|
||||
|
||||
pub using @core {type TraceContext}
|
||||
|
||||
pub using @sink_graph {trait Sink}
|
||||
|
||||
// Traits
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
///|
|
||||
pub type Field = @core.Field
|
||||
|
||||
///|
|
||||
pub type TraceContext = @core.TraceContext
|
||||
|
||||
///|
|
||||
pub fn field(key : String, value : String) -> Field {
|
||||
@core.field(key, value)
|
||||
@@ -11,6 +14,25 @@ pub fn fields(entries : Array[(String, String)]) -> Array[Field] {
|
||||
@core.fields(entries)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn trace_context(
|
||||
trace_id : String,
|
||||
span_id : String,
|
||||
trace_flags? : String = "01",
|
||||
trace_state? : String,
|
||||
) -> TraceContext {
|
||||
match trace_state {
|
||||
Some(value) =>
|
||||
@core.TraceContext::new(
|
||||
trace_id,
|
||||
span_id,
|
||||
trace_flags~,
|
||||
trace_state=value,
|
||||
)
|
||||
None => @core.TraceContext::new(trace_id, span_id, trace_flags~)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub type Record = @core.Record
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/file_model",
|
||||
"Nanaloveyuki/BitLogger/src/formatting",
|
||||
"Nanaloveyuki/BitLogger/src/sink_graph",
|
||||
"maria/json_parser",
|
||||
"moonbitlang/core/json",
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
"Nanaloveyuki/BitLogger/src/file_model",
|
||||
"Nanaloveyuki/BitLogger/src/file_runtime",
|
||||
"Nanaloveyuki/BitLogger/src/sink_graph",
|
||||
"maria/json_parser",
|
||||
}
|
||||
|
||||
// Values
|
||||
@@ -15,7 +14,7 @@ pub fn apply_queue_config(RuntimeSink, @config_model.QueueConfig) -> RuntimeSink
|
||||
|
||||
pub fn build_runtime_sink(@config_model.SinkConfig) -> RuntimeSink
|
||||
|
||||
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> @json_parser.JsonValue
|
||||
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> Json
|
||||
|
||||
pub fn stringify_runtime_file_state(@file_model.RuntimeFileState, pretty? : Bool) -> String
|
||||
|
||||
|
||||
@@ -55,9 +55,7 @@ pub struct RuntimeSinkProgress {
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn runtime_file_state_to_json(
|
||||
state : RuntimeFileState,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> Json {
|
||||
@file_model.runtime_file_state_to_json(state)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@ test "file state json helpers stringify stable snapshots" {
|
||||
let rotation_json = file_rotation_config_to_json(
|
||||
file_rotation(64, max_backups=2),
|
||||
)
|
||||
let rotation_obj = rotation_json.as_object().unwrap()
|
||||
let rotation_obj = rotation_json.expect_json_object()
|
||||
inspect(
|
||||
@json_parser.stringify(rotation_json),
|
||||
rotation_json.stringify(),
|
||||
content="{\"max_bytes\":64,\"max_backups\":2}",
|
||||
)
|
||||
inspect(
|
||||
rotation_obj.get("max_bytes").unwrap().as_number().unwrap().to_int(),
|
||||
rotation_obj.get("max_bytes").unwrap().expect_json_number().to_int(),
|
||||
content="64",
|
||||
)
|
||||
inspect(
|
||||
rotation_obj.get("max_backups").unwrap().as_number().unwrap().to_int(),
|
||||
rotation_obj.get("max_backups").unwrap().expect_json_number().to_int(),
|
||||
content="2",
|
||||
)
|
||||
inspect(rotation_obj.get("max_bytes_i64") is None, content="true")
|
||||
@@ -21,9 +21,9 @@ test "file state json helpers stringify stable snapshots" {
|
||||
let wide_rotation_json = file_rotation_config_to_json(
|
||||
file_rotation_i64(4294967296L, max_backups=2),
|
||||
)
|
||||
let wide_rotation_obj = wide_rotation_json.as_object().unwrap()
|
||||
let wide_rotation_obj = wide_rotation_json.expect_json_object()
|
||||
inspect(
|
||||
wide_rotation_obj.get("max_bytes_i64").unwrap().as_string().unwrap(),
|
||||
wide_rotation_obj.get("max_bytes_i64").unwrap().expect_json_string(),
|
||||
content="4294967296",
|
||||
)
|
||||
|
||||
@@ -40,51 +40,50 @@ test "file state json helpers stringify stable snapshots" {
|
||||
rotation_failures=4,
|
||||
),
|
||||
)
|
||||
let plain_obj = plain.as_object().unwrap()
|
||||
let plain_obj = plain.expect_json_object()
|
||||
let plain_rotation_obj = plain_obj
|
||||
.get("rotation")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
inspect(
|
||||
@json_parser.stringify(plain),
|
||||
plain.stringify(),
|
||||
content="{\"path\":\"logs/demo.log\",\"available\":true,\"append\":false,\"auto_flush\":true,\"open_failures\":1,\"write_failures\":2,\"flush_failures\":3,\"rotation_failures\":4,\"rotation\":{\"max_bytes\":64,\"max_backups\":2}}",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("path").unwrap().as_string().unwrap(),
|
||||
plain_obj.get("path").unwrap().expect_json_string(),
|
||||
content="logs/demo.log",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("available").unwrap().as_bool().unwrap(),
|
||||
plain_obj.get("available").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(plain_obj.get("append").unwrap().as_bool().unwrap(), content="false")
|
||||
inspect(plain_obj.get("append").unwrap().expect_json_bool(), content="false")
|
||||
inspect(
|
||||
plain_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
plain_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("open_failures").unwrap().as_number().unwrap().to_int(),
|
||||
plain_obj.get("open_failures").unwrap().expect_json_number().to_int(),
|
||||
content="1",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("write_failures").unwrap().as_number().unwrap().to_int(),
|
||||
plain_obj.get("write_failures").unwrap().expect_json_number().to_int(),
|
||||
content="2",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("flush_failures").unwrap().as_number().unwrap().to_int(),
|
||||
plain_obj.get("flush_failures").unwrap().expect_json_number().to_int(),
|
||||
content="3",
|
||||
)
|
||||
inspect(
|
||||
plain_obj.get("rotation_failures").unwrap().as_number().unwrap().to_int(),
|
||||
plain_obj.get("rotation_failures").unwrap().expect_json_number().to_int(),
|
||||
content="4",
|
||||
)
|
||||
inspect(
|
||||
plain_rotation_obj.get("max_bytes").unwrap().as_number().unwrap().to_int(),
|
||||
plain_rotation_obj.get("max_bytes").unwrap().expect_json_number().to_int(),
|
||||
content="64",
|
||||
)
|
||||
inspect(
|
||||
plain_rotation_obj.get("max_backups").unwrap().as_number().unwrap().to_int(),
|
||||
plain_rotation_obj.get("max_backups").unwrap().expect_json_number().to_int(),
|
||||
content="2",
|
||||
)
|
||||
|
||||
@@ -101,18 +100,16 @@ test "file state json helpers stringify stable snapshots" {
|
||||
rotation_failures=0,
|
||||
),
|
||||
)
|
||||
let wide_obj = wide.as_object().unwrap()
|
||||
let wide_obj = wide.expect_json_object()
|
||||
let wide_state_rotation_obj = wide_obj
|
||||
.get("rotation")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
inspect(
|
||||
wide_state_rotation_obj
|
||||
.get("max_bytes")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="2147483647",
|
||||
)
|
||||
@@ -120,13 +117,12 @@ test "file state json helpers stringify stable snapshots" {
|
||||
wide_state_rotation_obj
|
||||
.get("max_backups")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="3",
|
||||
)
|
||||
inspect(
|
||||
wide_state_rotation_obj.get("max_bytes_i64").unwrap().as_string().unwrap(),
|
||||
wide_state_rotation_obj.get("max_bytes_i64").unwrap().expect_json_string(),
|
||||
content="4294967296",
|
||||
)
|
||||
inspect(
|
||||
@@ -167,27 +163,27 @@ test "runtime file state json helpers stringify queue snapshots" {
|
||||
dropped_count=5,
|
||||
),
|
||||
)
|
||||
let runtime_obj = runtime_json.as_object().unwrap()
|
||||
let runtime_file_obj = runtime_obj.get("file").unwrap().as_object().unwrap()
|
||||
inspect(runtime_obj.get("queued").unwrap().as_bool().unwrap(), content="true")
|
||||
let runtime_obj = runtime_json.expect_json_object()
|
||||
let runtime_file_obj = runtime_obj.get("file").unwrap().expect_json_object()
|
||||
inspect(runtime_obj.get("queued").unwrap().expect_json_bool(), content="true")
|
||||
inspect(
|
||||
runtime_obj.get("pending_count").unwrap().as_number().unwrap().to_int(),
|
||||
runtime_obj.get("pending_count").unwrap().expect_json_number().to_int(),
|
||||
content="7",
|
||||
)
|
||||
inspect(
|
||||
runtime_obj.get("dropped_count").unwrap().as_number().unwrap().to_int(),
|
||||
runtime_obj.get("dropped_count").unwrap().expect_json_number().to_int(),
|
||||
content="5",
|
||||
)
|
||||
inspect(
|
||||
runtime_file_obj.get("path").unwrap().as_string().unwrap(),
|
||||
runtime_file_obj.get("path").unwrap().expect_json_string(),
|
||||
content="logs/queue.log",
|
||||
)
|
||||
inspect(
|
||||
runtime_file_obj.get("available").unwrap().as_bool().unwrap(),
|
||||
runtime_file_obj.get("available").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
runtime_file_obj.get("rotation").unwrap() is @json_parser.JsonValue::Null,
|
||||
runtime_file_obj.get("rotation").unwrap() is Json::Null,
|
||||
content="true",
|
||||
)
|
||||
|
||||
@@ -232,23 +228,20 @@ test "runtime file state json helpers stringify queue snapshots" {
|
||||
dropped_count=0,
|
||||
),
|
||||
)
|
||||
let wide_runtime_obj = wide_runtime_json.as_object().unwrap()
|
||||
let wide_runtime_obj = wide_runtime_json.expect_json_object()
|
||||
let wide_runtime_file_obj = wide_runtime_obj
|
||||
.get("file")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
let wide_runtime_rotation_obj = wide_runtime_file_obj
|
||||
.get("rotation")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
inspect(
|
||||
wide_runtime_rotation_obj
|
||||
.get("max_bytes")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="2147483647",
|
||||
)
|
||||
@@ -256,13 +249,12 @@ test "runtime file state json helpers stringify queue snapshots" {
|
||||
wide_runtime_rotation_obj
|
||||
.get("max_backups")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="2",
|
||||
)
|
||||
inspect(
|
||||
wide_runtime_rotation_obj.get("max_bytes_i64").unwrap().as_string().unwrap(),
|
||||
wide_runtime_rotation_obj.get("max_bytes_i64").unwrap().expect_json_string(),
|
||||
content="4294967296",
|
||||
)
|
||||
}
|
||||
@@ -276,31 +268,29 @@ test "file sink policy json helpers stringify stable policies" {
|
||||
rotation=Some(file_rotation(96, max_backups=3)),
|
||||
),
|
||||
)
|
||||
let policy_obj = policy_json.as_object().unwrap()
|
||||
let policy_obj = policy_json.expect_json_object()
|
||||
let policy_rotation_obj = policy_obj
|
||||
.get("rotation")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
inspect(
|
||||
@json_parser.stringify(policy_json),
|
||||
policy_json.stringify(),
|
||||
content="{\"append\":false,\"auto_flush\":true,\"rotation\":{\"max_bytes\":96,\"max_backups\":3}}",
|
||||
)
|
||||
inspect(policy_obj.get("append").unwrap().as_bool().unwrap(), content="false")
|
||||
inspect(policy_obj.get("append").unwrap().expect_json_bool(), content="false")
|
||||
inspect(
|
||||
policy_obj.get("auto_flush").unwrap().as_bool().unwrap(),
|
||||
policy_obj.get("auto_flush").unwrap().expect_json_bool(),
|
||||
content="true",
|
||||
)
|
||||
inspect(
|
||||
policy_rotation_obj.get("max_bytes").unwrap().as_number().unwrap().to_int(),
|
||||
policy_rotation_obj.get("max_bytes").unwrap().expect_json_number().to_int(),
|
||||
content="96",
|
||||
)
|
||||
inspect(
|
||||
policy_rotation_obj
|
||||
.get("max_backups")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="3",
|
||||
)
|
||||
@@ -312,18 +302,16 @@ test "file sink policy json helpers stringify stable policies" {
|
||||
rotation=Some(file_rotation_i64(4294967296L, max_backups=4)),
|
||||
),
|
||||
)
|
||||
let wide_policy_obj = wide_policy_json.as_object().unwrap()
|
||||
let wide_policy_obj = wide_policy_json.expect_json_object()
|
||||
let wide_policy_rotation_obj = wide_policy_obj
|
||||
.get("rotation")
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.expect_json_object()
|
||||
inspect(
|
||||
wide_policy_rotation_obj
|
||||
.get("max_bytes")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="2147483647",
|
||||
)
|
||||
@@ -331,13 +319,12 @@ test "file sink policy json helpers stringify stable policies" {
|
||||
wide_policy_rotation_obj
|
||||
.get("max_backups")
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.expect_json_number()
|
||||
.to_int(),
|
||||
content="4",
|
||||
)
|
||||
inspect(
|
||||
wide_policy_rotation_obj.get("max_bytes_i64").unwrap().as_string().unwrap(),
|
||||
wide_policy_rotation_obj.get("max_bytes_i64").unwrap().expect_json_string(),
|
||||
content="4294967296",
|
||||
)
|
||||
inspect(
|
||||
|
||||
@@ -8,9 +8,7 @@ pub type RuntimeFileState = @file_model.RuntimeFileState
|
||||
pub type RuntimeSinkProgress = @runtime.RuntimeSinkProgress
|
||||
|
||||
///|
|
||||
pub fn runtime_file_state_to_json(
|
||||
state : RuntimeFileState,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> Json {
|
||||
@file_model.runtime_file_state_to_json(state)
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -29,9 +29,7 @@ pub fn native_files_supported() -> Bool {
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_policy_to_json(
|
||||
policy : FileSinkPolicy,
|
||||
) -> @json_parser.JsonValue {
|
||||
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> Json {
|
||||
@file_model.file_sink_policy_to_json(policy)
|
||||
}
|
||||
|
||||
@@ -44,7 +42,7 @@ pub fn stringify_file_sink_policy(
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> Json {
|
||||
@file_model.file_sink_state_to_json(state)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
///|
|
||||
test "trace context maps W3C fields in a stable order" {
|
||||
let context = trace_context(
|
||||
"4bf92f3577b34da6a3ce929d0e0e4736",
|
||||
"00f067aa0ba902b7",
|
||||
trace_state="vendor=value",
|
||||
)
|
||||
let fields = context.as_fields()
|
||||
inspect(fields.length(), content="4")
|
||||
inspect(fields[0].key, content="trace_id")
|
||||
inspect(fields[0].value, content="4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
inspect(fields[1].key, content="span_id")
|
||||
inspect(fields[1].value, content="00f067aa0ba902b7")
|
||||
inspect(fields[2].key, content="trace_flags")
|
||||
inspect(fields[2].value, content="01")
|
||||
inspect(fields[3].key, content="trace_state")
|
||||
inspect(fields[3].value, content="vendor=value")
|
||||
}
|
||||
|
||||
///|
|
||||
test "sync facades bind trace context without mutating their base logger" {
|
||||
let captured : Ref[Array[Field]] = Ref([])
|
||||
let base = Logger::new(callback_sink(fn(rec) { captured.val = rec.fields }))
|
||||
let context = trace_context("trace-1", "span-1", trace_flags="00")
|
||||
let derived = base.with_trace_context(context)
|
||||
base.info("base")
|
||||
inspect(captured.val.length(), content="0")
|
||||
derived.info("derived", fields=[field("event", "request")])
|
||||
inspect(captured.val.length(), content="4")
|
||||
inspect(captured.val[0].key, content="trace_id")
|
||||
inspect(captured.val[2].value, content="00")
|
||||
inspect(captured.val[3].key, content="event")
|
||||
|
||||
let library = LibraryLogger::new(
|
||||
callback_sink(fn(rec) { captured.val = rec.fields }),
|
||||
).with_trace_context(context)
|
||||
library.info("library")
|
||||
inspect(captured.val.length(), content="3")
|
||||
inspect(captured.val[1].value, content="span-1")
|
||||
}
|
||||
Reference in New Issue
Block a user