viva_telemetry/log
viva_telemetry/log - Structured logging for Gleam
Inspired by: structlog (Python), zap (Go), tracing (Rust)
Features
- Structured logging with key-value fields
- Multiple handlers (console, file, JSON)
- Log levels (RFC 5424)
- Context propagation
- Sampling for high-volume logs
- Erlang
:loggerforwarding for BEAM applications
Quick Start
import viva_telemetry/log
pub fn main() {
log.configure_erlang(log.info_level)
log.info("Server started", [#("port", "8080")])
let logger =
log.logger("app.http")
|> log.with_field("request_id", "abc123")
logger
|> log.logger_info_with("Request completed", [#("status", "200")])
}
Types
Values
pub fn add_handler(handler: handler.Handler) -> Nil
Add a handler to the existing configuration
pub fn alert(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at alert level
pub fn alert_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy alert
pub fn bind_context(context: List(#(String, String))) -> Nil
Add context fields that persist for this process
pub fn configure(handlers: List(handler.Handler)) -> Nil
Configure handlers for the current process.
Logger configuration and context are process-local. Child or worker
processes should configure logging themselves or use Erlang :logger
forwarding through configure_erlang.
pub fn configure_console(lvl: level.Level) -> Nil
Quick setup: console handler with specified level
Example:
log.configure_console(log.debug_level)
pub fn configure_erlang(lvl: level.Level) -> Nil
Quick setup: forward structured logs to Erlang’s built-in :logger
This is the recommended production integration when running on the BEAM.
pub fn configure_erlang_with_name(
lvl: level.Level,
name: String,
) -> Nil
Quick setup: forward structured logs to Erlang’s built-in :logger with a
custom logger name attached as metadata.
pub fn configure_full(
console_level: level.Level,
json_path: String,
json_level: level.Level,
) -> Nil
Quick setup: console + JSON file
Example:
log.configure_full(log.debug_level, "app.jsonl", log.info_level)
pub fn configure_json(path: String, lvl: level.Level) -> Nil
Quick setup: JSON file handler with specified level
Example:
log.configure_json("app.jsonl", log.info_level)
pub fn critical(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at critical level
pub fn critical_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy critical
pub fn debug(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at debug level
pub fn debug_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy debug - most common use case for lazy logging
Example:
// String only constructed if debug level is enabled
log.debug_lazy(fn() { "Item: " <> int.to_string(i) }, [])
pub fn emergency(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at emergency level
pub fn emergency_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy emergency
pub fn error(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at error level
pub fn error_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy error
pub fn info_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy info
pub fn log(
lvl: level.Level,
message: String,
fields: List(#(String, String)),
) -> Nil
Log with explicit level
pub fn log_from(
source: String,
lvl: level.Level,
message: String,
fields: List(#(String, String)),
) -> Nil
Log with source module
pub fn log_lazy(
lvl: level.Level,
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy log - only evaluates message function if log will be emitted
Example:
log.debug_lazy(fn() { "Processing " <> expensive_to_string(data) }, [])
pub fn log_lazy_all(
lvl: level.Level,
message_fn: fn() -> String,
fields_fn: fn() -> List(#(String, String)),
) -> Nil
Lazy log with lazy fields - both message and fields evaluated only if needed
pub fn logger_alert(logger: Logger, message: String) -> Logger
Log an alert message with a named logger.
pub fn logger_alert_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log an alert message with additional one-off fields.
pub fn logger_critical(logger: Logger, message: String) -> Logger
Log a critical message with a named logger.
pub fn logger_critical_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log a critical message with additional one-off fields.
pub fn logger_debug(logger: Logger, message: String) -> Logger
Log a debug message with a named logger.
pub fn logger_debug_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log a debug message with additional one-off fields.
pub fn logger_emergency(
logger: Logger,
message: String,
) -> Logger
Log an emergency message with a named logger.
pub fn logger_emergency_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log an emergency message with additional one-off fields.
pub fn logger_error(logger: Logger, message: String) -> Logger
Log an error message with a named logger.
pub fn logger_error_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log an error message with additional one-off fields.
pub fn logger_info(logger: Logger, message: String) -> Logger
Log an info message with a named logger.
pub fn logger_info_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log an info message with additional one-off fields.
pub fn logger_log(
logger: Logger,
lvl: level.Level,
message: String,
) -> Logger
Log with any level using a named logger.
pub fn logger_log_with(
logger: Logger,
lvl: level.Level,
message: String,
fields: List(#(String, String)),
) -> Logger
Log with any level and one-off fields using a named logger.
pub fn logger_notice(logger: Logger, message: String) -> Logger
Log a notice message with a named logger.
pub fn logger_notice_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log a notice message with additional one-off fields.
pub fn logger_trace(logger: Logger, message: String) -> Logger
Log a trace message with a named logger.
pub fn logger_trace_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log a trace message with additional one-off fields.
pub fn logger_warning(logger: Logger, message: String) -> Logger
Log a warning message with a named logger.
pub fn logger_warning_with(
logger: Logger,
message: String,
fields: List(#(String, String)),
) -> Logger
Log a warning message with additional one-off fields.
pub fn notice(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at notice level
pub fn notice_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy notice
pub fn sampled(
lvl: level.Level,
rate: Float,
message: String,
fields: List(#(String, String)),
) -> Nil
Log with sampling (only log rate% of messages)
pub fn trace(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at trace level
pub fn trace_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy trace - for high-frequency logs that should be cheap when disabled
pub fn warning(
message: String,
fields: List(#(String, String)),
) -> Nil
Log at warning level
pub fn warning_lazy(
message_fn: fn() -> String,
fields: List(#(String, String)),
) -> Nil
Lazy warning
pub fn with_bool(
logger: Logger,
key: String,
value: Bool,
) -> Logger
Add a bool field to a named logger.
pub fn with_context(
context: List(#(String, String)),
f: fn() -> a,
) -> a
Execute function with additional context
pub fn with_field(
logger: Logger,
key: String,
value: String,
) -> Logger
Add a string field to a named logger.
pub fn with_fields(
logger: Logger,
fields: List(#(String, String)),
) -> Logger
Add many fields to a named logger.
pub fn with_float(
logger: Logger,
key: String,
value: Float,
) -> Logger
Add a float field to a named logger.
pub fn with_int(
logger: Logger,
key: String,
value: Int,
) -> Logger
Add an integer field to a named logger.
pub fn with_option(
logger: Logger,
key: String,
value: option.Option(a),
encode: fn(a) -> String,
) -> Logger
Add a field only when the option contains a value.
pub fn with_result(
logger: Logger,
key: String,
result: Result(a, b),
encode_ok: fn(a) -> String,
encode_error: fn(b) -> String,
) -> Logger
Add a result to a named logger.
Successful values are stored under key; errors are stored under the
standard error field.
pub fn would_log(lvl: level.Level) -> Bool
Check if any handler would log at this level Use this to avoid expensive computations when log won’t be emitted