5.0 KiB
5.0 KiB
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.
LogServiceis now a copyable handle that keeps shared configuration alive. Pass a copy to each worker, then callthreadLocalRef()inside that worker. KeepThreadLocalLogServiceand its loggers in the thread that creates them. Uselocal.serviceinstead oflocal[]to obtain a shared handle. ensureFreshness(LogService)has been removed. Refresh the relevantThreadLocalLogServiceinstead. Configuration methods remain available on both types;LogService.appenders()returns clones of the current shared configuration.- Custom appender callbacks now receive
state: var T.initCustomLogAppendercopies supported value state into each clone and rejects ordinary references, closures, and embedded synchronization primitives, including nested ones. UseSharedLogState[T]for shared mutable value data, orinitFactoryCustomLogAppenderfor independently created state containing references or closures. - Appenders now accept
LogFormatterobjects. The formerLogMessageFormattercallback type is replaced byStatefulLogFormatterFuncandStatelessLogFormatterFunc. Noncapturing formatter functions still convert automatically. Replace directly supplied captured closures withinitLogFormatter(seed, factory), and useformatter.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.tsremains aDateTime; structured JSON output uses aZsuffix. Explicit timestamps supplied in caller-constructed messages are preserved. - The test collector returns value snapshots.
LoggedMessages.get()now returnsseq[RecordedLogMessage]instead ofseq[LogMessage]. Timestamps areTimevalues, 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 formermessagesfield.
Added
SharedLogState[T]andinitSharedLogStateprovide copyable ownership of shared value data. UsewithStatefor synchronized updates andsnapshotfor independent copies. Raw pointers and custom copy/destruction hooks remain the caller's responsibility.FactoryCustomLogAppenderandinitFactoryCustomLogAppendercreate 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.
isEnabledintentionally 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 testruns all enabled unit, regression, and ThreadSanitizer suites under ORC with threads enabled. Individual suites remain available throughtestUnit,testRegression, andtestThreadSafety.- Validation passes 32 unit tests, 29 regression tests, and 11 sanitizer
scenarios. Two scenarios,
factory-stateandallocator, 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 withNSL_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 for details and diagnostic results.
See the README for usage and migration examples.