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
|
use memio;
use time::date;
use fmt;
use io;
use strings;
use trace;
type Tracer = struct {
trace::tracer,
sink: io::handle,
};
fn newtracer(sink: io::handle) Tracer =
Tracer {
log = &tracer_log,
sink = sink,
};
fn tracer_log(
tr: *trace::tracer,
ctx: nullable *trace::context,
lvl: trace::level,
fmt: str,
fields: fmt::field...
) void = {
const tr = tr: *Tracer;
// XXX: ideally there would be a statically allocated buffer for
// reasonably short messages and allocation would only happen for
// messages longer than that. ideally ideally there would be locking so
// we wouldn't need to allocate at all.
const buf = memio::dynamic_from(alloc([], 256));
defer io::close(&buf)!;
const now = date::now();
io::write(&buf, [0x5b])!; // "["
date::format(&buf, date::STAMP, &now)!;
io::write(&buf, [0x5d, 0x20])!; // "] "
const lvlstr = switch (lvl) {
case trace::level::ERROR =>
yield "error: ";
case trace::level::WARN =>
yield "warning: ";
case trace::level::INFO =>
yield "info: ";
case trace::level::DEBUG =>
yield "debug: ";
case trace::level::TRACE =>
yield "trace: ";
};
io::write(&buf, strings::toutf8(lvlstr))!;
let ctx_ = ctx;
for (true) match (ctx_) {
case let ctx__: *trace::context =>
fmt::fprintf(&buf, ctx__.fmt, ctx__.fields...)!;
io::write(&buf, [0x3a, 0x20])!; // ": "
ctx_ = ctx__.next;
case null => break;
};
fmt::fprintfln(&buf, fmt, fields...)!;
io::write(tr.sink, memio::buffer(&buf)): void;
};
|