90 lines
5.0 KiB
Markdown
90 lines
5.0 KiB
Markdown
# Changelog
|
|
|
|
## 3.0.0
|
|
|
|
This release reworks logging ownership for Nim's ORC memory manager, fixes
|
|
background writer shutdown, and switches generated timestamps to UTC. It
|
|
introduces breaking API changes from 2.1.2. Nim 2.2.0 or later remains required.
|
|
|
|
### Breaking changes and migration
|
|
|
|
- **Service handles and local caches are separate types.** `LogService` is now
|
|
a copyable handle that keeps shared configuration alive. Pass a copy to each
|
|
worker, then call `threadLocalRef()` inside that worker. Keep
|
|
`ThreadLocalLogService` and its loggers in the thread that creates them. Use
|
|
`local.service` instead of `local[]` to obtain a shared handle.
|
|
- **`ensureFreshness(LogService)` has been removed.** Refresh the relevant
|
|
`ThreadLocalLogService` instead. Configuration methods remain available on
|
|
both types; `LogService.appenders()` returns clones of the current shared
|
|
configuration.
|
|
- **Custom appender callbacks now receive `state: var T`.**
|
|
`initCustomLogAppender` copies supported value state into each clone and
|
|
rejects ordinary references, closures, and embedded synchronization
|
|
primitives, including nested ones. Use `SharedLogState[T]` for shared mutable
|
|
value data, or `initFactoryCustomLogAppender` for independently created state
|
|
containing references or closures.
|
|
- **Appenders now accept `LogFormatter` objects.** The former
|
|
`LogMessageFormatter` callback type is replaced by `StatefulLogFormatterFunc`
|
|
and `StatelessLogFormatterFunc`. Noncapturing formatter functions still
|
|
convert automatically. Replace directly supplied captured closures with
|
|
`initLogFormatter(seed, factory)`, and use `formatter.format(message)` to
|
|
invoke a formatter directly. Custom appender and formatter clones must own
|
|
independent state or use explicit shared-state handles.
|
|
- **Generated timestamps now use UTC.** `LogMessage.ts` remains a `DateTime`;
|
|
structured JSON output uses a `Z` suffix. Explicit timestamps supplied in
|
|
caller-constructed messages are preserved.
|
|
- **The test collector returns value snapshots.** `LoggedMessages.get()` now
|
|
returns `seq[RecordedLogMessage]` instead of `seq[LogMessage]`. Timestamps are
|
|
`Time` values, additional JSON data is serialized text, and exceptions retain
|
|
their name, message, and stacktrace as text. Use the collector's methods
|
|
instead of accessing its former `messages` field.
|
|
|
|
### Added
|
|
|
|
- `SharedLogState[T]` and `initSharedLogState` provide copyable ownership of
|
|
shared value data. Use `withState` for synchronized updates and `snapshot`
|
|
for independent copies. Raw pointers and custom copy/destruction hooks
|
|
remain the caller's responsibility.
|
|
- `FactoryCustomLogAppender` and `initFactoryCustomLogAppender` create custom
|
|
state lazily within the thread using each clone. Formatter factories provide
|
|
the same ownership pattern for captured formatter state. Configuration
|
|
refresh can replace clones and reset their local state; use shared-state
|
|
handles for data that must persist across refreshes.
|
|
|
|
### Fixed and clarified
|
|
|
|
- Service copies retain shared configuration without sharing ordinary ORC
|
|
reference ownership between threads. Workers can outlive the original
|
|
handle and the thread that created it.
|
|
- Local caches are built independently. Failed configuration refreshes preserve
|
|
the previous cache and retry on a later refresh without requiring another
|
|
configuration change.
|
|
- Background console and file writers drain queued records at normal process
|
|
exit, including empty messages and the final file batch. Shutdown excludes
|
|
further submissions and waits for writers to finish. Applications must join
|
|
their logging workers before exiting.
|
|
- Both message constructors avoid local-time conversion races by creating UTC
|
|
timestamps directly.
|
|
- `isEnabled` intentionally checks cached namespace thresholds without
|
|
refreshing configuration. Logging calls, `getLogger`, or an explicit local
|
|
refresh pick up changes. Applications that guard all logging should refresh
|
|
at suitable boundaries, such as the start of a request. The shared root
|
|
threshold is read only when no cached namespace threshold applies.
|
|
|
|
### Tests and known limitations
|
|
|
|
- `nimble test` runs all enabled unit, regression, and ThreadSanitizer suites
|
|
under ORC with threads enabled. Individual suites remain available through
|
|
`testUnit`, `testRegression`, and `testThreadSafety`.
|
|
- Validation passes 32 unit tests, 29 regression tests, and 11 sanitizer
|
|
scenarios. Two scenarios, `factory-state` and `allocator`, are temporarily
|
|
skipped because they expose a known race in Nim 2.2.10's default allocator.
|
|
Their code and assertions remain available; enable them with
|
|
`NSL_TEST_RUNTIME_ALLOCATOR=1 nimble testThreadSafety`.
|
|
- The allocator limitation remains unresolved and also reproduces without
|
|
importing this library. Passing the enabled tests does not establish full
|
|
validation of factory state under the default allocator. See
|
|
[the test documentation](tests/README.md) for details and diagnostic results.
|
|
|
|
See the [README](README.md) for usage and migration examples.
|