std.debug

import std.debug

debug.inspect(value)        # a readable rendering of any value
debug.type(value)           # the type name
debug.timer("load")         # start a named timer
debug.elapsed("load")       # milliseconds since it started

inspect

Renders any value readably, including nested maps and lists. Two things it does that print does not: it shows structure clearly for deep values, and it is secret-safe - a secret renders as [secret], so inspecting a config map cannot dump a credential into your terminal or a log.

That safety is why inspect is the right default for debugging a value whose contents you are unsure about.

type

The type name as a string. The global type_of does the same thing and is always in scope.

Timers

debug.timer("query")
rows = sql.query(db, "select * from big")
print("took {debug.elapsed('query')}ms")

Named, so several can overlap. Backed by a monotonic clock, so a system clock adjustment cannot produce a negative duration.

This is for debugging

Timers here are for a quick "where is the time going" answer. For measurement you intend to act on - comparing implementations, tracking a regression - use the perf package, which does best-of-N runs and reports statistics rather than a single sample.

A single timing is noise. That is not a limitation of this module, it is a property of measuring anything once.

Leaving it in

debug output goes to stdout, so it becomes part of your program's output. Deterministic output matters for testable programs - see ecko test - so remove timers before asserting on output, or use std.log, which writes to stderr and is level-filtered.