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; };