Boas Práticas de Logging Profissional com Monolog em PHP para Times Ágeis Já leu

Por que Logging Profissional Importa Logging é frequentemente negligenciado por desenvolvedores iniciantes, mas é a diferença entre um código que funciona e um código que você consegue manter em produção. Quando algo falha em produção, os logs são sua única fonte de verdade. O Monolog é a biblioteca padrão da comunidade PHP para logging, utilizada por frameworks como Laravel, Symfony e inúmeros projetos de grande escala. Dominá-la significa escrever aplicações profissionais que são fáceis de debugar, monitorar e manter. Entendendo a Arquitetura do Monolog Componentes Principais O Monolog funciona com uma arquitetura simples mas poderosa: um Logger recebe mensagens, passa pelos Handlers (que decidem aonde enviá-las) e pelos Formatters (que definem como aparecem). Além disso, Processors adicionam contexto às mensagens, como ID de requisição ou informações de usuário. Este exemplo mostra a estrutura fundamental: criamos um logger, adicionamos um handler com um formatter específico e começamos a registrar eventos. Os níveis de log (info, warning, error) seguem o padrão RFC

Por que Logging Profissional Importa

Logging é frequentemente negligenciado por desenvolvedores iniciantes, mas é a diferença entre um código que funciona e um código que você consegue manter em produção. Quando algo falha em produção, os logs são sua única fonte de verdade. O Monolog é a biblioteca padrão da comunidade PHP para logging, utilizada por frameworks como Laravel, Symfony e inúmeros projetos de grande escala. Dominá-la significa escrever aplicações profissionais que são fáceis de debugar, monitorar e manter.

Entendendo a Arquitetura do Monolog

Componentes Principais

O Monolog funciona com uma arquitetura simples mas poderosa: um Logger recebe mensagens, passa pelos Handlers (que decidem aonde enviá-las) e pelos Formatters (que definem como aparecem). Além disso, Processors adicionam contexto às mensagens, como ID de requisição ou informações de usuário.

<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handlers\StreamHandler;
use Monolog\Formatters\LineFormatter;

// Criar logger
$logger = new Logger('minha_app');

// Criar handler que escreve em arquivo
$handler = new StreamHandler(__DIR__ . '/app.log');

// Definir formato
$formatter = new LineFormatter(
    "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n",
    "Y-m-d H:i:s"
);
$handler->setFormatter($formatter);

// Adicionar handler ao logger
$logger->pushHandler($handler);

// Usar
$logger->info('Aplicação iniciada');
$logger->warning('Aviso importante', ['user_id' => 123]);
$logger->error('Erro crítico', ['arquivo' => 'user.php', 'linha' => 45]);
?>

Este exemplo mostra a estrutura fundamental: criamos um logger, adicionamos um handler com um formatter específico e começamos a registrar eventos. Os níveis de log (info, warning, error) seguem o padrão RFC 5424, permitindo filtragem por severidade.

Múltiplos Handlers em Paralelo

Um dos grandes poderes do Monolog é ter múltiplos handlers simultaneamente. Você pode enviar erros críticos por email, warnings para um arquivo, e todas as mensagens para um serviço centralizado.

<?php
use Monolog\Handlers\RotatingFileHandler;
use Monolog\Handlers\SwiftMailerHandler;
use Monolog\Level;

$logger = new Logger('api');

// Handler 1: Logs gerais com rotação
$fileHandler = new RotatingFileHandler(__DIR__ . '/logs/app.log', 7);
$fileHandler->setLevel(Level::Debug);
$logger->pushHandler($fileHandler);

// Handler 2: Apenas erros críticos por email
$mailHandler = new SwiftMailerHandler($swift, 'admin@empresa.com');
$mailHandler->setLevel(Level::Error);
$logger->pushHandler($mailHandler);

$logger->debug('Debug info'); // Vai só pro arquivo
$logger->error('Falha na BD'); // Vai pro arquivo E pro email
?>

Configuração Profissional com Monolog

