std.log — Logging Estruturado
O módulo std.log fornece um sistema de logging estruturado e configurável para o Zig. Diferente de std.debug.print, que é voltado para depuração temporária, std.log é projetado para mensagens de log em produção com níveis de severidade, escopos nomeados e filtragem controlada em tempo de compilação.
Visão Geral
const std = @import("std");
const log = std.log;
O sistema de log do Zig é baseado em dois conceitos: níveis de log e escopos. Os níveis controlam a severidade das mensagens, enquanto os escopos permitem categorizar mensagens por componente.
Níveis de Log
pub const Level = enum {
err, // Erro — condição de falha
warn, // Aviso — possível problema
info, // Informação — progresso normal
debug, // Debug — informação detalhada para depuração
};
Funções Principais
// Log padrão (escopo default)
pub fn err(comptime fmt: []const u8, args: anytype) void
pub fn warn(comptime fmt: []const u8, args: anytype) void
pub fn info(comptime fmt: []const u8, args: anytype) void
pub fn debug(comptime fmt: []const u8, args: anytype) void
// Cria logger com escopo nomeado
pub fn scoped(comptime scope: @Type(.EnumLiteral)) type
Exemplo 1: Uso Básico de Logging
const std = @import("std");
pub fn main() !void {
// Mensagens com diferentes níveis de severidade
std.log.info("Aplicação iniciada", .{});
std.log.debug("Modo de execução: {s}", .{@tagName(@import("builtin").mode)});
// Simulando operações
std.log.info("Conectando ao banco de dados...", .{});
const db_host = "localhost";
const db_port: u16 = 5432;
std.log.debug("Host: {s}, Porta: {d}", .{ db_host, db_port });
// Simulando um aviso
const tentativas_max: u32 = 3;
var tentativa: u32 = 0;
while (tentativa < tentativas_max) : (tentativa += 1) {
if (tentativa > 0) {
std.log.warn("Tentativa {d}/{d} de conexão", .{ tentativa + 1, tentativas_max });
}
}
// Simulando erro recuperável
std.log.err("Falha na conexão após {d} tentativas", .{tentativas_max});
// Continuando com fallback
std.log.info("Usando cache local como fallback", .{});
std.log.info("Aplicação encerrada com sucesso", .{});
}
Exemplo 2: Logging com Escopos
const std = @import("std");
// Define loggers com escopos específicos
const log_http = std.log.scoped(.http);
const log_db = std.log.scoped(.database);
const log_auth = std.log.scoped(.auth);
const log_cache = std.log.scoped(.cache);
const Usuario = struct {
id: u64,
nome: []const u8,
ativo: bool,
};
fn autenticar(nome: []const u8, senha: []const u8) ?Usuario {
log_auth.info("Tentativa de login: {s}", .{nome});
// Simulação de autenticação
if (std.mem.eql(u8, nome, "admin") and std.mem.eql(u8, senha, "1234")) {
log_auth.info("Login bem-sucedido para: {s}", .{nome});
return .{ .id = 1, .nome = nome, .ativo = true };
}
log_auth.warn("Login falhou para: {s}", .{nome});
return null;
}
fn buscarDados(usuario_id: u64) ![]const u8 {
log_db.debug("Consultando dados para usuário {d}", .{usuario_id});
// Tenta cache primeiro
log_cache.debug("Verificando cache para chave 'user_{d}'", .{usuario_id});
log_cache.info("Cache miss — buscando no banco", .{});
// Simulação de query
log_db.info("SELECT * FROM dados WHERE usuario_id = {d}", .{usuario_id});
log_db.debug("Query executada em 15ms", .{});
return "dados_simulados";
}
fn processarRequisicao(metodo: []const u8, rota: []const u8) !void {
log_http.info("{s} {s}", .{ metodo, rota });
if (autenticar("admin", "1234")) |usuario| {
log_http.debug("Usuário autenticado: {s} (id={d})", .{ usuario.nome, usuario.id });
const dados = try buscarDados(usuario.id);
log_http.info("Resposta: 200 OK ({d} bytes)", .{dados.len});
} else {
log_http.warn("Resposta: 401 Unauthorized", .{});
}
}
pub fn main() !void {
log_http.info("Servidor iniciando na porta 8080", .{});
try processarRequisicao("GET", "/api/dados");
log_http.info("Servidor encerrado", .{});
}
Exemplo 3: Customizando o Logger
const std = @import("std");
// Override da função de log padrão no escopo raiz
pub const std_options: std.Options = .{
// Nível mínimo: apenas warn e err em release, tudo em debug
.log_level = if (@import("builtin").mode == .Debug)
.debug
else
.warn,
// Função de log customizada
.logFn = meuLog,
};
fn meuLog(
comptime nivel: std.log.Level,
comptime escopo: @TypeOf(.enum_literal),
comptime fmt: []const u8,
args: anytype,
) void {
const prefixo_nivel = switch (nivel) {
.err => "ERRO ",
.warn => "AVISO",
.info => "INFO ",
.debug => "DEBUG",
};
const nome_escopo = @tagName(escopo);
const escopo_str = if (!std.mem.eql(u8, nome_escopo, "default"))
"[" ++ nome_escopo ++ "] "
else
"";
const timestamp = std.time.timestamp();
const stderr = std.io.getStdErr().writer();
stderr.print("{d} | {s} | {s}" ++ fmt ++ "\n", .{timestamp} ++ .{prefixo_nivel} ++ .{escopo_str} ++ args) catch return;
}
const log_app = std.log.scoped(.app);
const log_db = std.log.scoped(.db);
pub fn main() !void {
log_app.info("Aplicação iniciada", .{});
log_app.debug("Configuração carregada: modo={s}", .{@tagName(@import("builtin").mode)});
log_db.info("Conectando ao banco...", .{});
log_db.warn("Pool de conexões quase cheio: {d}/{d}", .{ 9, 10 });
log_db.err("Timeout na query após {d}ms", .{@as(u32, 5000)});
std.log.info("Mensagem sem escopo específico", .{});
}
Exemplo 4: Logger Estruturado para Produção
const std = @import("std");
/// Logger que registra eventos com contexto adicional.
const EventLogger = struct {
const Self = @This();
const log = std.log.scoped(.evento);
contagem: u64 = 0,
pub fn registrar(self: *Self, tipo: []const u8, descricao: []const u8) void {
self.contagem += 1;
log.info("#{d} [{s}] {s}", .{ self.contagem, tipo, descricao });
}
pub fn erro(self: *Self, tipo: []const u8, descricao: []const u8) void {
self.contagem += 1;
log.err("#{d} [{s}] {s}", .{ self.contagem, tipo, descricao });
}
pub fn resumo(self: *const Self) void {
log.info("Total de eventos registrados: {d}", .{self.contagem});
}
};
pub fn main() !void {
var logger = EventLogger{};
logger.registrar("INICIO", "Sistema de monitoramento ativo");
logger.registrar("CONFIG", "Intervalo de coleta: 30s");
logger.registrar("METRICA", "CPU: 45%, Memória: 2.1GB");
logger.registrar("METRICA", "Disco: 67% utilizado");
logger.erro("ALERTA", "Uso de disco acima de 60%");
logger.registrar("ACAO", "Notificação enviada ao administrador");
logger.resumo();
}
Filtragem em Tempo de Compilação
Uma das vantagens do std.log no Zig é que mensagens abaixo do nível configurado são completamente removidas em tempo de compilação. Não há custo de runtime para logs desativados.
| Build Mode | Nível Padrão | Mensagens Incluídas |
|---|---|---|
| Debug | .debug | err, warn, info, debug |
| ReleaseSafe | .info | err, warn, info |
| ReleaseFast | .info | err, warn, info |
| ReleaseSmall | .info | err, warn, info |
Módulos Relacionados
- std.debug — Print de depuração e assertions
- std.io — Entrada e saída formatada
- std.fmt — Formatação de strings
- std.time — Timestamps para logging
- std.testing — Framework de testes