Use getTime().utc in both message constructors, document the timezone change, and cover concurrent construction and structured formatting. Restore ordinary logging calls in the parallel writer fixture. Validation: 32 unit tests and 29 regression tests pass. Timestamp sanitizer checks pass under UTC and non-UTC timezones. The full suite retains the known allocator reports in factory-state and allocator.
63 lines
2.4 KiB
Nim
63 lines
2.4 KiB
Nim
import std/[os, strutils]
|
|
import ../src/namespaced_logging
|
|
|
|
when not defined(multithreaded):
|
|
{.error: "Compile this fixture with -d:multithreaded to exercise writer threads".}
|
|
|
|
proc formatLine(msg: LogMessage): string {.gcsafe.} = msg.msg & "\n"
|
|
|
|
proc formatOptionalLine(msg: LogMessage): string {.gcsafe.} =
|
|
if msg.msg.len > 0: msg.msg & "\n" else: ""
|
|
|
|
proc writeMessages(args: tuple[service: LogService, worker: int]) {.thread.} =
|
|
let logger = args.service.threadLocalRef().getLogger("shutdown")
|
|
for i in 0 ..< 2_500:
|
|
logger.info("message " & $(args.worker * 2_500 + i))
|
|
|
|
proc main() =
|
|
doAssert paramCount() == 2
|
|
let destination = paramStr(1)
|
|
var service = initLogService()
|
|
case destination
|
|
of "file", "file-parallel", "file-empty":
|
|
service.addAppender(initFileLogAppender(paramStr(2)))
|
|
of "files":
|
|
service.addAppender(initFileLogAppender(paramStr(2), namespace = "first"))
|
|
service.addAppender(initFileLogAppender(paramStr(2) & ".second", namespace = "second"))
|
|
of "console", "console-parallel", "console-empty":
|
|
# Keep stdout open through the library's exit hook. Redirecting it to a
|
|
# file also prevents a full parent/child pipe from blocking the writer.
|
|
doAssert open(stdout, paramStr(2), fmWrite)
|
|
if destination == "console-empty":
|
|
service.addAppender(initConsoleLogAppender(formatter = formatOptionalLine))
|
|
else:
|
|
service.addAppender(initConsoleLogAppender(formatter = formatLine))
|
|
of "stderr":
|
|
doAssert open(stderr, paramStr(2), fmWrite)
|
|
service.addAppender(initConsoleLogAppender(useStderr = true, formatter = formatLine))
|
|
else:
|
|
raise newException(ValueError, "unknown writer destination")
|
|
|
|
if destination.endsWith("-parallel"):
|
|
var workers: array[4, Thread[tuple[service: LogService, worker: int]]]
|
|
for i in 0 ..< workers.len:
|
|
createThread(workers[i], writeMessages, (service, i))
|
|
joinThreads(workers)
|
|
else:
|
|
let local = service.threadLocalRef()
|
|
if destination == "files":
|
|
let first = local.getLogger("first")
|
|
let second = local.getLogger("second")
|
|
for i in 0 ..< 10_000:
|
|
first.info("message " & $i)
|
|
second.info("message " & $i)
|
|
else:
|
|
let logger = local.getLogger("shutdown")
|
|
for i in 0 ..< 10_000:
|
|
if destination.endsWith("-empty") and i mod 128 == 0:
|
|
logger.info("")
|
|
logger.info("message " & $i)
|
|
# No sleep or explicit flush: normal exit must preserve accepted messages.
|
|
|
|
main()
|