WIP support for user-defined record types

This commit is contained in:
Zahary Karadjov 2018-04-29 21:02:47 +03:00
parent c7f2d2228c
commit c91207357c
4 changed files with 91 additions and 23 deletions

View File

@ -8,9 +8,9 @@ export
template chroniclesLexScopeIMPL* =
0 # scope revision number
macro mergeScopes(scopes: typed, newBindings: untyped): untyped =
macro mergeScopes(prevScopes: typed, newBindings: untyped): untyped =
var
bestScope = scopes.lastScopeHolder
bestScope = prevScopes.lastScopeHolder
bestScopeRev = bestScope.scopeRevision
var finalBindings = initTable[string, NimNode]()
@ -20,12 +20,18 @@ macro mergeScopes(scopes: typed, newBindings: untyped): untyped =
for k, v in assignments(newBindings):
finalBindings[k] = v
result = newStmtList()
var newScopeDefinition = newStmtList(newLit(bestScopeRev + 1))
for k, v in finalBindings:
newScopeDefinition.add newAssignment(newIdentNode(k), v)
if k == "stream":
let streamId = newIdentNode($v)
result.add quote do:
template chroniclesActiveStreamIMPL: typedesc = `streamId`
else:
newScopeDefinition.add newAssignment(newIdentNode(k), v)
result = quote:
result.add quote do:
template chroniclesLexScopeIMPL = `newScopeDefinition`
template logScope*(newBindings: untyped) {.dirty.} =
@ -37,10 +43,10 @@ template dynamicLogScope*(bindings: varargs[untyped]) {.dirty.} =
bind bindSym, brForceOpen
dynamicLogScopeIMPL(bindSym("chroniclesLexScopeIMPL", brForceOpen), bindings)
macro logImpl(severity: LogLevel, scopes: typed,
macro logImpl(activeStream: typed, severity: LogLevel, scopes: typed,
logStmtBindings: varargs[untyped]): untyped =
if not loggingEnabled: return
let lexicalBindings = scopes.finalLexicalBindings
var finalBindings = initOrderedTable[string, NimNode]()
@ -95,7 +101,10 @@ macro logImpl(severity: LogLevel, scopes: typed,
template log*(severity: LogLevel, props: varargs[untyped]) {.dirty.} =
bind logImpl, bindSym, brForceOpen
logImpl(severity, bindSym("chroniclesLexScopeIMPL", brForceOpen), props)
logImpl(chroniclesActiveStreamIMPL(),
severity,
bindSym("chroniclesLexScopeIMPL", brForceOpen),
props)
template logFn(name, severity) =
template `name`*(props: varargs[untyped]) =

View File

@ -36,7 +36,7 @@ proc selectOutputType(dst: LogDestination): NimNode =
of toSysLog: bnd"SysLogOutput"
of toFile: newTree(nnkBracketExpr, bnd"FileOutput", newLit(dst.outputId))
proc selectRecordType(s: StreamSpec, sinkIdx: int): NimNode =
proc selectRecordType(sink: SinkSpec): NimNode =
# This proc translates the SinkSpecs loaded in the `options` module
# to their corresponding LogRecord types.
#
@ -48,8 +48,6 @@ proc selectRecordType(s: StreamSpec, sinkIdx: int): NimNode =
# BufferedOutput[(Output1, Output2, ...)]
#
let sink = s.sinks[sinkIdx]
# Determine the head symbol of the instantiation
let recordType = case sink.format
of json: bnd"JsonRecord"
@ -303,27 +301,49 @@ template flushRecord*(r: var JsonRecord) =
# configured output stream.
#
proc createCompositeLogRecord(s: StreamSpec): NimNode =
if s.sinks.len > 1:
proc createCompositeLogRecord(sinks: seq[SinkSpec]): NimNode =
if sinks.len > 1:
result = newTree(nnkPar)
for i in 0 ..< s.sinks.len:
result.add s.selectRecordType(i)
for i in 0 ..< sinks.len:
result.add selectRecordType(sinks[i])
else:
result = s.selectRecordType(0)
result = selectRecordType(sinks[0])
template recordTypeName*(s: StreamSpec): string =
s.name & "LogRecord"
import dynamic_scope_types
template createStreamSymbol(name: untyped, RecordType: typedesc) =
type `name` {.inject.} = object
template chroniclesLogRecordTypeIMPL*(T: type `name`): typedesc = RecordType
template chroniclesSinksCountIMPL*(T: type `name`): int = 1
var rootDynScope {.threadvar.}: ptr BindingsFrame[RecordType]
template tlsSlot*(T: type RecordType): auto = rootDynScope
macro customLogStream*(streamDef: untyped): untyped =
syntaxCheckStreamExpr streamDef
return newCall(bindSym"createStreamSymbol", streamDef[0], streamDef[1])
macro logStream*(streamDef: untyped): untyped =
syntaxCheckStreamExpr streamDef
let streamSinks = sinkSpecsFromNode(streamDef)
return newCall(bindSym"createStreamSymbol",
streamDef[0],
createCompositeLogRecord(streamSinks))
macro createStreamRecordTypes: untyped =
result = newStmtList()
for s in config.streams:
for i in 0 ..< config.streams.len:
let
s = config.streams[i]
streamName = newIdentNode(s.name)
typeName = newIdentNode(s.recordTypeName)
tlsSlot = newIdentNode($typeName & "TlsSlot")
typeDef = createCompositeLogRecord(s)
typeDef = createCompositeLogRecord(s.sinks)
result.add quote do:
type `typeName`* = `typeDef`
@ -344,6 +364,12 @@ macro createStreamRecordTypes: untyped =
template flushRecord*(r: var `typeName`) =
for f in r.fields: flushRecord(f)
createStreamSymbol(`streamName`, `typeName`)
if i == 0:
result.add quote do:
template chroniclesActiveStreamIMPL*: typedesc = `streamName`
createStreamRecordTypes()
#

View File

@ -142,7 +142,12 @@ const
defaultColorScheme = when handleYesNoOption(chronicles_colors): AnsiColors
else: NoColors
proc sinkSpecsFromNode(streamNode: NimNode): seq[SinkSpec] =
proc syntaxCheckStreamExpr*(n: NimNode) =
if n.kind != nnkBracketExpr or n[0].kind != nnkIdent:
error &"Invalid stream definition. " &
"Please use a bracket expressions such as 'stream_name[sinks_list]'."
proc sinkSpecsFromNode*(streamNode: NimNode): seq[SinkSpec] =
newSeq(result, 0)
for i in 1 ..< streamNode.len:
let n = streamNode[i]
@ -163,10 +168,7 @@ proc parseStreamsSpec(spec: string): Configuration {.compileTime.} =
newSeq(result.streams, 0)
var specNodes = parseExpr "(" & spec.replace("\\", "/") & ")"
for n in specNodes:
if n.kind != nnkBracketExpr or n[0].kind != nnkIdent:
error &"Invalid stream definition. " &
"Please use a bracket expressions such as 'stream_name[sinks_list]'."
syntaxCheckStreamExpr(n)
let streamName = $n[0]
for prev in result.streams:
if prev.name == streamName:

31
tests/custom_stream.nim Normal file
View File

@ -0,0 +1,31 @@
import
chronicles
type
MyRecord = object
template initLogRecord*(r: var MyRecord, lvl: LogLevel, name: string) =
stdout.write "[", lvl, "] ", name, ": "
template setPropertyImpl(r: var MyRecord, key: string, val: auto) =
stdout.write key, "=", val
template setFirstProperty*(r: var MyRecord, key: string, val: auto) =
stdout.write " ("
setPropertyImpl(r, key, val)
template setProperty*(r: var MyRecord, key: string, val: auto) =
stdout.write ", "
setPropertyImpl(r, key, val)
template flushRecord*(r: var MyRecord) =
stdout.write ")\n"
customLogStream myStream[MyRecord]
logScope:
stream = "myStream"
info "test"
# myStream.info "info panel"