Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 127 additions & 120 deletions chronicles.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import
macros, tables, strutils, strformat,
chronicles/[formats, scope_helpers, dynamic_scope, log_output, options]
chronicles/[formats, scope_helpers, options]

export
formats, dynamic_scope, log_output, options
formats, options

when not defined(`any`) and not defined(standalone):
import
chronicles/[dynamic_scope, log_output]

export
dynamic_scope, log_output

# So, how does Chronicles work?
#
Expand Down Expand Up @@ -205,149 +212,150 @@ macro logIMPL(lineInfo: static InstInfo,
silenceCompilerWarning()
return

# This is the compile-time topic filtering code, which has a similar
# logic to the generated run-time filtering code:
var enabledTopicsMatch = enabledTopics.len == 0 and severity >= enabledLogLevel
var requiredTopicsCount = requiredTopics.len
var topicsNode = newLit("")
var activeTopics: seq[string] = @[]
var useLineNumbers = lineNumbersEnabled
var useThreadIds = threadIdsEnabled
if finalBindings.hasKey("topics"):
topicsNode = finalBindings["topics"]
finalBindings.del("topics")

if topicsNode.kind notin {nnkStrLit, nnkTripleStrLit}:
error "Please specify the 'topics' list as a space separated string literal", topicsNode

activeTopics = topicsNode.strVal.split({','} + Whitespace)

for t in activeTopics:
if t in disabledTopics:
return
else:
for topic in enabledTopics:
if topic.name == t:
if topic.logLevel != LogLevel.NONE:
if severity >= topic.logLevel:
when loggingEnabled:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff is hard to read here. This is just putting the rest of the code in this macro behind the when loggingEnabled conditional.

# This is the compile-time topic filtering code, which has a similar
# logic to the generated run-time filtering code:
var enabledTopicsMatch = enabledTopics.len == 0 and severity >= enabledLogLevel
var requiredTopicsCount = requiredTopics.len
var topicsNode = newLit("")
var activeTopics: seq[string] = @[]
var useLineNumbers = lineNumbersEnabled
var useThreadIds = threadIdsEnabled
if finalBindings.hasKey("topics"):
topicsNode = finalBindings["topics"]
finalBindings.del("topics")

if topicsNode.kind notin {nnkStrLit, nnkTripleStrLit}:
error "Please specify the 'topics' list as a space separated string literal", topicsNode

activeTopics = topicsNode.strVal.split({','} + Whitespace)

for t in activeTopics:
if t in disabledTopics:
return
else:
for topic in enabledTopics:
if topic.name == t:
if topic.logLevel != LogLevel.NONE:
if severity >= topic.logLevel:
enabledTopicsMatch = true
elif severity >= enabledLogLevel:
enabledTopicsMatch = true
elif severity >= enabledLogLevel:
enabledTopicsMatch = true
if t in requiredTopics:
dec requiredTopicsCount

if severity != NONE and not enabledTopicsMatch or requiredTopicsCount > 0:
silenceCompilerWarning()
return
if t in requiredTopics:
dec requiredTopicsCount

if severity != NONE and not enabledTopicsMatch or requiredTopicsCount > 0:
silenceCompilerWarning()
return

proc lookForScopeOverride(option: var bool, overrideName: string) =
if finalBindings.hasKey(overrideName):
let overrideValue = $finalBindings[overrideName]
if overrideValue notin ["true", "false"]:
error(overrideName & " should be set to either true or false",
finalBindings[overrideName])
option = overrideValue == "true"
finalBindings.del(overrideName)

# The user is allowed to override the compile-time options for line numbers
# and thread ids in particular log statements or scopes:
lookForScopeOverride(useLineNumbers, "chroniclesLineNumbers")
lookForScopeOverride(useThreadIds, "chroniclesThreadIds")

var code = newStmtList()

when runtimeFilteringEnabled:
if severity != LogLevel.NONE:
code.add runtimeTopicFilteringCode(severity, activeTopics)

# The rest of the code selects the active LogRecord type (which can
# be a tuple when the sink has multiple destinations) and then
# translates the log statement to a set of calls to `initLogRecord`,
# `setProperty` and `flushRecord`.
let
record = genSym(nskVar, "record")
recordTypeSym = skipTypedesc(RecordType.getTypeImpl())
recordTypeNodes = recordTypeSym.getTypeImpl()
recordArity = if recordTypeNodes.kind != nnkTupleConstr: 1
else: recordTypeNodes.len
lvl = newDotExpr(bindSym("LogLevel", brClosed), ident $severity)
chroniclesExpandItIMPL = bindSym("chroniclesExpandItIMPL", brForceOpen)
prepareOutput = bindSym("prepareOutput", brForceOpen)
initLogRecord = bindSym("initLogRecord", brForceOpen)
setProperty = bindSym("setProperty", brForceOpen)

proc lookForScopeOverride(option: var bool, overrideName: string) =
if finalBindings.hasKey(overrideName):
let overrideValue = $finalBindings[overrideName]
if overrideValue notin ["true", "false"]:
error(overrideName & " should be set to either true or false",
finalBindings[overrideName])
option = overrideValue == "true"
finalBindings.del(overrideName)

# The user is allowed to override the compile-time options for line numbers
# and thread ids in particular log statements or scopes:
lookForScopeOverride(useLineNumbers, "chroniclesLineNumbers")
lookForScopeOverride(useThreadIds, "chroniclesThreadIds")

var code = newStmtList()

when runtimeFilteringEnabled:
if severity != LogLevel.NONE:
code.add runtimeTopicFilteringCode(severity, activeTopics)

# The rest of the code selects the active LogRecord type (which can
# be a tuple when the sink has multiple destinations) and then
# translates the log statement to a set of calls to `initLogRecord`,
# `setProperty` and `flushRecord`.
let
record = genSym(nskVar, "record")
recordTypeSym = skipTypedesc(RecordType.getTypeImpl())
recordTypeNodes = recordTypeSym.getTypeImpl()
recordArity = if recordTypeNodes.kind != nnkTupleConstr: 1
else: recordTypeNodes.len
lvl = newDotExpr(bindSym("LogLevel", brClosed), ident $severity)
chroniclesExpandItIMPL = bindSym("chroniclesExpandItIMPL", brForceOpen)
prepareOutput = bindSym("prepareOutput", brForceOpen)
initLogRecord = bindSym("initLogRecord", brForceOpen)
setProperty = bindSym("setProperty", brForceOpen)

code.add quote do:
var `record`: `RecordType`

if recordArity > 1 and runtimeFilteringEnabled:
code.add quote do:
`prepareOutput`(`record`, `lvl`, `chroniclesTopicsMatchVar`)
`initLogRecord`(`record`, `lvl`, `topicsNode`, `eventName`, `chroniclesTopicsMatchVar`)
else:
code.add quote do:
`prepareOutput`(`record`, `lvl`)
`initLogRecord`(`record`, `lvl`, `topicsNode`, `eventName`)
var `record`: `RecordType`

if useThreadIds:
# called tid even when it's a process id - this to avoid differences in
# logging between threads and no threads
if recordArity > 1 and runtimeFilteringEnabled:
code.add quote do:
`setProperty`(`record`, "tid", getLogThreadId(), `chroniclesTopicsMatchVar`)
`prepareOutput`(`record`, `lvl`, `chroniclesTopicsMatchVar`)
`initLogRecord`(`record`, `lvl`, `topicsNode`, `eventName`, `chroniclesTopicsMatchVar`)
else:
code.add quote do:
`setProperty`(`record`, "tid", getLogThreadId())
`prepareOutput`(`record`, `lvl`)
`initLogRecord`(`record`, `lvl`, `topicsNode`, `eventName`)

if useThreadIds:
# called tid even when it's a process id - this to avoid differences in
# logging between threads and no threads
if recordArity > 1 and runtimeFilteringEnabled:
code.add quote do:
`setProperty`(`record`, "tid", getLogThreadId(), `chroniclesTopicsMatchVar`)
else:
code.add quote do:
`setProperty`(`record`, "tid", getLogThreadId())

if useLineNumbers:
var filename = lineInfo.filename & ":" & $lineInfo.line
if recordArity > 1 and runtimeFilteringEnabled:
code.add newCall(setProperty, record, newLit("file"), newLit(filename), chroniclesTopicsMatchVar)
else:
code.add newCall(setProperty, record, newLit("file"), newLit(filename))
if useLineNumbers:
var filename = lineInfo.filename & ":" & $lineInfo.line
if recordArity > 1 and runtimeFilteringEnabled:
code.add newCall(setProperty, record, newLit("file"), newLit(filename), chroniclesTopicsMatchVar)
else:
code.add newCall(setProperty, record, newLit("file"), newLit(filename))

for k, v in finalBindings:
if recordArity > 1 and runtimeFilteringEnabled:
code.add newCall(chroniclesExpandItIMPL, record, newLit(k), v, chroniclesTopicsMatchVar)
else:
code.add newCall(chroniclesExpandItIMPL, record, newLit(k), v)

for k, v in finalBindings:
if recordArity > 1 and runtimeFilteringEnabled:
code.add newCall(chroniclesExpandItIMPL, record, newLit(k), v, chroniclesTopicsMatchVar)
code.add newCall("logAllDynamicProperties", Stream, record, chroniclesTopicsMatchVar)
code.add newCall("flushRecord", record, chroniclesTopicsMatchVar)
else:
code.add newCall(chroniclesExpandItIMPL, record, newLit(k), v)

if recordArity > 1 and runtimeFilteringEnabled:
code.add newCall("logAllDynamicProperties", Stream, record, chroniclesTopicsMatchVar)
code.add newCall("flushRecord", record, chroniclesTopicsMatchVar)
else:
code.add newCall("logAllDynamicProperties", Stream, record)
code.add newCall("flushRecord", record)
code.add newCall("logAllDynamicProperties", Stream, record)
code.add newCall("flushRecord", record)

result.add quote do:
try:
block `chroniclesBlockName`:
`code`
except CatchableError as err:
logLoggingFailure(cstring(`eventName`), err)
result.add quote do:
try:
block `chroniclesBlockName`:
`code`
except CatchableError as err:
logLoggingFailure(cstring(`eventName`), err)

when defined(debugLogImpl):
echo result.repr
when defined(debugLogImpl):
echo result.repr

# Translate all the possible overloads to `logIMPL`:
template log*(lineInfo: static InstInfo,
severity: static[LogLevel],
eventName: static[string],
props: varargs[untyped]) {.dirty.} =

bind logIMPL, bindSym, brForceOpen
logIMPL(lineInfo, activeChroniclesStream(),
Record(activeChroniclesStream()), eventName, severity,
bindSym("activeChroniclesScope", brForceOpen), props)
when loggingEnabled:
bind logIMPL, bindSym, brForceOpen
logIMPL(lineInfo, activeChroniclesStream(),
Record(activeChroniclesStream()), eventName, severity,
bindSym("activeChroniclesScope", brForceOpen), props)

template log*(lineInfo: static InstInfo,
stream: type,
severity: static[LogLevel],
eventName: static[string],
props: varargs[untyped]) {.dirty.} =

bind logIMPL, bindSym, brForceOpen
logIMPL(lineInfo, stream, stream.Record, eventName, severity,
bindSym("activeChroniclesScope", brForceOpen), props)
when loggingEnabled:
bind logIMPL, bindSym, brForceOpen
logIMPL(lineInfo, stream, stream.Record, eventName, severity,
bindSym("activeChroniclesScope", brForceOpen), props)

template wrapSideEffects(debug: bool, body: untyped) {.inject.} =
when debug:
Expand Down Expand Up @@ -393,4 +401,3 @@ logFn fatal , LogLevel.FATAL
# between dynamic and lexical bindings)
#
# * implement some of the leading standardized structured logging formats

Loading