woof
Types
Controls whether ANSI colors are used in Text output.
pub type ColorMode {
Auto
Always
Never
}
Constructors
-
AutoAuto-detect: colors are enabled when stdout is a TTY and the
NO_COLORenvironment variable is not set. -
AlwaysAlways emit ANSI color codes, even when piped to a file.
-
NeverNever emit ANSI color codes.
A fully resolved log entry, ready to be formatted.
This type is public so that Custom formatters can pattern-match on it
and arrange the data however they like.
pub type Entry {
Entry(
level: Level,
message: String,
fields: List(#(String, String)),
namespace: option.Option(String),
timestamp: String,
)
}
Constructors
-
Entry( level: Level, message: String, fields: List(#(String, String)), namespace: option.Option(String), timestamp: String, )
Controls how log output is formatted.
Text— human-readable lines, great for development.Json— one JSON object per line, great for production and log aggregation tools.Compact— single-line, key=value pairs. A middle ground betweenTextreadability andJsonparsability.Custom— bring your own formatter. The function receives a fully assembledEntryand must return the string to print. This is the escape hatch for integrating with other formatting or output libraries.
pub type Format {
Text
Json
Compact
Custom(formatter: fn(Entry) -> String)
}
Constructors
-
Text -
Json -
Compact -
Custom(formatter: fn(Entry) -> String)
Log severity levels, ordered from least to most severe.
Only messages at or above the configured minimum level are emitted.
The default level is Debug (everything is printed).
pub type Level {
Debug
Info
Warning
Error
}
Constructors
-
Debug -
Info -
Warning -
Error
Values
pub fn bool_field(key: String, value: Bool) -> #(String, String)
Create a field from a Bool.
woof.info("Cache", [woof.bool_field("hit", True)])
pub fn configure(config: Config) -> Nil
Replace the current configuration.
This sets level, format, and color mode at once. Global context is
left untouched — use set_global_context if you need to change it.
woof.configure(woof.Config(
level: woof.Info,
format: woof.Json,
colors: woof.Auto,
))
pub fn debug(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at Debug level.
woof.debug("Cache hit", [#("key", cache_key)])
pub fn debug_lazy(
build: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Log at Debug level, evaluating the message only if Debug is enabled.
Use this when building the message string is expensive.
woof.debug_lazy(fn() { expensive_debug_dump() }, [])
pub fn error(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at Error level.
woof.error("Connection lost", [#("host", "db01")])
pub fn error_lazy(
build: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Log at Error level, evaluating the message only if Error is enabled.
pub fn field(key: String, value: String) -> #(String, String)
Create a string field. Same as writing #(key, value) directly, but
reads nicely alongside the typed helpers.
woof.info("Request", [woof.field("path", "/api/users")])
pub fn float_field(
key: String,
value: Float,
) -> #(String, String)
Create a field from a Float.
woof.info("Timing", [woof.float_field("duration_ms", 12.5)])
pub fn format(entry: Entry, output_format: Format) -> String
Format an entry without emitting it.
Handy for testing, previews, or sending formatted output to a custom sink (file, HTTP, etc.).
let line = woof.format(entry, woof.Json)
pub fn info(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at Info level.
woof.info("User signed in", [#("user_id", id)])
pub fn info_lazy(
build: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Log at Info level, evaluating the message only if Info is enabled.
pub fn int_field(key: String, value: Int) -> #(String, String)
Create a field from an Int.
woof.info("Response", [woof.int_field("status", 200)])
pub fn level_name(level: Level) -> String
Return the lowercase name of a level.
Useful inside Custom formatters.
woof.level_name(woof.Warning) // -> "warning"
pub fn log(
logger: Logger,
level: Level,
message: String,
fields: List(#(String, String)),
) -> Nil
Log a message through a namespaced logger.
let log = woof.new("http")
log |> woof.log(woof.Warning, "Slow response", [#("ms", "1200")])
pub fn log_error(
res: Result(a, b),
message: String,
fields: List(#(String, String)),
) -> Result(a, b)
If the Result is Error, log the message at Error level and pass
the original value through — useful in result pipelines.
fetch_data()
|> woof.log_error("Failed to fetch data", [])
|> result.unwrap(default)
pub fn new(namespace: String) -> Logger
Create a namespaced logger.
The namespace is prepended to every message formatted with Text and
included as a "ns" field in Json output.
let log = woof.new("database")
log |> woof.log(woof.Info, "Connected", [#("host", "localhost")])
pub fn set_colors(mode: ColorMode) -> Nil
Set the color mode.
Colors only affect Text output — Json and Compact are never colored.
woof.set_colors(woof.Never)
pub fn set_format(format: Format) -> Nil
Set the output format.
woof.set_format(woof.Json)
pub fn set_global_context(fields: List(#(String, String))) -> Nil
Set fields that appear on every log message globally.
Typically called once at application start.
woof.set_global_context([
#("app", "my-service"),
#("version", "1.2.0"),
])
pub fn set_level(level: Level) -> Nil
Set the minimum log level.
Messages below this level are silently dropped with near-zero overhead.
woof.set_level(woof.Warning)
pub fn tap_debug(
value: a,
message: String,
fields: List(#(String, String)),
) -> a
Log the value at Debug level and pass it through.
pub fn tap_error(
value: a,
message: String,
fields: List(#(String, String)),
) -> a
Log the value at Error level and pass it through.
pub fn tap_info(
value: a,
message: String,
fields: List(#(String, String)),
) -> a
Log the value at Info level and pass it through. Fits naturally in pipelines.
fetch_user(id)
|> woof.tap_info("Fetched user", [])
|> transform_user()
pub fn tap_warning(
value: a,
message: String,
fields: List(#(String, String)),
) -> a
Log the value at Warning level and pass it through.
pub fn time(label: String, body: fn() -> a) -> a
Measure how long body takes and log it at Info level.
Returns whatever body returns — the timing log is a side effect.
use <- woof.time("db_query")
database.query(sql)
pub fn warning(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at Warning level.
woof.warning("Rate limit at 90 %", [#("limit", "100/min")])
pub fn warning_lazy(
build: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Log at Warning level, evaluating the message only if Warning is enabled.
pub fn with_context(
fields: List(#(String, String)),
body: fn() -> a,
) -> a
Run body with extra fields attached to every log call inside it.
Fields from the context are merged with inline fields. If a key appears in both, the inline value wins (it comes last in the list).
Contexts can be nested — inner fields accumulate on top of outer ones.
On the BEAM each process gets its own context (process dictionary), so concurrent request handlers never interfere with each other.
use <- woof.with_context([#("request_id", req.id)])
woof.info("Processing", []) // includes request_id automatically