Bibliotecas de Logging em Zig — Registro Estruturado e Diagnóstico

Bibliotecas de Logging em Zig — Registro Estruturado e Diagnóstico

Logging adequado é essencial para qualquer aplicação em produção. O Zig aborda logging de forma única: a biblioteca padrão inclui um sistema de logging integrado que é avaliado em tempo de compilação, permitindo que logs de debug sejam completamente removidos em builds de release sem custo algum. Além disso, o ecossistema oferece bibliotecas de terceiros para logging estruturado avançado.

std.log — O Sistema Integrado

O std.log é o sistema de logging da biblioteca padrão do Zig. Suas principais vantagens são a avaliação em comptime e a eliminação de código morto pelo compilador:

const std = @import("std");
const log = std.log;

pub fn main() void {
    log.info("Aplicação iniciada na porta {}", .{8080});
    log.debug("Detalhes internos: buffer_size={}", .{4096});
    log.warn("Conexão lenta detectada: latência={}ms", .{500});
    log.err("Falha ao conectar ao banco de dados: {s}", .{"connection refused"});
}

Níveis de Log

O std.log define quatro níveis, do mais verboso ao mais crítico:

// Do mais verboso ao mais severo
log.debug("..."); // Informações de desenvolvimento
log.info("...");  // Eventos normais de operação
log.warn("...");  // Situações anômalas mas não fatais
log.err("...");   // Erros que afetam funcionalidade

Escopos de Log

Defina escopos para categorizar mensagens por módulo:

const std = @import("std");

// Escopo customizado
const db_log = std.log.scoped(.database);
const http_log = std.log.scoped(.http);
const auth_log = std.log.scoped(.auth);

pub fn conectarBanco() !void {
    db_log.info("Conectando ao PostgreSQL em {s}:{}", .{ "localhost", 5432 });
    // ...
    db_log.info("Conexão estabelecida com sucesso", .{});
}

pub fn processarRequisicao() !void {
    http_log.debug("Recebida requisição GET /api/usuarios", .{});
    // ...
    http_log.info("Requisição processada em {}ms", .{42});
}

pub fn autenticar(usuario: []const u8) !void {
    auth_log.info("Tentativa de login: {s}", .{usuario});
    // ...
    auth_log.warn("Login falhou para {s}: senha incorreta", .{usuario});
}

Configuração de Nível de Log

Controle quais níveis são ativos em tempo de compilação:

// Na raiz do módulo (root.zig ou main.zig)
pub const std_options = struct {
    // Definir nível mínimo de log
    pub const log_level: std.log.Level = .debug; // Em dev
    // pub const log_level: std.log.Level = .info; // Em produção

    // Filtrar por escopo
    pub const log_scope_levels = &[_]std.log.ScopeLevel{
        .{ .scope = .database, .level = .warn },
        .{ .scope = .http, .level = .info },
        .{ .scope = .auth, .level = .debug },
    };
};

Função de Log Customizada

Substitua a implementação padrão de log:

pub const std_options = struct {
    pub const logFn = meuLogCustomizado;
};

fn meuLogCustomizado(
    comptime nivel: std.log.Level,
    comptime escopo: @TypeOf(.enum_literal),
    comptime formato: []const u8,
    args: anytype,
) void {
    const prefixo_nivel = comptime switch (nivel) {
        .debug => "DEBUG",
        .info => "INFO ",
        .warn => "WARN ",
        .err => "ERROR",
    };

    const prefixo_escopo = if (escopo != .default)
        "[" ++ @tagName(escopo) ++ "] "
    else
        "";

    const timestamp = std.time.timestamp();

    std.debug.print(
        "{} {s} {s}" ++ formato ++ "\n",
        .{timestamp} ++ .{prefixo_nivel} ++ .{prefixo_escopo} ++ args,
    );
}

Logging Estruturado

Para aplicações que precisam de logs estruturados (JSON, logfmt), bibliotecas de terceiros complementam a std:

zig-log (Logging JSON)

const zlog = @import("zig-log");

pub fn main() !void {
    var logger = try zlog.Logger.init(.{
        .format = .json,
        .output = .stderr,
        .level = .info,
    });
    defer logger.deinit();

    logger.info("Requisição processada", .{
        .method = "GET",
        .path = "/api/usuarios",
        .status = 200,
        .duracao_ms = 42,
        .ip = "192.168.1.100",
    });
    // Saída: {"level":"info","msg":"Requisição processada","method":"GET","path":"/api/usuarios","status":200,"duracao_ms":42,"ip":"192.168.1.100","timestamp":"2026-02-21T10:30:00Z"}
}

Logging para Arquivo com Rotação

const FileLogger = struct {
    arquivo: std.fs.File,
    max_tamanho: usize,
    tamanho_atual: usize,
    caminho_base: []const u8,

    pub fn init(caminho: []const u8, max_tamanho: usize) !FileLogger {
        const arquivo = try std.fs.cwd().createFile(caminho, .{ .truncate = false });
        const stat = try arquivo.stat();
        return FileLogger{
            .arquivo = arquivo,
            .max_tamanho = max_tamanho,
            .tamanho_atual = stat.size,
            .caminho_base = caminho,
        };
    }

    pub fn log(self: *FileLogger, comptime fmt: []const u8, args: anytype) !void {
        const msg = try std.fmt.allocPrint(
            std.heap.page_allocator,
            fmt ++ "\n",
            args,
        );
        defer std.heap.page_allocator.free(msg);

        if (self.tamanho_atual + msg.len > self.max_tamanho) {
            try self.rotacionar();
        }

        try self.arquivo.writeAll(msg);
        self.tamanho_atual += msg.len;
    }

    fn rotacionar(self: *FileLogger) !void {
        self.arquivo.close();
        // Renomear arquivo atual
        var buf: [256]u8 = undefined;
        const novo_nome = try std.fmt.bufPrint(&buf, "{s}.{}", .{
            self.caminho_base,
            std.time.timestamp(),
        });
        try std.fs.cwd().rename(self.caminho_base, novo_nome);
        self.arquivo = try std.fs.cwd().createFile(self.caminho_base, .{});
        self.tamanho_atual = 0;
    }
};

Integração com Sistemas Externos

Syslog

const posix = std.posix;

fn logParaSyslog(mensagem: []const u8) void {
    const sock = posix.socket(posix.AF.UNIX, posix.SOCK.DGRAM, 0) catch return;
    defer posix.close(sock);

    const addr = posix.sockaddr.un{ .path = "/dev/log" };
    _ = posix.sendto(sock, mensagem, 0, &addr, @sizeOf(@TypeOf(addr))) catch {};
}

Boas Práticas

  1. Use escopos: Categorize logs por módulo para facilitar filtragem
  2. Nível apropriado: Use debug para desenvolvimento, info para operação, warn para anomalias, err para falhas
  3. Logs estruturados em produção: JSON facilita parsing por ferramentas como ELK e Datadog
  4. Não logue dados sensíveis: Nunca registre senhas, tokens ou dados pessoais
  5. Contexto suficiente: Inclua IDs de request, timestamps e metadata relevante
  6. Configure por ambiente: Debug em dev, info em staging, warn/err em produção

Próximos Passos

Combine logging com as ferramentas de debug para diagnóstico avançado, as ferramentas de profiling para correlacionar logs com performance, e os frameworks de teste para verificar comportamento de logging. Consulte nossos tutoriais para exemplos práticos.

Continue aprendendo Zig

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