Introdução: por que a registro é crítica para aplicações Rust
Registro não é apenas gravar eventos em um arquivo. Em um ambiente de produção, logs de qualidade permitem diagnosticar rapidamente erros, monitorar desempenho e analisar o comportamento do sistema. Rust, sendo uma linguagem de programação de sistemas, oferece ferramentas flexíveis e performáticas para essa tarefa. Neste artigo, abordaremos as melhores práticas de registro em Rust: desde a escolha da crate até a configuração de logs estruturados e rotação.
1. Fundamentos: as crates log e env_logger
O ecossistema Rust oferece uma abordagem modular. A crate log fornece uma abstração (fachada), e uma crate separada, como env_logger ou pretty_env_logger, conecta a implementação concreta (backend).
Instalação e configuração
Adicione as dependências no Cargo.toml:
[dependencies]log = "0.4"env_logger = "0.10"Exemplo básico
use log::{info, warn, error};
fn main() { env_logger::init();
info!("Aplicação iniciada"); let user_id = 42; warn!("Usuário {} não encontrado no cache", user_id); error!("Erro de conexão com o banco de dados: {:?}", "timeout");}Importante: Por padrão, os logs não são exibidos até que você defina a variável de ambiente RUST_LOG. Execute o programa assim:
RUST_LOG=info cargo runNíveis de registro
A crate log suporta cinco níveis padrão (do mais prioritário ao menos):
error— erros críticos que exigem intervenção imediata.warn— avisos sobre problemas potenciais.info— informações gerais sobre o progresso da execução.debug— informações de depuração para desenvolvedores.trace— eventos extremamente detalhados (frequentemente usados em loops).
Filtragem por módulos: RUST_LOG=my_crate=debug,other_crate=info.
2. Registro avançado: a crate tracing
Para aplicações complexas (servidores web, microsserviços), recomenda-se usar a crate tracing. Ela fornece o conceito de span (intervalos de tempo) e registro estruturado com contexto.
Instalação do tracing
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"Exemplo com span
use tracing::{info, instrument, span, Level};use tracing_subscriber;
fn main() { tracing_subscriber::fmt::init();
let parent_span = span!(Level::INFO, "processamento_requisicao", request_id = 123); let _guard = parent_span.enter(); process_data();}
#[instrument]fn process_data() { info!("Processamento de dados iniciado"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("Processamento de dados concluído");}O atributo #[instrument] cria automaticamente um span para a função e registra o tempo de execução. A saída conterá carimbos de tempo aninhados, que são indispensáveis para a criação de perfis.
3. Logs estruturados (JSON) e rotação
Em um ambiente de produção, os logs são frequentemente coletados centralizadamente (ELK, Loki, Grafana). Para isso, é conveniente gerar logs no formato JSON.
Saída em JSON com tracing-subscriber
use tracing_subscriber::fmt::format::Json;use tracing_subscriber::prelude::*;
fn main() { let format = Json::new() .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc3339()) .with_target(true) .with_level(true);
tracing_subscriber::fmt() .event_format(format) .init();
tracing::info!("Servidor iniciado na porta 8080");}Agora os logs terão a seguinte aparência: