-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcutelog.nim
155 lines (139 loc) · 4.5 KB
/
cutelog.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import std/terminal
import std/strutils
import std/logging
export logging
type
CuteLogger* = ref object of Logger
forward: Logger
CuteLoggerPrefixer* = proc (level: Level): string {.raises: [], noSideEffect.}
CuteLoggerPainter* = proc (level: Level): CutePalette {.raises: [], noSideEffect.}
CutePalette* = tuple
style: set[Style]
fg: ForegroundColor
bg: BackgroundColor
type
CuteConsoleLogger* = ref object of ConsoleLogger
prefixer: CuteLoggerPrefixer
painter: CuteLoggerPainter
func emojiPrefix(level: Level): string {.used.} =
case level:
of lvlFatal: # use this level for our most critical outputs
result = "" # and don't prefix them with a glyph
of lvlError:
result = "💥"
of lvlWarn:
result = "⚠️"
of lvlNotice:
result = "❌"
of lvlInfo:
result = "✔️"
of lvlDebug:
result = "🐞"
of lvlAll, lvlNone:
discard
method log*(logger: CuteLogger; level: Level; args: varargs[string, `$`]) =
## anything that isn't fatal gets a cute emoji
var
arguments: seq[string]
for a in args.items:
arguments.add a
when defined(cutelogEmojis):
let prefix = level.emojiPrefix
else:
const prefix = ""
# separate logging arguments with spaces for convenience
logger.forward.log(level, prefix & arguments.join(" "))
proc painter*(level: Level): CutePalette =
result = (style: {}, fg: fgDefault, bg: bgDefault)
case level:
of lvlFatal:
result.fg = fgWhite
of lvlError:
result.style.incl styleBright
result.style.incl styleItalic
result.fg = fgRed
of lvlWarn:
result.style.incl styleBright
result.fg = fgYellow
of lvlNotice:
result.fg = fgCyan
result.style.incl styleItalic
of lvlInfo:
result.fg = fgGreen
of lvlDebug:
result.fg = fgBlue
of lvlAll, lvlNone:
discard
when defined(cutelogMonochrome):
result.fg = fgDefault
result.bg = bgDefault
when defined(cutelogBland):
result.style = {}
# use a lock to avoid thread contention on tty/stderr
when compileOption"threads" and not defined(cutelogNoLock):
import std/rlocks
var clobber {.global.}: RLock
initRLock clobber
template noclobber*(body: untyped) =
## serialize access to the body; usually for output reasons
withRLock clobber:
body
else:
template noclobber*(body: untyped) = body
method log*(logger: CuteConsoleLogger; level: Level; args: varargs[string, `$`]) =
## use color and a prefix func to log
let
prefix = logger.prefixer(level)
palette = logger.painter(level)
template output: untyped =
if logger.useStderr:
stderr
else:
stdmsg()
var
arguments: seq[string]
for a in args:
arguments.add a
var ln: string
try:
ln = substituteLog(logger.fmtStr, level, arguments)
except OsError: # really...
ln = arguments.join " "
template ttyWrap(logic: untyped): untyped {.dirty.} =
## setting/resetting terminal styling as necessary
noclobber: # use the lock around output
if output.isatty:
output.resetAttributes
output.setForegroundColor(palette.fg,
bright = styleBright in palette.style)
output.setBackgroundColor(palette.bg,
bright = false)
output.setStyle(palette.style)
logic
output.resetAttributes()
else:
logic
ttyWrap:
# separate logging arguments with spaces for convenience
output.writeLine(prefix & ln)
proc newCuteLogger*(console: ConsoleLogger): CuteLogger =
## create a new logger instance which forwards to the given console logger
result = CuteLogger(forward: console)
proc newCuteConsoleLogger*(prefixer: CuteLoggerPrefixer;
painter: CuteLoggerPainter;
levelThreshold = lvlAll; fmtStr = "";
useStderr = true): CuteConsoleLogger =
result = CuteConsoleLogger(levelThreshold: levelThreshold,
prefixer: prefixer, painter: painter,
fmtStr: fmtStr, useStderr: useStderr)
proc newCuteConsoleLogger*(levelThreshold = lvlAll; fmtStr = "";
useStderr = true): CuteConsoleLogger =
var
prefixer: CuteLoggerPrefixer
when defined(cutelogEmojis):
prefixer = emojiPrefix
else:
prefixer = func (level: Level): string = ""
result = newCuteConsoleLogger(levelThreshold = levelThreshold, fmtStr = fmtStr,
prefixer = prefixer, painter = painter,
useStderr = useStderr)