Registro em aplicações C++: melhores práticas
Registro (logging) é uma parte fundamental do desenvolvimento de qualquer software sério em C++. Sem um sistema de registro confiável, depurar erros complexos, monitorar desempenho e auditar segurança tornam-se praticamente impossíveis. Neste artigo, vamos abordar as melhores práticas de registro em C++, desde a escolha da biblioteca até técnicas avançadas de formatação e rotação de arquivos.
1. Escolha da biblioteca de registro
Em C++, existem várias bibliotecas maduras para registro. Abandonar uma implementação própria em favor de uma solução pronta economiza tempo e reduz a quantidade de bugs.
- spdlog — a biblioteca mais popular. Destaca-se pelo alto desempenho (header-only, modo assíncrono), API simples e suporte a múltiplos formatos (JSON, plain text).
- Boost.Log — uma biblioteca poderosa, porém mais pesada, do conjunto Boost. Ideal para projetos enterprise que exigem filtragem complexa e configuração flexível de backends.
- Google Logging (glog) — uma biblioteca simples do Google. Adequada para projetos pequenos, mas menos flexível.
Exemplo de inicialização do spdlog:
#include <spdlog/spdlog.h>#include <spdlog/sinks/basic_file_sink.h>
int main() { // Criamos um logger que escreve no arquivo "logs/app.log" auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log"); // Definimos o nível de registro logger->set_level(spdlog::level::debug); // Registramos uma mensagem logger->info("Aplicação iniciada"); return 0;}2. Níveis de registro e filtragem
O uso de níveis de registro (trace, debug, info, warning, error, critical) é o padrão de facto. Eles permitem configurar flexivelmente o volume de informações exibidas de acordo com o ambiente (desenvolvimento, teste, produção).
Recomendações por nível:
- Trace/Debug — apenas para desenvolvimento. Informações detalhadas sobre fluxos de execução, valores de variáveis.
- Info — eventos padrão: inicialização/parada do serviço, operações bem-sucedidas.
- Warning — situações anormais que não causam erro (ex.: uso de API obsoleta).
- Error — erros que exigem atenção, mas não são fatais.
- Critical — erros fatais, após os quais a aplicação não pode continuar.
Exemplo de configuração de filtragem no spdlog:
// Em produção, registramos apenas warning e superiorauto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::warn);
// No arquivo, registramos tudo, incluindo debugauto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/debug.log");file_sink->set_level(spdlog::level::debug);
auto logger = std::make_shared<spdlog::logger>("multi_sink", spdlog::sinks_init_list({console_sink, file_sink}));
spdlog::register_logger(logger);3. Desempenho e registro assíncrono
O registro síncrono pode se tornar um gargalo de desempenho, especialmente em sistemas de alta carga. Cada chamada logger->info(...) bloqueia a thread até a conclusão da gravação no disco.
Melhores práticas para desempenho:
- Registro assíncrono — use uma thread separada para gravar os logs. O spdlog suporta isso nativamente.
- Evite operações caras nas linhas de log — não chame funções pesadas dentro da macro de registro. Por exemplo,
logger->info("Resultado: {}", computeExpensiveValue());chamarácomputeExpensiveValue()mesmo que o nível do log não seja