diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..de2748e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,89 @@ +# 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. diff --git a/README.md b/README.md index f78c8b3..007158b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ four main motivating features: ## Getting Started +See the [changelog](CHANGELOG.md) for version 3.0.0 changes and migration notes. + Install the package via nimble: ```bash @@ -588,12 +590,13 @@ file system operations that might fail and cause cascading errors. ## Testing -Run `nimble test` for all unit, regression, and ThreadSanitizer ownership tests. +Run `nimble test` for all enabled unit, regression, and ThreadSanitizer ownership tests. It runs every suite even when an earlier suite fails. For focused debugging, use `nimble testUnit`, `nimble testRegression`, or `nimble testThreadSafety`. -The regression tests currently expose known defects and are expected to fail -until those defects are fixed. See [tests/README.md](tests/README.md) for commands, -requirements, and coverage limitations. +Two scenarios that expose a known Nim allocator race are temporarily skipped +by default. Enable them with `NSL_TEST_RUNTIME_ALLOCATOR=1 nimble testThreadSafety`. +See [tests/README.md](tests/README.md) for commands, requirements, and coverage +limitations. [log4j]: https://logging.apache.org/log4j/2.x/ [logback]: https://logback.qos.ch/