Logging
stdlib::log é um logger estruturado com níveis. Seis níveis (trace < debug < info < warn < error < fatal) fluem por um Logger global para um ou mais sinks (stdout/stderr, arquivos, arquivos rotativos, syslog ou um callback), renderizados como texto simples, logfmt key=value, ou um objeto JSON por linha. Cada nível tem uma macro no estilo println! (info!, warn!, …) além de uma função livre (info, warn, …) roteada pelo logger global do processo.
Import
import stdlib::log::*;
Este único glob traz tanto as funções/tipos de runtime quanto as proc-macros (info!, warn!, @trace, @logged). O módulo de macros (stdlib::log_macros) é re-importado de forma transparente — você nunca o importa diretamente.
Superfície pública em resumo
| Grupo | Itens |
|---|---|
| Constantes de nível | LOG_TRACE LOG_DEBUG LOG_INFO LOG_WARN LOG_ERROR LOG_FATAL LOG_OFF |
| Constantes de formato | LOG_FMT_TEXT LOG_FMT_KV LOG_FMT_JSON |
| Constantes de timestamp | LOG_TS_NONE LOG_TS_ISO LOG_TS_UNIX LOG_TS_UNIX_MS LOG_TS_RELATIVE |
| Constantes de sink | SINK_STDOUT SINK_STDERR SINK_FILE SINK_ROTATING SINK_SYSLOG SINK_CALLBACK |
| Structs | Logger LoggerBuilder LogSink LogLine LogField LogEntry |
| Funções livres (global) | trace debug info warn error fatal at log_set_level log_set_format |
| Helpers de temporização | trace_now_ns trace_since_str |
Estáticos de Logger |
default global set_global |
Métodos de Logger |
with logv logv_at trace debug info warn error fatal |
LoggerBuilder |
new level format timestamps add_sink async_writer build |
Construtores de LogSink |
stdout stderr file rotating_file syslog callback |
Métodos de LogSink |
min_level |
LogLine |
trace debug info warn error fatal kv emit emit_to |
| Macros | trace! debug! info! warn! error! fatal! |
| Atributos | @trace @trace(level) @logged @logged(level) |
Níveis, formatos, timestamps e tipos de sink
Todos os valores de configuração são constantes i32 simples. Números de nível menores são mais verbosos; um logger emite uma entrada apenas quando o número do nível é >= logger.level.
Constantes de nível
| Constante | Valor | Significado |
|---|---|---|
LOG_TRACE |
0 |
Mais verboso; marcadores de fluxo. |
LOG_DEBUG |
1 |
Diagnósticos de debug. |
LOG_INFO |
2 |
Mensagens operacionais normais. |
LOG_WARN |
3 |
Problemas recuperáveis. |
LOG_ERROR |
4 |
Erros. |
LOG_FATAL |
5 |
Condições fatais. |
LOG_OFF |
99 |
Definir como nível do logger para silenciar tudo. |
pub const LOG_TRACE: i32 = 0;
pub const LOG_DEBUG: i32 = 1;
pub const LOG_INFO: i32 = 2;
pub const LOG_WARN: i32 = 3;
pub const LOG_ERROR: i32 = 4;
pub const LOG_FATAL: i32 = 5;
pub const LOG_OFF: i32 = 99;
Constantes de formato de saída
| Constante | Valor | Saída |
|---|---|---|
LOG_FMT_TEXT |
0 |
Legível por humanos, com suporte a cores (INFO server started k=v). |
LOG_FMT_KV |
1 |
logfmt: level=INFO msg="…" k=v. |
LOG_FMT_JSON |
2 |
Um objeto JSON por linha. |
A mesma entrada INFO LogLine::info("server started").kv("port", "8080").emit() é renderizada de forma diferente por formato (timestamps omitidos por clareza):
# LOG_FMT_TEXT
INFO server started port=8080
# LOG_FMT_KV
level=INFO msg="server started" port=8080
# LOG_FMT_JSON
{"level":"INFO","msg":"server started","port":"8080"}
Em KV e JSON, valores que contêm espaços, =, aspas, tabulações ou quebras de linha são colocados entre aspas/escapados (msg="slow query", \n → \\n). O formato texto emite a mensagem e os campos sem escapamento. Todo valor de campo é uma `string` — não existe tipo de campo numérico ou booleano; formate seus números antes de passá-los.
Constantes de timestamp
| Constante | Valor | Renderizado como |
|---|---|---|
LOG_TS_NONE |
0 |
(sem timestamp) |
LOG_TS_ISO |
1 |
2026-05-23T15:30:45Z |
LOG_TS_UNIX |
2 |
1716480000 (segundos) |
LOG_TS_UNIX_MS |
3 |
1716480000123 (milissegundos) |
LOG_TS_RELATIVE |
4 |
0.001s decorrido desde o início do logger (renderizado como <ms>ms) |
Constantes de tipo de sink
Essas constantes marcam um LogSink.kind; normalmente você cria sinks com os construtores LogSink::* em vez de definir kind diretamente.
| Constante | Valor | Sink |
|---|---|---|
SINK_STDOUT |
0 |
Saída padrão. |
SINK_STDERR |
1 |
Saída de erro padrão. |
SINK_FILE |
2 |
Adiciona ao final de um arquivo. |
SINK_ROTATING |
3 |
Arquivo rotativo com tamanho máximo. |
SINK_SYSLOG |
4 |
Log do sistema (no-op no Windows). |
SINK_CALLBACK |
5 |
fn(string) fornecida pelo usuário. |
Início rápido: o logger global
A forma mais rápida de registrar logs. As funções livres e as macros ! roteiam todas pelo Logger::global() — um singleton inicializado preguiçosamente que usa INFO+ no stdout no formato texto por padrão.
Macros ! (estilo println!)
info!(fmt, args…) formata exatamente como format!/println! e registra o resultado no nível correspondente. A macro expande para stdlib::log::info(format!(fmt, args…)), portanto usa o caminho completo do módulo e resolve independentemente de como você importou. Sem string de formato, um {} é sintetizado por argumento.
| Macro | Nível |
|---|---|
trace!(fmt, …) |
TRACE |
debug!(fmt, …) |
DEBUG |
info!(fmt, …) |
INFO |
warn!(fmt, …) |
WARN |
error!(fmt, …) |
ERROR |
fatal!(fmt, …) |
FATAL |
import stdlib::log::*;
fn main() -> i32 {
log_set_level(LOG_DEBUG);
log_set_format(LOG_FMT_TEXT);
info!("server started");
warn!("queue at {}% capacity", 90);
error!("request failed: {}", "timeout");
debug!("cache size {}", 1024);
trace!("entering loop");
return 0;
}
Funções livres
Funções simples que roteiam pelo logger global sem formatação. Com um import qualificado, aparecem como log::info(...).
| Função | Assinatura | Registra em |
|---|---|---|
trace |
pub fn trace(msg: string) |
TRACE |
debug |
pub fn debug(msg: string) |
DEBUG |
info |
pub fn info(msg: string) |
INFO |
warn |
pub fn warn(msg: string) |
WARN |
error |
pub fn error(msg: string) |
ERROR |
fatal |
pub fn fatal(msg: string) |
FATAL |
at |
pub fn at(lvl: i32, msg: string) |
lvl |
import stdlib::log::*;
fn main() -> i32 {
info("bare fn info");
error("bare fn error");
at(LOG_INFO, "ready"); // registra em um nível explícito
return 0;
}
Configuração global
| Função | Assinatura | Efeito |
|---|---|---|
log_set_level |
pub fn log_set_level(lvl: i32) |
Define o nível mínimo do logger global. |
log_set_format |
pub fn log_set_format(fmt: i32) |
Define o formato de saída do logger global. |
Essas são conveniências sobre Logger::global().level = lvl / .format = fmt.
Logger
O logger armazena um nível, formato, estilo de timestamp, um tempo de início (para timestamps relativos), seus sinks e quaisquer campos vinculados herdados por loggers filhos.
pub struct Logger {
pub level: i32,
pub format: i32,
pub ts_format: i32,
pub time_start_ns: i64,
pub sinks: *Vector<*LogSink>,
pub bound_fields: *Vector<LogField>,
}
Construtores e o singleton global
| Função | Assinatura | Descrição |
|---|---|---|
default |
pub fn default() -> *Logger |
INFO+ para stdout, com cor quando stdout é um TTY, timestamps ISO. |
global |
pub fn global() -> *Logger |
O singleton de todo o processo (criado preguiçosamente no primeiro uso). |
set_global |
pub fn set_global(l: *Logger) |
Substitui o logger global. |
Logger::global() é criado no primeiro uso chamando Logger::default(). As funções livres (info, warn, …), as macros !, at, log_set_level e log_set_format roteiam todas por ele. Para tornar um logger personalizado o padrão para esses pontos de entrada, construa-o e passe-o para Logger::set_global.
import stdlib::log::*;
fn main() -> i32 {
let l: *Logger = Logger::default(); // INFO+, stdout, ts ISO, cor no TTY
l.info("from default logger");
Logger::set_global(l); // instala como logger do processo
log_set_level(LOG_DEBUG); // ajusta o logger agora global
log_set_format(LOG_FMT_KV);
Logger::global().debug("now visible at DEBUG");
info("global free fn"); // roteia pelo mesmo logger
return 0;
}
Métodos de chamada direta
Cada método emite uma mensagem no seu nível fixo. Eles não capturam file:line.
pub fn trace(self: *Logger, msg: string)
pub fn debug(self: *Logger, msg: string)
pub fn info (self: *Logger, msg: string)
pub fn warn (self: *Logger, msg: string)
pub fn error(self: *Logger, msg: string)
pub fn fatal(self: *Logger, msg: string)
Loggers filhos — with
with retorna um logger filho que herda sinks/nível/formato/timestamps e adiciona um campo vinculado. Toda entrada do filho carrega esse campo. Encadeie with para vincular vários.
pub fn with(self: *Logger, key: string, value: string) -> *Logger
Emissão de baixo nível — logv / logv_at
extras é uma lista plana de chave, valor, chave, valor, …. logv_at é igual, mas com localização no código-fonte fornecida pelo chamador, que a saída renderizada inclui como (file:line).
pub fn logv(self: *Logger, lvl: i32, msg: string, extras: *Vector<string>)
pub fn logv_at(self: *Logger, lvl: i32, msg: string, file: string, line: i32,
extras: *Vector<string>)
import stdlib::log::*;
fn main() -> i32 {
let l: *Logger = LoggerBuilder::new()
.level(LOG_DEBUG)
.add_sink(LogSink::stdout(true))
.add_sink(LogSink::file("/var/log/app.log"))
.format(LOG_FMT_JSON)
.timestamps(LOG_TS_ISO)
.build();
l.info("started");
// Logger filho: req_id + user anexados a cada linha.
let req_log: *Logger = l.with("req_id", "abc123").with("user", "alice");
req_log.info("handled");
// Baixo nível: extras chave/valor planos + localização explícita no código.
let extras: *Vector<string> = Vector::new();
extras.push("port");
extras.push("8080");
l.logv(LOG_INFO, "with extras", extras);
l.logv_at(LOG_INFO, "with loc", "main.glide", 42, extras);
Logger::set_global(l); // torna-o o global
Logger::global().info("via global");
return 0;
}
No formato KV, as mesmas chamadas logv / logv_at são renderizadas com os extras como pares chave=valor à direita e a localização como file=… line=…:
import stdlib::log::*;
fn main() -> i32 {
let l: *Logger = LoggerBuilder::new().format(LOG_FMT_KV).build();
let extras: *Vector<string> = Vector::new();
extras.push("port"); extras.push("8080");
extras.push("tls"); extras.push("on");
l.logv(LOG_INFO, "listening", extras);
// level=INFO msg=listening port=8080 tls=on
l.logv_at(LOG_ERROR, "boom", "server.glide", 128, extras);
// level=ERROR msg=boom file=server.glide line=128 port=8080 tls=on
return 0;
}
LoggerBuilder
Builder fluente para um Logger personalizado. Cada setter retorna self para encadeamento; build() produz o *Logger. Se nenhum sink for adicionado, build() usa por padrão um sink stdout colorido.
| Método | Assinatura | Descrição |
|---|---|---|
new |
pub fn new() -> *LoggerBuilder |
Inicia com os padrões (INFO, texto, timestamps ISO, sem sinks). |
level |
pub fn level(self: *LoggerBuilder, lvl: i32) -> *LoggerBuilder |
Nível mínimo. |
format |
pub fn format(self: *LoggerBuilder, fmt: i32) -> *LoggerBuilder |
Formato de saída. |
timestamps |
pub fn timestamps(self: *LoggerBuilder, ts: i32) -> *LoggerBuilder |
Estilo de timestamp. |
add_sink |
pub fn add_sink(self: *LoggerBuilder, s: *LogSink) -> *LoggerBuilder |
Adiciona um sink (chame repetidamente). |
async_writer |
pub fn async_writer(self: *LoggerBuilder, on: bool) -> *LoggerBuilder |
Solicita escritas não-bloqueantes. |
build |
pub fn build(self: *LoggerBuilder) -> *Logger |
Finaliza. |
Escolhendo formato e timestamps
Cada builder produz um logger independente — útil para rotear diferentes subsistemas para diferentes formatos. O estilo de timestamp é aplicado uniformemente ao formato selecionado.
import stdlib::log::*;
fn main() -> i32 {
let kv: *Logger = LoggerBuilder::new()
.format(LOG_FMT_KV)
.timestamps(LOG_TS_UNIX)
.add_sink(LogSink::stdout(false))
.build();
LogLine::warn("slow query").kv("ms", "320").kv("table", "users").emit_to(kv);
let js: *Logger = LoggerBuilder::new()
.format(LOG_FMT_JSON)
.timestamps(LOG_TS_ISO)
.build();
js.with("svc", "api").info("ready");
let rel: *Logger = LoggerBuilder::new()
.timestamps(LOG_TS_RELATIVE) // <ms>ms desde o início do logger
.format(LOG_FMT_TEXT)
.build();
rel.info("relative timestamp");
let none: *Logger = LoggerBuilder::new().timestamps(LOG_TS_NONE).build();
none.info("no timestamp");
return 0;
}
LogSink
Um sink é um destino de saída com seu próprio nível mínimo. Construa sinks com os construtores estáticos, opcionalmente restrinja-os com min_level e anexe-os via LoggerBuilder::add_sink.
pub struct LogSink {
pub kind: i32,
pub level: i32, // filtro por sink (LOG_TRACE por padrão)
pub color: bool, // ANSI para formato texto
pub fd: i32, // fd bruto para stdout/stderr/arquivo
pub path: string,
pub max_size: i32, // rotativo: bytes por arquivo
pub max_files: i32, // rotativo: mantém N arquivos rotacionados
pub cur_size: i32, // rotativo: tamanho atual no arquivo ativo
pub cb: fn(string), // SINK_CALLBACK
}
Construtores
| Construtor | Assinatura | Descrição |
|---|---|---|
stdout |
pub fn stdout(color: bool) -> *LogSink |
stdout; cor aplicada somente quando o fd 1 é um TTY. |
stderr |
pub fn stderr(color: bool) -> *LogSink |
stderr; cor aplicada somente quando o fd 2 é um TTY. |
file |
pub fn file(path: string) -> *LogSink |
Adiciona ao final de path (cria o arquivo). |
rotating_file |
pub fn rotating_file(path: string, max_size: i32, max_files: i32) -> *LogSink |
Rotaciona para .1….N quando max_size bytes é atingido, mantendo max_files arquivos rotacionados. |
syslog |
pub fn syslog(ident: string) -> *LogSink |
syslog POSIX com o ident fornecido (no-op no Windows). |
callback |
pub fn callback(f: fn(string)) -> *LogSink |
Invoca f com cada linha completamente renderizada. |
Métodos
| Método | Assinatura | Descrição |
|---|---|---|
min_level |
pub fn min_level(self: *LogSink, lvl: i32) -> *LogSink |
Rejeita entradas abaixo de lvl neste sink. Encadeável. |
import stdlib::log::*;
fn audit(line: string) {
// encaminha cada linha renderizada para algum lugar (ex.: uma trilha de auditoria)
return;
}
fn main() -> i32 {
let l: *Logger = LoggerBuilder::new()
.add_sink(LogSink::stdout(false).min_level(LOG_WARN)) // warnings+ no stdout
.add_sink(LogSink::file("/var/log/app.log")) // tudo para o arquivo
.add_sink(LogSink::rotating_file("/var/log/app.log", 10485760, 5))
.add_sink(LogSink::syslog("myapp"))
.add_sink(LogSink::callback(audit))
.build();
l.info("multi-sink");
return 0;
}
Uma entrada precisa passar por dois portões: o level do logger (decide se a entrada é construída) e depois o level próprio de cada sink (decide se aquele sink a escreve). Um sink usa LOG_TRACE por padrão, portanto aceita tudo que o logger emite.
import stdlib::log::*;
fn main() -> i32 {
let l: *Logger = LoggerBuilder::new()
.level(LOG_DEBUG)
.add_sink(LogSink::stdout(false).min_level(LOG_WARN))
.add_sink(LogSink::file("/tmp/app.log")) // herda o piso LOG_TRACE
.build();
l.debug("only in file"); // abaixo do piso do console -> somente no arquivo
l.warn("file + console"); // passa pelos pisos de ambos os sinks
return 0;
}
Sinks de callback e LogField
Um sink SINK_CALLBACK entrega à sua fn(string) cada linha completamente renderizada (já formatada e com newline ao final). Use-o para integrar a uma trilha de auditoria, um ring buffer, um contador de métricas ou um harness de testes. A struct LogField é o portador público {key, value} que os renderizadores iteram; tanto key quanto value são string.
import stdlib::log::*;
fn render(line: string) {
// Encaminha a linha renderizada para algum lugar: uma trilha de auditoria,
// um ring buffer, um contador de métricas, etc. Aqui apenas inspecionamos seu tamanho.
let _n: i32 = line.len();
return;
}
fn main() -> i32 {
let f: LogField = LogField { key: "user", value: "alice" };
println!("field {}={}", f.key, f.value);
let l: *Logger = LoggerBuilder::new()
.format(LOG_FMT_KV)
.timestamps(LOG_TS_NONE)
.add_sink(LogSink::callback(render))
.build();
LogLine::info("login").kv("user", f.value).emit_to(l);
return 0;
}
LogLine — campos estruturados fluentes
Quando você quer campos chave=valor em uma única linha sem vinculá-los a um logger filho, construa um LogLine, encadeie kv e então emit (global) ou emit_to (um logger específico).
| Método | Assinatura | Descrição |
|---|---|---|
trace |
pub fn trace(msg: string) -> *LogLine |
Inicia uma linha TRACE. |
debug |
pub fn debug(msg: string) -> *LogLine |
Inicia uma linha DEBUG. |
info |
pub fn info(msg: string) -> *LogLine |
Inicia uma linha INFO. |
warn |
pub fn warn(msg: string) -> *LogLine |
Inicia uma linha WARN. |
error |
pub fn error(msg: string) -> *LogLine |
Inicia uma linha ERROR. |
fatal |
pub fn fatal(msg: string) -> *LogLine |
Inicia uma linha FATAL. |
kv |
pub fn kv(self: *LogLine, k: string, v: string) -> *LogLine |
Anexa um campo. Encadeável. |
emit |
pub fn emit(self: *LogLine) |
Envia pelo logger global. |
emit_to |
pub fn emit_to(self: *LogLine, lg: *Logger) |
Envia pelo logger lg. |
import stdlib::log::*;
fn main() -> i32 {
LogLine::info("server started")
.kv("port", "8080")
.kv("workers", "4")
.emit();
let l: *Logger = LoggerBuilder::new().build();
LogLine::warn("slow query").kv("ms", "320").emit_to(l);
return 0;
}
LogField e LogEntry
Os dados carregados por um único registro de log. Você raramente os constrói diretamente — logv/LogLine/with os criam para você — mas os campos são públicos para renderizadores e callbacks.
pub struct LogField {
pub key: string,
pub value: string,
}
pub struct LogEntry {
pub level: i32,
pub msg: string,
pub time_ns: i64,
pub file: string,
pub line: i32,
pub fields: *Vector<LogField>,
}
Atributos de rastreamento de fluxo — @trace e @logged
Esses proc-atributos envolvem uma função para que ela registre logs na entrada e na saída. O primeiro argumento opcional escolhe o nível (trace, debug, info, warn, error, fatal); o padrão é debug.
| Atributo | Registra |
|---|---|
@trace |
> name na entrada, < name na saída (marcadores de fluxo simples). |
@trace(info) |
O mesmo, mas no nível indicado. |
@logged |
Entrada com os valores dos argumentos (> name(4, 6)), saída com o valor de retorno e o tempo decorrido (< name -> 10 (340ns)). |
@logged(info) |
O mesmo, no nível indicado. |
Uma função sem retorno omite a parte -> {} na saída (< name (340ns)). O identificador de nível é a palavra simples info, não LOG_INFO. Ambos os atributos roteiam suas linhas de entrada/saída pelo logger global via a função livre correspondente, portanto as linhas obedecem ao level global.
import stdlib::log::*;
@trace
fn step() { return; } // registra `> step` / `< step`
@trace(info)
fn ping() -> i32 { return 1; } // entrada/saída em INFO
@logged
fn add(a: i32, b: i32) -> i32 { return a + b; } // registra args, retorno, duração
fn main() -> i32 {
log_set_level(LOG_TRACE);
step();
let p: i32 = ping();
let s: i32 = add(4, 6);
let t0: i64 = trace_now_ns();
let dur: string = trace_since_str(t0);
info!("p={} s={} dur={}", p, s, dur);
return 0;
}
Helpers de temporização
@logged é respaldado por dois helpers públicos que você também pode chamar diretamente para medir um trecho de código.
| Função | Assinatura | Descrição |
|---|---|---|
trace_now_ns |
pub fn trace_now_ns() -> i64 |
Marca monotônica em nanossegundos. |
trace_since_str |
pub fn trace_since_str(start_ns: i64) -> string |
Tempo decorrido desde uma marca, com escala automática (340ns / 12.3us / 4.5ms / 1.2s). |
import stdlib::log::*;
fn main() -> i32 {
let t0: i64 = trace_now_ns();
// ... trabalho ...
let elapsed: string = trace_since_str(t0);
info!("done in {}", elapsed);
return 0;
}
Ressalvas
- Sinks que tocam recursos reais (
LogSink::file,rotating_file,syslog) abrem arquivos / conectam ao syslog no momento da construção; os exemplos acima os constroem e passam emcheck, mas não são executados contra destinos reais.syslogé um no-op no Windows. async_writer(true)é atualmente um no-op compatível com versões futuras (as escritas permanecem síncronas).- Todos os valores de campo são
string—LogLine::kv,withe os extras delogvaceitam apenasstring. Formate números/bools (format!("{}", n)) antes de passá-los. - Para silenciar um logger completamente, defina seu nível como
LOG_OFF(99); nenhum número de nível chega até ele, portanto nada é emitido independentemente dos filtros por sink. - A cor é decidida uma única vez, a partir do flag
colordo primeiro sink, e se aplica somente aLOG_FMT_TEXT.LogSink::stdout(true)/stderr(true)condicionam adicionalmente a cor ao fd ser realmente um TTY, então a saída redirecionada permanece limpa. rotating_filerotaciona quando o arquivo ativo atingemax_sizebytes, deslocandobase → .1 → … → .Ne mantendomax_filesarquivos rotacionados; ele rastreia o tamanho a partir do comprimento existente do arquivo na construção.
Veja também
stdlib::time—now_ns,time_now_ns,time_since_nse o tipoTimesustentam os timestamps e as durações de@logged.- Os capítulos HTTP do livro usam este logger para registro de requisições; o HTTP em si está documentado lá, não aqui.