Logging
Write log messages with levels.
On this page
valk.log writes log records with a level, a message and fields. Records go to standard error as one line each, as readable text or as JSON.
Write a record#
Valk
use valk.log
fn main() {
log.info("server started", .{ "port" => 8080, "host" => "example.com" })
// 2026-03-30T07:00:00.250Z INFO server started port=8080 host=example.com
log.warn("disk almost full")
log.debug("not written")
}
The levels are debug, info, warn and error, each with a function of that name. Fields are a map of text, numbers, bools or null, and are optional. Records below the current level, info at the start, are dropped.
Settings#
Valk
use valk.log
fn main() {
log.set_level(log.Level.debug)
log.set_format(log.Format.json)
log.debug("cache miss", .{ "key" => "user:17" })
// {"time":"...","level":"debug","msg":"cache miss","key":"user:17"}
let level = log.parse_level("warn") !? log.Level.info
log.set_level(level)
}
| Function | Effect |
|---|---|
set_level(level) |
Lowest level that is written |
set_format(format) |
Format.text or Format.json (fields keep their type) |
set_local_time(true) |
Timestamps in the local time zone instead of UTC |
to_file(path) |
Append to a file, created when missing |
to_stdout(), to_stderr() |
Write to standard output or standard error |
parse_level(name) |
Read a level name, for example from an environment variable |
enabled(level) |
Whether a level is written, to skip preparing expensive fields |
Settings apply to every thread, and records from different threads never mix.
Add context to every record#
Valk
use valk.log
fn main() {
let request = log.with(.{ "request" => "r-17" })
request.info("query done", .{ "ms" => 12 })
// 2026-03-30T07:00:00.250Z INFO query done request=r-17 ms=12
}
with returns a logger that adds its fields to each record it writes. Calling with on that logger adds more.
Your own logger#
Valk
use valk.log
fn main() {
let out = ByteBuffer.new(256)
let logger = log.Logger.new(out, log.Level.debug, log.Format.text)
logger.debug("step", .{ "n" => 1 })
print(out.to_string()) // 2026-03-30T07:00:00.250Z DEBUG step n=1
}
Logger.new writes to any io.Writer, such as a file stream or a buffer, with its own level and format. Its level, format and local_time fields can be changed later.
