I'm trying to apply red color to error text , yellow to warn, etc.
{.experimental: "dynamicBindSym".}
import logging
import macros
export logging
var consoleLog = newConsoleLogger(
fmtStr = "[$time] "
)
addHandler(consoleLog)
proc red*(s: string): string = "\e[31m" & s & "\e[0m"
proc yellow*(s: string): string = "\e[93m" & s & "\e[0m"
macro log(lvl, color: static[string], args: varargs[typed]) : untyped =
var call = nnkCall.newTree(
newDotExpr(
newIdentNode("logging"),
newIdentNode(lvl), # logging.error / logging.warn ...
),
)
for arg in args:
let strArg = newCall(bindSym"$", arg)
let redArg = newCall(bindSym color, strArg)
call.add(redArg)
call.add(newStrLitNode(" "))
nnkStmtList.newTree(call)
macro error*(args: varargs[typed]) : untyped =
log("error", "red", args)
macro warn*(args: varargs[typed]) : untyped =
log("warn", "yellow", args)
Usage:
var list = @[1,2,3]
log "error", "red", "aaa", 222, list # <-------- this works
error "aaa", 222, list # <-------- this doesn't work
The problem is this:
macro log(lvl, color: static[string], ...
The params "lvl" and "color", they are not evaluated at compile time. The param "color" works with the flag {.experimental: "dynamicBindSym".} enabled because it's only used in this file. But the "lvl" still doesn't work, it's used in the std/logging module, it shows error:
How to fix that? Thanks.
In addition to leveraging std/terminal for writing styled text to the terminal. I would recommend avoing a macro in this case.
You should be able to achieve this with a custom logger that inherits from std/logging.Logger
type StyledLogger* = ref object of Logger
# fields
method log*(logger: StyledLogger, level: Level, args: varargs[string, `$`]) {.gcsafe.} =
# impl
This shows how to avoid excessive allocations during logging as well, by writing directly to a stream instead of allocating lots of little strings, which is what varargs[string, $] will do
I would recommend avoing a macro in this case.
Makes sense, I was trying to learn macro but inheritance seems better here, thanks.
This shows how to avoid excessive allocations during logging as well, by writing directly to a stream instead of allocating lots of little strings
Thank you for the detailed information, I'll look into that.
I've tried chronicles and some other libraries by status-im, they are very comprehensive. The only problem is the compiling speed. import chronicles brings noticeable deleay(~200ms) when compiling with nim c -r ..., it becomes quite significant when these small delays add up. For example, import json_rpc costs 5 seconds. I'm trying to avoid that by implementing my own simple libraries.
I've tried the experimental compiling flag --incremental:on, but it doesn't even work with hello world.
I wonder how you use these large libraries? Just compile and wait, or is there any magic here?
I wonder how you use these large libraries? Just compile and wait, or is there any magic here?
Compile-and-wait, indeed. When writing these libraries, we trade correctness, comprehensiveness and runtime performance for compilation speed, in many cases.
implementing my own
Two relevant questions here are:
Nim is not a fast compiler, by any measure - the only reason you don't see longer compile times is that the programs you've been compiling so far have not yet reached any significant complexity - if they do, you will be facing the same slow compile times as the status libraries do.
Writing your own library can be satisfying in many ways, but from experience, compile-time speed is probably not the best reason to do so ;)
Compile-and-wait, indeed.
Thanks for confirming, then I'll stop looking for workarounds..
when you're done implementing all the features you want at the desired runtime performance level, will your libraries still be small?
Good point. I'll try it first, my projects are rather simple, I can accept a bit performance loss. Plus, stealing code from other libraries is a good way to learn :)
If it takes 5s to compile something, how many times must you compile in order to make up for the total time you will spend implementing, bugfixing and maintaining your own?
It just feels uncomfortable. I switched to Nim from Go partly because repeating if err != nil endlessly makes me feel #$@%... Just like the delays, the minor discomforts add up over time and eventually I'll have do something, I rather do it now. Compared to that, spending more time is fine to me.
This is temporary, it will no longer be a problem once Nimony is released, I suppose..
Thanks again for your help.
stealing code from other libraries
that's what it's there for :) If you find anything that is redundant and can be made faster to compile, we're always interested in a patch - compile time is something we measure regularly, it's just not the first thing we measure ;)
200ms for import chronicles sounds about accurate btw, just tested - given that import std/[tables,os,strformat] together is ~1.2s, it's not outrageous at least.
If you find anything that is redundant and can be made faster to compile, we're always interested in a patch
I'd try :-)