std.log em Zig — Referência e Exemplos

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 ModeNível PadrãoMensagens Incluídas
Debug.debugerr, warn, info, debug
ReleaseSafe.infoerr, warn, info
ReleaseFast.infoerr, warn, info
ReleaseSmall.infoerr, warn, info

Módulos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.