From c91207357c2d0046d2de3f940d48c07cce73044f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 29 Apr 2018 21:02:47 +0300 Subject: [PATCH] WIP support for user-defined record types --- chronicles.nim | 25 ++++++++++++++------- chronicles/log_output.nim | 46 ++++++++++++++++++++++++++++++--------- chronicles/options.nim | 12 +++++----- tests/custom_stream.nim | 31 ++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 tests/custom_stream.nim diff --git a/chronicles.nim b/chronicles.nim index 52bf380..a2ce896 100644 --- a/chronicles.nim +++ b/chronicles.nim @@ -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]) = diff --git a/chronicles/log_output.nim b/chronicles/log_output.nim index 04a2d67..fef8c57 100644 --- a/chronicles/log_output.nim +++ b/chronicles/log_output.nim @@ -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() # diff --git a/chronicles/options.nim b/chronicles/options.nim index d5242a6..fb99ac7 100644 --- a/chronicles/options.nim +++ b/chronicles/options.nim @@ -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: diff --git a/tests/custom_stream.nim b/tests/custom_stream.nim new file mode 100644 index 0000000..c3c44b7 --- /dev/null +++ b/tests/custom_stream.nim @@ -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" +