2) log(level, msg) or both?
e,g, https://github.com/Araq/Nimrod/blob/master/devel/logging.nim#L120
template log*(level: TLevel, msg: string) =
## logs a message of the given level
bind logLoop
if level >= logging.Level:
logLoop(level, frmt, args)
An update, I have forked Nimrod repo, and made some changes to logging.nim, https://github.com/exhu/Nimrod/tree/logging
Made it compile, but did not test. I have recently had little time to work on hobby projects, so the progress here is poor but if someone is eager and has time to complete logging facility quickly then this bit of work can help.
template fatal*(frmt: string, args: openarray[string]) =
In current interface you need to make an open array of strings, I want to make it an openarray of something like variant types, so that I can pass ints, objects etc without previously converting them to strings. Or do you mean the openarray will be optimized away after inlining the template?
In current interface you need to make an open array of strings, I want to make it an openarray of something like variant types, so that I can pass ints, objects etc without previously converting them to strings.
There is a feature planned to assist with this, but Nimrod's variant is called string. ;-) If the logger needs to output the value, it needs to convert it to a string anyway.
Or do you mean the openarray will be optimized away after inlining the template?
I mean that the array of strings is not constructed if the current logging level disables the output.
In this code global variable "level" hides template argument with the same name, is it the intended behaviour?
var
level* = lvlNone
handlers*: seq[ref TLogger] = @[]
proc logLoop(level: TLevel, frmt: string, args: openarray[string]) =
for logger in items(handlers):
if level >= logger.levelThreshold:
log(logger, level, frmt, args)
template log*(level: TLevel, frmt: string, args: openarray[string]) =
## logs a message of the given level
bind logLoop
if level >= logging.Level: # seems like level and logging.Level are the same here, compiler bug?
var s : string = "logging.level = $1, level = $2" % [$logging.level, $level]
writeln(stdout,s)
logLoop(level, frmt, args)
And this template uses percent operator from strutils, how can I bind it, so that client code is not required to import strutils?
It's no bug, but a bit subtle:
# tstempl.nim
import strutils
type TLev = enum
levA,
levB
var abclev : TLev = levB
template tstLev(abclev:TLev) =
writeln(stdout, "global = $1, arg = $2, test = $3" % [$tstempl.abclev, $abclev, $(tstempl.abclev == abclev)]) # evaluates to true, but must be false
tstLev(levA)
is transformed to:
writeln(stdout, "global = $1, arg = $2, test = $3" % [$tstempl.levA, $levA, $(tstempl.levA == levA)])
A template's parameter p is even substituted in x.p. This feature allows to pass a field name to a template. However I improved the bind feature, so you can do bind tstempl.abclev to get the desired behaviour.
Great! This should be mentioned in the docs doc/manual.txt:
Templates section (line 2789):
Note: template arguments can be used as field names and a global symbol can be
screened by the same argument name even when fully qualified.
See "tests/run/tstempl.nim"
Bind statement section (line 2916):
Note: you can use "bind moduleName.symbolName" to access a global without substitution.
Probably better wording is needed, because I'm not a native English speaker.
proc myWriteln(f: TFile, a: varargs[string, `$`]) =
for s in items(a):
write(f, s)
write(f, "\n")
myWriteln(stdout, 123, "abc", 4.0)
# is transformed to:
myWriteln(stdout, [$123, $"def", $4.0])
This feature is quite useful for formatted output and logging.
Of course something like varargs[Variant, toVariant] is possible too.