PHP

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

8 min de leitura

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

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

<h2>Por que Logging Profissional Importa</h2>

<p>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.</p>

<h2>Entendendo a Arquitetura do Monolog</h2>

<h3>Componentes Principais</h3>

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

<pre><code class="language-php">&lt;?php

require &#039;vendor/autoload.php&#039;;

use Monolog\Logger;

use Monolog\Handlers\StreamHandler;

use Monolog\Formatters\LineFormatter;

// Criar logger

$logger = new Logger(&#039;minha_app&#039;);

// Criar handler que escreve em arquivo

$handler = new StreamHandler(__DIR__ . &#039;/app.log&#039;);

// Definir formato

$formatter = new LineFormatter(

&quot;[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n&quot;,

&quot;Y-m-d H:i:s&quot;

);

$handler-&gt;setFormatter($formatter);

// Adicionar handler ao logger

$logger-&gt;pushHandler($handler);

// Usar

$logger-&gt;info(&#039;Aplicação iniciada&#039;);

$logger-&gt;warning(&#039;Aviso importante&#039;, [&#039;user_id&#039; =&gt; 123]);

$logger-&gt;error(&#039;Erro crítico&#039;, [&#039;arquivo&#039; =&gt; &#039;user.php&#039;, &#039;linha&#039; =&gt; 45]);

?&gt;</code></pre>

<p>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.</p>

<h3>Múltiplos Handlers em Paralelo</h3>

<p>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.</p>

<pre><code class="language-php">&lt;?php

use Monolog\Handlers\RotatingFileHandler;

use Monolog\Handlers\SwiftMailerHandler;

use Monolog\Level;

$logger = new Logger(&#039;api&#039;);

// Handler 1: Logs gerais com rotação

$fileHandler = new RotatingFileHandler(__DIR__ . &#039;/logs/app.log&#039;, 7);

$fileHandler-&gt;setLevel(Level::Debug);

$logger-&gt;pushHandler($fileHandler);

// Handler 2: Apenas erros críticos por email

$mailHandler = new SwiftMailerHandler($swift, &#039;admin@empresa.com&#039;);

$mailHandler-&gt;setLevel(Level::Error);

$logger-&gt;pushHandler($mailHandler);

$logger-&gt;debug(&#039;Debug info&#039;); // Vai só pro arquivo

$logger-&gt;error(&#039;Falha na BD&#039;); // Vai pro arquivo E pro email

?&gt;</code></pre>

<h2>Configuração Profissional com Monolog</h2>

<h3>Usando Factory Pattern com Configuração</h3>

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

<pre><code class="language-php">&lt;?php

// LoggerFactory.php

class LoggerFactory

{

private static $logger;

public static function create()

{

if (self::$logger === null) {

$logger = new Logger(&#039;producao&#039;);

// Handler para arquivo com rotação

$rotatingHandler = new RotatingFileHandler(

storage_path(&#039;logs/app.log&#039;),

30,

Level::Debug

);

// Adicionar processor para incluir IP e URL

$logger-&gt;pushProcessor(function ($record) {

$record-&gt;extra[&#039;ip&#039;] = $_SERVER[&#039;REMOTE_ADDR&#039;] ?? &#039;CLI&#039;;

$record-&gt;extra[&#039;url&#039;] = $_SERVER[&#039;REQUEST_URI&#039;] ?? &#039;&#039;;

return $record;

});

$logger-&gt;pushHandler($rotatingHandler);

self::$logger = $logger;

}

return self::$logger;

}

}

// Uso em qualquer lugar

$logger = LoggerFactory::create();

$logger-&gt;info(&#039;Usuário logado&#039;, [&#039;usuario_id&#039; =&gt; $user-&gt;id]);

?&gt;</code></pre>

<h3>Diferentes Configurações por Ambiente</h3>

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

<pre><code class="language-php">&lt;?php

class LoggerConfig

{

public static function setup($environment)

{

$logger = new Logger(&#039;app&#039;);

if ($environment === &#039;production&#039;) {

// Produção: Syslog + Slack para erros críticos

$logger-&gt;pushHandler(new SyslogHandler(&#039;app&#039;));

$slackHandler = new SlackHandler(&#039;seu-webhook-url&#039;);

$slackHandler-&gt;setLevel(Level::Critical);

$logger-&gt;pushHandler($slackHandler);

} else if ($environment === &#039;development&#039;) {

// Desenvolvimento: Arquivo + Console

$logger-&gt;pushHandler(new StreamHandler(&#039;php://stdout&#039;));

$logger-&gt;pushHandler(new StreamHandler(storage_path(&#039;logs/debug.log&#039;)));

}

return $logger;

}

}

?&gt;</code></pre>

<h2>Contexto e Processors Avançados</h2>

<h3>Adicionando Dados Estruturados</h3>

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

<pre><code class="language-php">&lt;?php

$logger = new Logger(&#039;transacoes&#039;);

$handler = new StreamHandler(__DIR__ . &#039;/transacoes.log&#039;);

$handler-&gt;setFormatter(new JsonFormatter());

$logger-&gt;pushHandler($handler);

// Usar contexto estruturado

$logger-&gt;info(&#039;Transação processada&#039;, [

&#039;transacao_id&#039; =&gt; &#039;TX-12345&#039;,

&#039;usuario_id&#039; =&gt; 789,

&#039;valor&#039; =&gt; 150.50,

&#039;metodo_pagamento&#039; =&gt; &#039;cartao_credito&#039;,

&#039;status&#039; =&gt; &#039;aprovada&#039;,

&#039;tempo_processamento_ms&#039; =&gt; 234

]);

// Output em JSON:

// {&quot;message&quot;:&quot;Transação processada&quot;,&quot;context&quot;:{&quot;transacao_id&quot;:&quot;TX-12345&quot;,...},&quot;level&quot;:200}

?&gt;</code></pre>

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

<h3>Processadores Customizados</h3>

<p>Processadores adicionam dados automaticamente a cada log sem precisar passar manualmente:</p>

<pre><code class="language-php">&lt;?php

$logger-&gt;pushProcessor(function ($record) {

$record-&gt;extra[&#039;memory_usage_mb&#039;] = round(memory_get_usage(true) / 1024 / 1024, 2);

$record-&gt;extra[&#039;execution_time_ms&#039;] = round((microtime(true) - $_SERVER[&#039;REQUEST_TIME_FLOAT&#039;]) * 1000, 2);

$record-&gt;extra[&#039;request_id&#039;] = $_SERVER[&#039;HTTP_X_REQUEST_ID&#039;] ?? uniqid();

return $record;

});

$logger-&gt;info(&#039;Requisição processada&#039;);

// Automaticamente inclui memory_usage, tempo de execução e request_id

?&gt;</code></pre>

<h2>Conclusão</h2>

<p>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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://seldaek.github.io/monolog/" target="_blank" rel="noopener noreferrer">Documentação Oficial Monolog</a></li>

<li><a href="https://symfony.com/doc/current/logging.html" target="_blank" rel="noopener noreferrer">Symfony Logging Documentation</a></li>

<li><a href="https://laravel.com/docs/logging" target="_blank" rel="noopener noreferrer">Laravel Logging Guide</a></li>

<li><a href="https://www.php-fig.org/psr/psr-3/" target="_blank" rel="noopener noreferrer">PHP Standards: PSR-3 Logger Interface</a></li>

<li><a href="https://martinfowler.com/articles/patterns-of-distributed-systems/index.html" target="_blank" rel="noopener noreferrer">Martin Fowler - Structured Logging</a></li>

</ul>

Comentários

Mais em PHP

Migrations Manuais em PHP: Versionando o Banco de Dados na Prática
Migrations Manuais em PHP: Versionando o Banco de Dados na Prática

O que são Migrations e por que você precisa delas Migrations são controle de...

Composer: Gerenciamento de Dependências em PHP na Prática
Composer: Gerenciamento de Dependências em PHP na Prática

O que é Composer e por que você precisa dele Composer é o gerenciador de depe...

Como Usar Estruturas de Controle: if, else, switch e match em Produção
Como Usar Estruturas de Controle: if, else, switch e match em Produção

Estruturas de Controle: Dominando o Fluxo do Seu Código Estruturas de control...