Usando Factory Pattern com Configuração

Em aplicações reais, você não instancia o logger manualmente em cada lugar. Use um container ou factory para gerenciar instâncias globais:

<?php
// LoggerFactory.php
class LoggerFactory
{
    private static $logger;

    public static function create()
    {
        if (self::$logger === null) {
            $logger = new Logger('producao');

            // Handler para arquivo com rotação
            $rotatingHandler = new RotatingFileHandler(
                storage_path('logs/app.log'),
                30,
                Level::Debug
            );

            // Adicionar processor para incluir IP e URL
            $logger->pushProcessor(function ($record) {
                $record->extra['ip'] = $_SERVER['REMOTE_ADDR'] ?? 'CLI';
                $record->extra['url'] = $_SERVER['REQUEST_URI'] ?? '';
                return $record;
            });

            $logger->pushHandler($rotatingHandler);
            self::$logger = $logger;
        }

        return self::$logger;
    }
}

// Uso em qualquer lugar
$logger = LoggerFactory::create();
$logger->info('Usuário logado', ['usuario_id' => $user->id]);
?>

Diferentes Configurações por Ambiente

Aplicações profissionais precisam de comportamentos diferentes em desenvolvimento, staging e produção:

<?php
class LoggerConfig
{
    public static function setup($environment)
    {
        $logger = new Logger('app');

        if ($environment === 'production') {
            // Produção: Syslog + Slack para erros críticos
            $logger->pushHandler(new SyslogHandler('app'));
            $slackHandler = new SlackHandler('seu-webhook-url');
            $slackHandler->setLevel(Level::Critical);
            $logger->pushHandler($slackHandler);
        } else if ($environment === 'development') {
            // Desenvolvimento: Arquivo + Console
            $logger->pushHandler(new StreamHandler('php://stdout'));
            $logger->pushHandler(new StreamHandler(storage_path('logs/debug.log')));
        }

        return $logger;
    }
}
?>

Contexto e Processors Avançados

Adicionando Dados Estruturados

A verdadeira força do Monolog está em contextualizar seus logs com dados estruturados que facilitam buscas e análises posteriores:

<?php
$logger = new Logger('transacoes');
$handler = new StreamHandler(__DIR__ . '/transacoes.log');
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);

// Usar contexto estruturado
$logger->info('Transação processada', [
    'transacao_id' => 'TX-12345',
    'usuario_id' => 789,
    'valor' => 150.50,
    'metodo_pagamento' => 'cartao_credito',
    'status' => 'aprovada',
    'tempo_processamento_ms' => 234
]);

// Output em JSON:
// {"message":"Transação processada","context":{"transacao_id":"TX-12345",...},"level":200}
?>

Este formato é ideal para agregar logs com ELK Stack, Datadog ou CloudWatch, permitindo criar dashboards e alertas sofisticados.

Processadores Customizados

Processadores adicionam dados automaticamente a cada log sem precisar passar manualmente:

<?php
$logger->pushProcessor(function ($record) {
    $record->extra['memory_usage_mb'] = round(memory_get_usage(true) / 1024 / 1024, 2);
    $record->extra['execution_time_ms'] = round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2);
    $record->extra['request_id'] = $_SERVER['HTTP_X_REQUEST_ID'] ?? uniqid();
    return $record;
});

$logger->info('Requisição processada'); 
// Automaticamente inclui memory_usage, tempo de execução e request_id
?>

Conclusão

Você aprendeu que o Monolog segue uma arquitetura clara de Logger → Handlers → Formatters, permitindo flexibilidade total na forma como seus logs são capturados e distribuídos. Em segundo lugar, dominar configuração profissional com factories, ambientes diferentes e contexto estruturado transforma logs de ruído em informação acionável. Por fim, utilize processadores para adicionar dados automaticamente, criando trilhas auditáveis que facilitam debugging e monitoramento em produção. O Monolog não é apenas uma ferramenta de logging — é sua seguradora contra os imprevistos do código em produção.

Referências


Artigos relacionados