Clarify shared logging state and owning handle types
This commit is contained in:
1 parent
761bd66793
commit
851caef706
2 files changed
+94
-95
No files matched your search
@@ -266,7 +266,7 @@ usability in multithreaded environments. The design centers on two key types:
|
|||||||
#### LogService (Value Type)
|
#### LogService (Value Type)
|
||||||
```nim
|
```nim
|
||||||
type LogService* = object
|
type LogService* = object
|
||||||
global: GlobalLogService
|
shared: ptr SharedLogServiceState
|
||||||
```
|
```
|
||||||
|
|
||||||
The *LogService* object is intended to support uses cases such as:
|
The *LogService* object is intended to support uses cases such as:
|
||||||
@@ -288,7 +288,7 @@ The *LogService* object is intended to support uses cases such as:
|
|||||||
#### ThreadLocalLogService (Reference Type)
|
#### ThreadLocalLogService (Reference Type)
|
||||||
```nim
|
```nim
|
||||||
type ThreadLocalLogService* = ref object
|
type ThreadLocalLogService* = ref object
|
||||||
shared: LogService
|
serviceHandle: LogService
|
||||||
configVersion: int
|
configVersion: int
|
||||||
appenders: seq[LogAppender]
|
appenders: seq[LogAppender]
|
||||||
thresholds: TableRef[string, Level]
|
thresholds: TableRef[string, Level]
|
||||||
@@ -321,7 +321,7 @@ during regular operation and support both the configuration functions of
|
|||||||
> *ThreadLocalLogService* objects should **never** be shared outside the
|
> *ThreadLocalLogService* objects should **never** be shared outside the
|
||||||
> context of the thread in which they were initialized.
|
> context of the thread in which they were initialized.
|
||||||
|
|
||||||
#### GlobalLogService (Internal)
|
#### SharedLogServiceState (Internal)
|
||||||
|
|
||||||
Under the hood *LogService* holds a pointer to a manually allocated shared
|
Under the hood *LogService* holds a pointer to a manually allocated shared
|
||||||
control block that serves as the single source of truth for logging
|
control block that serves as the single source of truth for logging
|
||||||
@@ -376,12 +376,12 @@ changes:
|
|||||||
```nim
|
```nim
|
||||||
proc ensureFreshness*(ls: ThreadLocalLogService) =
|
proc ensureFreshness*(ls: ThreadLocalLogService) =
|
||||||
# Cheap atomic check first
|
# Cheap atomic check first
|
||||||
if ls.configVersion == ls.global.configVersion.load():
|
if ls.configVersion == ls.shared.configVersion.load():
|
||||||
return # No changes, return immediately
|
return # No changes, return immediately
|
||||||
|
|
||||||
# Only acquire lock and copy if versions differ
|
# Only acquire lock and copy if versions differ
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
ls.configVersion = ls.global.configVersion.load
|
ls.configVersion = ls.shared.configVersion.load
|
||||||
# Sync state...
|
# Sync state...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+88
-89
@@ -7,7 +7,7 @@ from std/logging import Level
|
|||||||
export Level
|
export Level
|
||||||
|
|
||||||
type
|
type
|
||||||
GlobalLogServiceObj = object
|
SharedLogServiceState = object
|
||||||
owners: Atomic[int] # Lifetime of independently owned service handles
|
owners: Atomic[int] # Lifetime of independently owned service handles
|
||||||
configVersion: Atomic[int] # To signal the need for threads to update
|
configVersion: Atomic[int] # To signal the need for threads to update
|
||||||
lock: Lock # To protect reads/writes across threads
|
lock: Lock # To protect reads/writes across threads
|
||||||
@@ -19,8 +19,6 @@ type
|
|||||||
errorHandler: ErrorHandlerFunc
|
errorHandler: ErrorHandlerFunc
|
||||||
errorHandlerLock: Lock
|
errorHandlerLock: Lock
|
||||||
|
|
||||||
GlobalLogService = ptr GlobalLogServiceObj
|
|
||||||
|
|
||||||
|
|
||||||
ErrorHandlerFunc* =
|
ErrorHandlerFunc* =
|
||||||
proc (err: ref Exception, msg: string) {.gcsafe,nimcall.}
|
proc (err: ref Exception, msg: string) {.gcsafe,nimcall.}
|
||||||
@@ -30,13 +28,13 @@ type
|
|||||||
## Copyable shared configuration handle with an atomic lifetime.
|
## Copyable shared configuration handle with an atomic lifetime.
|
||||||
## Each thread must own its own handle; do not concurrently assign to or
|
## Each thread must own its own handle; do not concurrently assign to or
|
||||||
## destroy the same handle variable while another thread reads it.
|
## destroy the same handle variable while another thread reads it.
|
||||||
global: GlobalLogService
|
shared: ptr SharedLogServiceState
|
||||||
|
|
||||||
|
|
||||||
ThreadLocalLogService* = ref object
|
ThreadLocalLogService* = ref object
|
||||||
## Cache confined to the thread that creates it. Appender clones must
|
## Cache confined to the thread that creates it. Appender clones must
|
||||||
## themselves respect thread ownership of custom state and formatters.
|
## themselves respect thread ownership of custom state and formatters.
|
||||||
shared: LogService
|
serviceHandle: LogService
|
||||||
configVersion: int
|
configVersion: int
|
||||||
appenders: seq[LogAppender]
|
appenders: seq[LogAppender]
|
||||||
thresholds: TableRef[string, Level]
|
thresholds: TableRef[string, Level]
|
||||||
@@ -133,52 +131,52 @@ type
|
|||||||
|
|
||||||
const UninitializedConfigVersion = low(int)
|
const UninitializedConfigVersion = low(int)
|
||||||
|
|
||||||
proc retainGlobal(gls: GlobalLogService) {.inline.} =
|
proc retainSharedState(state: ptr SharedLogServiceState) {.inline.} =
|
||||||
if gls != nil:
|
if state != nil:
|
||||||
discard gls.owners.fetchAdd(1, moRelaxed)
|
discard state.owners.fetchAdd(1, moRelaxed)
|
||||||
|
|
||||||
|
|
||||||
proc releaseGlobal(gls: GlobalLogService) =
|
proc releaseSharedState(state: ptr SharedLogServiceState) =
|
||||||
if gls != nil and gls.owners.fetchSub(1, moAcquireRelease) == 1:
|
if state != nil and state.owners.fetchSub(1, moAcquireRelease) == 1:
|
||||||
# The last owner has exclusive access to the control block. Its managed
|
# The last owner has exclusive access to the control block. Its managed
|
||||||
# fields must be destroyed explicitly because it is manually allocated.
|
# fields must be destroyed explicitly because it is manually allocated.
|
||||||
`=destroy`(gls.appenders)
|
`=destroy`(state.appenders)
|
||||||
`=destroy`(gls.thresholds)
|
`=destroy`(state.thresholds)
|
||||||
deinitLock(gls.errorHandlerLock)
|
deinitLock(state.errorHandlerLock)
|
||||||
deinitLock(gls.lock)
|
deinitLock(state.lock)
|
||||||
deallocShared(gls)
|
deallocShared(state)
|
||||||
|
|
||||||
|
|
||||||
proc `=destroy`*(ls: LogService) =
|
proc `=destroy`*(ls: LogService) =
|
||||||
releaseGlobal(ls.global)
|
releaseSharedState(ls.shared)
|
||||||
|
|
||||||
|
|
||||||
proc `=copy`*(dest: var LogService, source: LogService) =
|
proc `=copy`*(dest: var LogService, source: LogService) =
|
||||||
if dest.global == source.global: return
|
if dest.shared == source.shared: return
|
||||||
retainGlobal(source.global)
|
retainSharedState(source.shared)
|
||||||
releaseGlobal(dest.global)
|
releaseSharedState(dest.shared)
|
||||||
dest.global = source.global
|
dest.shared = source.shared
|
||||||
|
|
||||||
|
|
||||||
proc `=dup`*(source: LogService): LogService =
|
proc `=dup`*(source: LogService): LogService =
|
||||||
retainGlobal(source.global)
|
retainSharedState(source.shared)
|
||||||
result.global = source.global
|
result.shared = source.shared
|
||||||
|
|
||||||
|
|
||||||
proc `=wasMoved`*(ls: var LogService) =
|
proc `=wasMoved`*(ls: var LogService) =
|
||||||
ls.global = nil
|
ls.shared = nil
|
||||||
|
|
||||||
|
|
||||||
# The compiler-generated sink operation transfers the pointer after releasing
|
# The compiler-generated sink operation transfers the pointer after releasing
|
||||||
# the destination. It does not retain the transferred owner.
|
# the destination. It does not retain the transferred owner.
|
||||||
template global(ls: ThreadLocalLogService): untyped =
|
template shared(ls: ThreadLocalLogService): untyped =
|
||||||
ls.shared.global
|
ls.serviceHandle.shared
|
||||||
|
|
||||||
|
|
||||||
proc service*(ls: ThreadLocalLogService): LogService =
|
proc service*(ls: ThreadLocalLogService): LogService =
|
||||||
## Obtain an independently owned shared handle, e.g. to pass to a worker.
|
## Obtain an independently owned shared handle, e.g. to pass to a worker.
|
||||||
## This replaces copying ls[] when ThreadLocalLogService was ref LogService.
|
## This replaces copying ls[] when ThreadLocalLogService was ref LogService.
|
||||||
ls.shared
|
ls.serviceHandle
|
||||||
|
|
||||||
|
|
||||||
var consoleLogging {.global.}: LogWriterThreadState[ConsoleMessage]
|
var consoleLogging {.global.}: LogWriterThreadState[ConsoleMessage]
|
||||||
@@ -274,7 +272,7 @@ proc shutdownThreadedFileLogging() =
|
|||||||
proc ensureFreshness*(ls: var LogService) =
|
proc ensureFreshness*(ls: var LogService) =
|
||||||
## A shared handle has no cached configuration. Retained for callers that
|
## A shared handle has no cached configuration. Retained for callers that
|
||||||
## previously refreshed LogService values; only local services need refresh.
|
## previously refreshed LogService values; only local services need refresh.
|
||||||
if ls.global == nil:
|
if ls.shared == nil:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"LogService not properly initialized. Use initLogService")
|
"LogService not properly initialized. Use initLogService")
|
||||||
|
|
||||||
@@ -284,7 +282,7 @@ proc ensureFreshness*(ls: ThreadLocalLogService) =
|
|||||||
## against the global log service definition. This uses an atomic version
|
## against the global log service definition. This uses an atomic version
|
||||||
## number to allow the check to be inexpensive, only incurring the more
|
## number to allow the check to be inexpensive, only incurring the more
|
||||||
## expensive copy operations if there are actual changes.
|
## expensive copy operations if there are actual changes.
|
||||||
if ls.isNil or ls.global.isNil:
|
if ls.isNil or ls.shared.isNil:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"LogService not properly initialized. Use initLogService to obtain a" &
|
"LogService not properly initialized. Use initLogService to obtain a" &
|
||||||
"properly initialized LogService object")
|
"properly initialized LogService object")
|
||||||
@@ -293,23 +291,24 @@ proc ensureFreshness*(ls: ThreadLocalLogService) =
|
|||||||
# particular, do not iterate a seq through a function-returned expression:
|
# particular, do not iterate a seq through a function-returned expression:
|
||||||
# that can create an owning temporary and register its shared appender refs
|
# that can create an owning temporary and register its shared appender refs
|
||||||
# with this thread's ORC cycle collector even while the lock is held.
|
# with this thread's ORC cycle collector even while the lock is held.
|
||||||
let gls = ls.shared.global
|
let state = ls.serviceHandle.shared
|
||||||
if ls.configVersion == gls.configVersion.load(): return
|
if ls.configVersion == state.configVersion.load(): return
|
||||||
|
|
||||||
withLock gls.lock:
|
withLock state.lock:
|
||||||
ls.configVersion = gls.configVersion.load
|
ls.configVersion = state.configVersion.load
|
||||||
|
|
||||||
ls.appenders = @[]
|
ls.appenders = @[]
|
||||||
for a in gls.appenders: ls.appenders.add(clone(a))
|
for a in state.appenders: ls.appenders.add(clone(a))
|
||||||
|
|
||||||
ls.thresholds = newTable[string, Level]()
|
ls.thresholds = newTable[string, Level]()
|
||||||
for ns, t in pairs(gls.thresholds): ls.thresholds[ns] = t
|
for ns, t in pairs(state.thresholds): ls.thresholds[ns] = t
|
||||||
|
|
||||||
|
|
||||||
proc initGlobalLogService(
|
proc initSharedLogServiceState(
|
||||||
rootLevel = lvlAll,
|
rootLevel = lvlAll,
|
||||||
errorHandler = defaultErrorHandlerFunc): GlobalLogService =
|
errorHandler = defaultErrorHandlerFunc): ptr SharedLogServiceState =
|
||||||
result = cast[GlobalLogService](allocShared0(sizeof(GlobalLogServiceObj)))
|
result = cast[ptr SharedLogServiceState](
|
||||||
|
allocShared0(sizeof(SharedLogServiceState)))
|
||||||
result.owners.store(1)
|
result.owners.store(1)
|
||||||
result.configVersion.store(0)
|
result.configVersion.store(0)
|
||||||
initLock(result.lock)
|
initLock(result.lock)
|
||||||
@@ -335,26 +334,26 @@ proc initLogService*(
|
|||||||
## Multiple calls to *initLogService* will result in completely independent
|
## Multiple calls to *initLogService* will result in completely independent
|
||||||
## *LogService* objects with their own synchronized configuration and global
|
## *LogService* objects with their own synchronized configuration and global
|
||||||
## state.
|
## state.
|
||||||
result.global = initGlobalLogService(rootLevel, errorHandler)
|
result.shared = initSharedLogServiceState(rootLevel, errorHandler)
|
||||||
|
|
||||||
|
|
||||||
proc threadLocalRef*(ls: LogService): ThreadLocalLogService =
|
proc threadLocalRef*(ls: LogService): ThreadLocalLogService =
|
||||||
## Build a fresh cache in the calling thread, retaining only the shared
|
## Build a fresh cache in the calling thread, retaining only the shared
|
||||||
## handle. Never copy another thread's cached appender or threshold refs.
|
## handle. Never copy another thread's cached appender or threshold refs.
|
||||||
result = ThreadLocalLogService(shared: ls,
|
result = ThreadLocalLogService(serviceHandle: ls,
|
||||||
configVersion: UninitializedConfigVersion)
|
configVersion: UninitializedConfigVersion)
|
||||||
ensureFreshness(result)
|
ensureFreshness(result)
|
||||||
|
|
||||||
|
|
||||||
proc reportLoggingError(
|
proc reportLoggingError(
|
||||||
gls: GlobalLogService,
|
state: ptr SharedLogServiceState,
|
||||||
err: ref Exception,
|
err: ref Exception,
|
||||||
msg: string) =
|
msg: string) =
|
||||||
|
|
||||||
var handler: ErrorHandlerFunc
|
var handler: ErrorHandlerFunc
|
||||||
|
|
||||||
withLock gls.errorHandlerLock:
|
withLock state.errorHandlerLock:
|
||||||
handler = gls.errorHandler
|
handler = state.errorHandler
|
||||||
|
|
||||||
if not handler.isNil:
|
if not handler.isNil:
|
||||||
try: handler(err, msg)
|
try: handler(err, msg)
|
||||||
@@ -398,23 +397,23 @@ proc setErrorHandler*(
|
|||||||
|
|
||||||
if errHandler.isNil: return
|
if errHandler.isNil: return
|
||||||
|
|
||||||
withLock ls.global.errorHandlerLock:
|
withLock ls.shared.errorHandlerLock:
|
||||||
ls.global.errorHandler = errHandler
|
ls.shared.errorHandler = errHandler
|
||||||
|
|
||||||
|
|
||||||
proc setErrorHandler*(
|
proc setErrorHandler*(
|
||||||
ls: ThreadLocalLogService,
|
ls: ThreadLocalLogService,
|
||||||
errHandler = defaultErrorHandlerFunc) {.gcsafe.} =
|
errHandler = defaultErrorHandlerFunc) {.gcsafe.} =
|
||||||
setErrorHandler(ls.shared, errHandler)
|
setErrorHandler(ls.serviceHandle, errHandler)
|
||||||
|
|
||||||
|
|
||||||
proc setRootThreshold*(ls: var LogService, lvl: Level) {.gcsafe.} =
|
proc setRootThreshold*(ls: var LogService, lvl: Level) {.gcsafe.} =
|
||||||
ls.global.rootLevel.store(lvl)
|
ls.shared.rootLevel.store(lvl)
|
||||||
ls.global.configVersion.atomicInc
|
ls.shared.configVersion.atomicInc
|
||||||
|
|
||||||
|
|
||||||
proc setRootThreshold*(ls: ThreadLocalLogService, lvl: Level) {.gcsafe.} =
|
proc setRootThreshold*(ls: ThreadLocalLogService, lvl: Level) {.gcsafe.} =
|
||||||
setRootThreshold(ls.shared, lvl)
|
setRootThreshold(ls.serviceHandle, lvl)
|
||||||
|
|
||||||
|
|
||||||
func formatSimpleTextLog*(msg: LogMessage): string {.gcsafe.} = msg.msg
|
func formatSimpleTextLog*(msg: LogMessage): string {.gcsafe.} = msg.msg
|
||||||
@@ -424,24 +423,24 @@ func formatJsonStructuredLog*(msg: LogMessage): string {.gcsafe.} = $(%msg)
|
|||||||
|
|
||||||
|
|
||||||
proc setThreshold*(ls: var LogService, scope: string, lvl: Level) {.gcsafe.} =
|
proc setThreshold*(ls: var LogService, scope: string, lvl: Level) {.gcsafe.} =
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
ls.global.thresholds[scope] = lvl
|
ls.shared.thresholds[scope] = lvl
|
||||||
ls.global.configVersion.atomicInc
|
ls.shared.configVersion.atomicInc
|
||||||
|
|
||||||
|
|
||||||
proc setThreshold*(ls: ThreadLocalLogService, scope: string, lvl: Level) {.gcsafe.} =
|
proc setThreshold*(ls: ThreadLocalLogService, scope: string, lvl: Level) {.gcsafe.} =
|
||||||
setThreshold(ls.shared, scope, lvl)
|
setThreshold(ls.serviceHandle, scope, lvl)
|
||||||
ensureFreshness(ls)
|
ensureFreshness(ls)
|
||||||
|
|
||||||
|
|
||||||
proc setThresholds*(ls: var LogService, thresholds: TableRef[string, Level]) {.gcsafe.} =
|
proc setThresholds*(ls: var LogService, thresholds: TableRef[string, Level]) {.gcsafe.} =
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
for k,v in thresholds: ls.global.thresholds[k] = v
|
for k,v in thresholds: ls.shared.thresholds[k] = v
|
||||||
ls.global.configVersion.atomicInc
|
ls.shared.configVersion.atomicInc
|
||||||
|
|
||||||
|
|
||||||
proc setThresholds*(ls: ThreadLocalLogService, thresholds: TableRef[string, Level]) {.gcsafe.} =
|
proc setThresholds*(ls: ThreadLocalLogService, thresholds: TableRef[string, Level]) {.gcsafe.} =
|
||||||
setThresholds(ls.shared, thresholds)
|
setThresholds(ls.serviceHandle, thresholds)
|
||||||
ensureFreshness(ls)
|
ensureFreshness(ls)
|
||||||
|
|
||||||
proc getLogger*(
|
proc getLogger*(
|
||||||
@@ -479,8 +478,8 @@ proc getLogger*(
|
|||||||
|
|
||||||
|
|
||||||
proc appenders*(ls: var LogService): seq[LogAppender] {.gcsafe.} =
|
proc appenders*(ls: var LogService): seq[LogAppender] {.gcsafe.} =
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
for a in ls.global.appenders: result.add(clone(a))
|
for a in ls.shared.appenders: result.add(clone(a))
|
||||||
|
|
||||||
proc appenders*(ls: ThreadLocalLogService): seq[LogAppender] {.gcsafe.} =
|
proc appenders*(ls: ThreadLocalLogService): seq[LogAppender] {.gcsafe.} =
|
||||||
for a in ls.appenders: result.add(clone(a))
|
for a in ls.appenders: result.add(clone(a))
|
||||||
@@ -488,34 +487,34 @@ proc appenders*(ls: ThreadLocalLogService): seq[LogAppender] {.gcsafe.} =
|
|||||||
proc addAppender*(ls: var LogService, appender: LogAppender) {.gcsafe.} =
|
proc addAppender*(ls: var LogService, appender: LogAppender) {.gcsafe.} =
|
||||||
## Add a log appender to the shared configuration. Local services refresh
|
## Add a log appender to the shared configuration. Local services refresh
|
||||||
## their cached state on their next freshness check.
|
## their cached state on their next freshness check.
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
var copiedAppender = clone(appender)
|
var copiedAppender = clone(appender)
|
||||||
ls.global.appenders.add(copiedAppender)
|
ls.shared.appenders.add(copiedAppender)
|
||||||
ls.global.configVersion.atomicInc
|
ls.shared.configVersion.atomicInc
|
||||||
|
|
||||||
|
|
||||||
proc addAppender*(ls: ThreadLocalLogService, appender: LogAppender) {.gcsafe.} =
|
proc addAppender*(ls: ThreadLocalLogService, appender: LogAppender) {.gcsafe.} =
|
||||||
addAppender(ls.shared, appender)
|
addAppender(ls.serviceHandle, appender)
|
||||||
ensureFreshness(ls)
|
ensureFreshness(ls)
|
||||||
|
|
||||||
|
|
||||||
proc clearAppenders*(ls: var LogService) {.gcsafe.} =
|
proc clearAppenders*(ls: var LogService) {.gcsafe.} =
|
||||||
## Remove all shared appenders. Local services refresh their cached state
|
## Remove all shared appenders. Local services refresh their cached state
|
||||||
## on their next freshness check.
|
## on their next freshness check.
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
ls.global.appenders = @[]
|
ls.shared.appenders = @[]
|
||||||
ls.global.configVersion.atomicInc
|
ls.shared.configVersion.atomicInc
|
||||||
|
|
||||||
|
|
||||||
proc clearAppenders*(ls: ThreadLocalLogService) {.gcsafe.} =
|
proc clearAppenders*(ls: ThreadLocalLogService) {.gcsafe.} =
|
||||||
clearAppenders(ls.shared)
|
clearAppenders(ls.serviceHandle)
|
||||||
|
|
||||||
|
|
||||||
func getEffectiveThreshold(logger: Logger): Level {.gcsafe.} =
|
func getEffectiveThreshold(logger: Logger): Level {.gcsafe.} =
|
||||||
## Get the effective logging level threshold for a logger. This is the most
|
## Get the effective logging level threshold for a logger. This is the most
|
||||||
## specific level that is set for the logger or any of its parents. The root
|
## specific level that is set for the logger or any of its parents. The root
|
||||||
## logger is used as the default if no other level is set.
|
## logger is used as the default if no other level is set.
|
||||||
result = logger.threadSvc.global.rootLevel.load
|
result = logger.threadSvc.shared.rootLevel.load
|
||||||
|
|
||||||
# Check for exact match first
|
# Check for exact match first
|
||||||
if logger.threadSvc.thresholds.hasKey(logger.scope):
|
if logger.threadSvc.thresholds.hasKey(logger.scope):
|
||||||
@@ -738,7 +737,7 @@ method appendLogMessage[T](
|
|||||||
raise newException(ValueError, "CustomLogAppender.appendLogMessage: doLogMessage is nil")
|
raise newException(ValueError, "CustomLogAppender.appendLogMessage: doLogMessage is nil")
|
||||||
else: cla.doLogMessage(cla.state, msg)
|
else: cla.doLogMessage(cla.state, msg)
|
||||||
except Exception:
|
except Exception:
|
||||||
ls.global.reportLoggingError(
|
ls.shared.reportLoggingError(
|
||||||
getCurrentException(),
|
getCurrentException(),
|
||||||
"unable to append to CustomLogAppender")
|
"unable to append to CustomLogAppender")
|
||||||
|
|
||||||
@@ -822,7 +821,7 @@ method appendLogMessage(
|
|||||||
else:
|
else:
|
||||||
cla.appendLogMessageSingleThreaded(ls, msg)
|
cla.appendLogMessageSingleThreaded(ls, msg)
|
||||||
except Exception:
|
except Exception:
|
||||||
ls.global.reportLoggingError(
|
ls.shared.reportLoggingError(
|
||||||
getCurrentException(),
|
getCurrentException(),
|
||||||
"unable to append to ConsoleLogAppender")
|
"unable to append to ConsoleLogAppender")
|
||||||
|
|
||||||
@@ -892,7 +891,7 @@ method appendLogMessage(
|
|||||||
else:
|
else:
|
||||||
fla.appendLogMessageSingleThreaded(ls, msg)
|
fla.appendLogMessageSingleThreaded(ls, msg)
|
||||||
except Exception:
|
except Exception:
|
||||||
ls.global.reportLoggingError(
|
ls.shared.reportLoggingError(
|
||||||
getCurrentException(),
|
getCurrentException(),
|
||||||
"unable to append to FileLogAppender")
|
"unable to append to FileLogAppender")
|
||||||
|
|
||||||
@@ -958,22 +957,22 @@ when isMainModule:
|
|||||||
import std/[files, tempfiles, unittest]
|
import std/[files, tempfiles, unittest]
|
||||||
import ./namespaced_logging/testutil
|
import ./namespaced_logging/testutil
|
||||||
|
|
||||||
suite "GlobalLogService Initialization":
|
suite "LogService Initialization":
|
||||||
|
|
||||||
test "initLogService creates valid service":
|
test "initLogService creates valid service":
|
||||||
let ls = initLogService()
|
let ls = initLogService()
|
||||||
let local = ls.threadLocalRef()
|
let local = ls.threadLocalRef()
|
||||||
check:
|
check:
|
||||||
ls.global != nil
|
ls.shared != nil
|
||||||
ls.global.owners.load() == 2
|
ls.shared.owners.load() == 2
|
||||||
ls.global.configVersion.load() == 0
|
ls.shared.configVersion.load() == 0
|
||||||
ls.global.rootLevel.load() == lvlAll
|
ls.shared.rootLevel.load() == lvlAll
|
||||||
local.configVersion >= 0
|
local.configVersion >= 0
|
||||||
local.configVersion == ls.global.configVersion.load
|
local.configVersion == ls.shared.configVersion.load
|
||||||
|
|
||||||
test "initLogService with custom root level":
|
test "initLogService with custom root level":
|
||||||
let ls = initLogService(lvlInfo)
|
let ls = initLogService(lvlInfo)
|
||||||
check ls.global.rootLevel.load() == lvlInfo
|
check ls.shared.rootLevel.load() == lvlInfo
|
||||||
|
|
||||||
test "threadLocalRef builds independent caches for an unchanged service":
|
test "threadLocalRef builds independent caches for an unchanged service":
|
||||||
var service = initLogService()
|
var service = initLogService()
|
||||||
@@ -982,7 +981,7 @@ when isMainModule:
|
|||||||
let first = service.threadLocalRef()
|
let first = service.threadLocalRef()
|
||||||
let second = service.threadLocalRef()
|
let second = service.threadLocalRef()
|
||||||
check:
|
check:
|
||||||
first.global == second.global
|
first.shared == second.shared
|
||||||
first.configVersion == second.configVersion
|
first.configVersion == second.configVersion
|
||||||
cast[pointer](first.thresholds) != cast[pointer](second.thresholds)
|
cast[pointer](first.thresholds) != cast[pointer](second.thresholds)
|
||||||
first.thresholds["worker"] == second.thresholds["worker"]
|
first.thresholds["worker"] == second.thresholds["worker"]
|
||||||
@@ -996,7 +995,7 @@ when isMainModule:
|
|||||||
|
|
||||||
test "setRootThreshold updates level":
|
test "setRootThreshold updates level":
|
||||||
ls.setRootThreshold(lvlError)
|
ls.setRootThreshold(lvlError)
|
||||||
check ls.global.rootLevel.load() == lvlError
|
check ls.shared.rootLevel.load() == lvlError
|
||||||
|
|
||||||
test "setThreshold adds namespace threshold":
|
test "setThreshold adds namespace threshold":
|
||||||
ls.setThreshold("api", lvlWarn)
|
ls.setThreshold("api", lvlWarn)
|
||||||
@@ -1004,9 +1003,9 @@ when isMainModule:
|
|||||||
|
|
||||||
test "ensureFreshness syncs configuration":
|
test "ensureFreshness syncs configuration":
|
||||||
# Modify global state directly
|
# Modify global state directly
|
||||||
withLock ls.global.lock:
|
withLock ls.shared.lock:
|
||||||
ls.global.thresholds["test"] = lvlError
|
ls.shared.thresholds["test"] = lvlError
|
||||||
ls.global.configVersion.atomicInc()
|
ls.shared.configVersion.atomicInc()
|
||||||
|
|
||||||
# Local state should be out of sync
|
# Local state should be out of sync
|
||||||
check not ls.thresholds.hasKey("test")
|
check not ls.thresholds.hasKey("test")
|
||||||
@@ -1021,18 +1020,18 @@ when isMainModule:
|
|||||||
ls.setThreshold("api", lvlWarn)
|
ls.setThreshold("api", lvlWarn)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
ls.configVersion == ls.global.configVersion.load
|
ls.configVersion == ls.shared.configVersion.load
|
||||||
ls.configVersion != ls2.configVersion
|
ls.configVersion != ls2.configVersion
|
||||||
ls2.configVersion != ls.global.configVersion.load
|
ls2.configVersion != ls.shared.configVersion.load
|
||||||
ls.thresholds["api"] == lvlWarn
|
ls.thresholds["api"] == lvlWarn
|
||||||
ls.global.thresholds["api"] == lvlWarn
|
ls.shared.thresholds["api"] == lvlWarn
|
||||||
not ls2.thresholds.contains("api")
|
not ls2.thresholds.contains("api")
|
||||||
|
|
||||||
ls2.ensureFreshness()
|
ls2.ensureFreshness()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
ls.configVersion == ls2.configVersion
|
ls.configVersion == ls2.configVersion
|
||||||
ls2.configVersion == ls.global.configVersion.load
|
ls2.configVersion == ls.shared.configVersion.load
|
||||||
ls2.thresholds["api"] == lvlWarn
|
ls2.thresholds["api"] == lvlWarn
|
||||||
|
|
||||||
test "ensureFreshness syncs configuration across threads":
|
test "ensureFreshness syncs configuration across threads":
|
||||||
@@ -1083,13 +1082,13 @@ when isMainModule:
|
|||||||
joinThreads(tA, tB)
|
joinThreads(tA, tB)
|
||||||
|
|
||||||
test "configuration version incrementing":
|
test "configuration version incrementing":
|
||||||
let initialVersion = ls.global.configVersion.load()
|
let initialVersion = ls.shared.configVersion.load()
|
||||||
|
|
||||||
ls.setRootThreshold(lvlWarn)
|
ls.setRootThreshold(lvlWarn)
|
||||||
check ls.global.configVersion.load() > initialVersion
|
check ls.shared.configVersion.load() > initialVersion
|
||||||
|
|
||||||
ls.setThreshold("test", lvlError)
|
ls.setThreshold("test", lvlError)
|
||||||
check ls.global.configVersion.load() > initialVersion + 1
|
check ls.shared.configVersion.load() > initialVersion + 1
|
||||||
|
|
||||||
|
|
||||||
suite "Logger Creation and Usage":
|
suite "Logger Creation and Usage":
|
||||||
|
|||||||
Reference in new issue
Block a user