Makrosites

Suas ideias em realidade digital!

Logs em PHP: como registrar erros, requisições e eventos da API

Logs em PHP: como registrar erros, requisições e eventos da API >

Criar logs em PHP é uma das práticas mais importantes para manter uma aplicação segura, estável e fácil de diagnosticar. Sem logs, quando uma API falha, um webhook não chega, um login dá erro ou uma consulta no banco quebra, fica muito difícil descobrir o que realmente aconteceu.

Em sistemas PHP, os logs podem registrar erros internos, requisições recebidas, eventos importantes, IP do usuário, rota acessada, método HTTP, status da resposta, tempo de execução e mensagens de falha. Isso ajuda muito no suporte, no debug e na manutenção do sistema.

Neste artigo, você vai aprender como criar logs em PHP puro, como registrar erros da API, como salvar logs em arquivo, como organizar logs por data e como evitar expor informações sensíveis.

Resumo prático: logs são essenciais para descobrir erros em produção. Registre o problema real no servidor, mas retorne mensagens seguras e genéricas para o usuário.

O que são logs em PHP?

Logs são registros de eventos que acontecem dentro da aplicação. Eles funcionam como um histórico técnico do sistema, mostrando o que ocorreu, quando ocorreu e, em alguns casos, quem realizou determinada ação.

Um log pode registrar desde uma falha de conexão com o banco até uma requisição recebida em uma API.

Exemplos de informações que podem ir para o log:

Por que logs são importantes em APIs PHP?

Em uma API PHP, muitos erros não aparecem visualmente para o usuário. O frontend pode receber apenas uma mensagem genérica como Erro interno no servidor, enquanto a causa real fica oculta por segurança.

Sem logs, você fica tentando descobrir o problema no chute. Com logs, você consegue investigar o que aconteceu com mais precisão.

Logs ajudam a responder perguntas como:

Usando error_log no PHP

A forma mais simples de registrar logs em PHP é usando a função nativa error_log.

<?php

error_log('Mensagem de teste no log');

Essa mensagem será enviada para o log configurado no servidor ou no php.ini.

Exemplo registrando uma variável:

<?php

$id = $_GET['id'] ?? null;

error_log('ID recebido: ' . $id);

Essa abordagem é útil para testes rápidos e diagnóstico inicial.

Registrando erro com try/catch

Em operações críticas, como conexão com banco, envio de e-mail ou chamada para API externa, use try/catch para capturar e registrar erros.

<?php

try {
    $pdo = new PDO(
        'mysql:host=localhost;dbname=sua_base;charset=utf8mb4',
        'usuario',
        'senha',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    );

} catch (PDOException $e) {
    error_log('Erro de conexão com banco: ' . $e->getMessage());

    http_response_code(500);
    echo json_encode([
        'success' => false,
        'message' => 'Erro interno no servidor.'
    ]);
    exit;
}

O usuário recebe uma mensagem genérica, enquanto o erro real fica salvo no log.

Criando uma função simples de log em arquivo

Para ter mais controle, você pode criar uma função própria para gravar logs em um arquivo específico.

<?php

function appLog(string $message): void
{
    $logDir = __DIR__ . '/logs';
    $logFile = $logDir . '/app.log';

    if (!is_dir($logDir)) {
        mkdir($logDir, 0755, true);
    }

    $linha = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;

    file_put_contents($logFile, $linha, FILE_APPEND);
}

appLog('Aplicação iniciada');

Esse código cria a pasta logs, caso ela não exista, e adiciona uma linha no arquivo app.log.

Organizando logs por data

Em aplicações com muitas requisições, é melhor separar os logs por data. Assim, cada dia tem seu próprio arquivo.

<?php

function appLog(string $message): void
{
    $logDir = __DIR__ . '/logs';

    if (!is_dir($logDir)) {
        mkdir($logDir, 0755, true);
    }

    $logFile = $logDir . '/app-' . date('Y-m-d') . '.log';

    $linha = '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL;

    file_put_contents($logFile, $linha, FILE_APPEND);
}

Com esse modelo, os arquivos ficam assim:

app-2026-06-13.log
app-2026-06-14.log
app-2026-06-15.log

Criando logs com níveis: INFO, WARNING e ERROR

Nem todo log representa erro. Alguns logs são apenas informativos, outros são alertas e outros representam falhas. Podemos criar níveis para organizar melhor.

<?php

function appLog(string $level, string $message): void
{
    $logDir = __DIR__ . '/logs';

    if (!is_dir($logDir)) {
        mkdir($logDir, 0755, true);
    }

    $logFile = $logDir . '/app-' . date('Y-m-d') . '.log';

    $linha = sprintf(
        "[%s] [%s] %s%s",
        date('Y-m-d H:i:s'),
        strtoupper($level),
        $message,
        PHP_EOL
    );

    file_put_contents($logFile, $linha, FILE_APPEND);
}

appLog('info', 'Usuário acessou a API');
appLog('warning', 'Tentativa de acesso sem token');
appLog('error', 'Erro ao conectar no banco');

Exemplo de saída:

[2026-06-13 10:30:00] [INFO] Usuário acessou a API
[2026-06-13 10:31:15] [WARNING] Tentativa de acesso sem token
[2026-06-13 10:32:20] [ERROR] Erro ao conectar no banco

Registrando requisições da API

Em APIs, é muito útil registrar informações sobre cada requisição recebida.

<?php

function getClientIp(): string
{
    return $_SERVER['HTTP_CLIENT_IP']
        ?? $_SERVER['HTTP_X_FORWARDED_FOR']
        ?? $_SERVER['REMOTE_ADDR']
        ?? 'unknown';
}

$method = $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN';
$uri = $_SERVER['REQUEST_URI'] ?? '';
$ip = getClientIp();

appLog('info', "Requisição recebida: {$method} {$uri} IP: {$ip}");

Esse log ajuda a entender quais rotas estão sendo acessadas e de onde vêm as chamadas.

Atenção: se você estiver atrás de proxy, CDN ou balanceador, o IP real pode estar em headers como X-Forwarded-For. Use com cuidado e valide conforme sua infraestrutura.

Registrando status HTTP da resposta

Você pode adaptar sua função de resposta JSON para registrar o status HTTP enviado.

<?php

function jsonResponse(int $statusCode, array $data): void
{
    appLog('info', 'Resposta enviada com status HTTP: ' . $statusCode);

    http_response_code($statusCode);
    header('Content-Type: application/json; charset=utf-8');

    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

Assim, cada resposta importante da API fica registrada no log.

Registrando tempo de execução da requisição

Para identificar lentidão, registre o tempo gasto pela requisição.

<?php

$inicio = microtime(true);

// Código da aplicação...

$tempo = microtime(true) - $inicio;

appLog('info', 'Tempo de execução: ' . round($tempo, 4) . ' segundos');

Esse tipo de log ajuda a descobrir endpoints lentos, consultas pesadas e gargalos de performance.

Log JSON: formato mais organizado para APIs

Em vez de salvar logs apenas como texto, você pode salvar cada linha como JSON. Esse formato é mais fácil de processar depois.

<?php

function apiLog(array $data): void
{
    $logDir = __DIR__ . '/logs';

    if (!is_dir($logDir)) {
        mkdir($logDir, 0755, true);
    }

    $logFile = $logDir . '/api-' . date('Y-m-d') . '.log';

    $data['datetime'] = date('Y-m-d H:i:s');

    file_put_contents(
        $logFile,
        json_encode($data, JSON_UNESCAPED_UNICODE) . PHP_EOL,
        FILE_APPEND
    );
}

Exemplo de uso:

<?php

apiLog([
    'level' => 'info',
    'method' => $_SERVER['REQUEST_METHOD'] ?? '',
    'uri' => $_SERVER['REQUEST_URI'] ?? '',
    'ip' => getClientIp(),
    'message' => 'Requisição recebida'
]);

Exemplo de linha gerada:

{
  "level": "info",
  "method": "GET",
  "uri": "/api/usuarios",
  "ip": "127.0.0.1",
  "message": "Requisição recebida",
  "datetime": "2026-06-13 10:30:00"
}

Registrando erros de banco com segurança

Em caso de erro no banco, registre a mensagem real no log, mas não mostre detalhes para o usuário.

<?php

try {
    $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE id = :id");
    $stmt->execute([
        ':id' => $id
    ]);

} catch (PDOException $e) {
    apiLog([
        'level' => 'error',
        'message' => 'Erro PDO',
        'error' => $e->getMessage(),
        'uri' => $_SERVER['REQUEST_URI'] ?? '',
        'method' => $_SERVER['REQUEST_METHOD'] ?? '',
        'ip' => getClientIp()
    ]);

    jsonResponse(500, [
        'success' => false,
        'message' => 'Erro interno no servidor.'
    ]);
}

Esse padrão evita expor detalhes internos na resposta pública da API.

Registrando usuário autenticado no log

Se sua API usa login ou JWT, também pode ser útil registrar o ID do usuário autenticado.

<?php

$usuarioId = $usuarioAutenticado['id'] ?? null;

apiLog([
    'level' => 'info',
    'message' => 'Usuário consultou lista de pedidos',
    'user_id' => $usuarioId,
    'uri' => $_SERVER['REQUEST_URI'] ?? '',
    'method' => $_SERVER['REQUEST_METHOD'] ?? '',
    'ip' => getClientIp()
]);

Isso ajuda em auditoria e investigação de ações no sistema.

Registrando webhooks recebidos

Webhooks são um caso em que logs ajudam muito. Quando uma integração externa chama sua API, você precisa saber se a requisição chegou, qual payload veio e qual resposta foi enviada.

<?php

$payload = file_get_contents('php://input');

apiLog([
    'level' => 'info',
    'message' => 'Webhook recebido',
    'uri' => $_SERVER['REQUEST_URI'] ?? '',
    'method' => $_SERVER['REQUEST_METHOD'] ?? '',
    'ip' => getClientIp(),
    'payload' => $payload
]);

Em produção, avalie se o payload possui dados sensíveis antes de salvar tudo no log.

O que não salvar em logs

Logs são úteis, mas também podem virar risco de segurança se armazenarem dados sensíveis.

Evite salvar:

Quando precisar registrar algo sensível para debug, prefira mascarar parte do valor.

<?php

function mascararToken(string $token): string
{
    return substr($token, 0, 10) . '...';
}

Como proteger a pasta de logs

A pasta de logs não deve ficar acessível publicamente pelo navegador. Se possível, mantenha os logs fora da pasta pública do site.

Estrutura recomendada:

/var/www/site
  /app
    /logs
  /public
    index.php

Se você precisar manter a pasta dentro do projeto público, bloqueie o acesso via configuração do servidor.

Bloqueando acesso aos logs com .htaccess

Em Apache, você pode criar um arquivo .htaccess dentro da pasta de logs:

Require all denied

Em hospedagens antigas, pode ser necessário:

Deny from all

O ideal é testar conforme a versão do Apache usada no servidor.

Rotação de logs: por que é importante?

Se os logs crescerem sem controle, podem ocupar muito espaço em disco. Por isso, é importante separar logs por data e, quando necessário, apagar ou arquivar arquivos antigos.

Exemplo simples para remover logs com mais de 30 dias:

<?php

function limparLogsAntigos(string $logDir, int $dias = 30): void
{
    foreach (glob($logDir . '/*.log') as $arquivo) {
        if (filemtime($arquivo) < strtotime("-{$dias} days")) {
            unlink($arquivo);
        }
    }
}

limparLogsAntigos(__DIR__ . '/logs', 30);

Em servidores Linux, também é possível usar ferramentas como logrotate.

Exemplo completo de logger simples em PHP

Abaixo temos uma classe simples para registrar logs em arquivo JSON.

<?php

class Logger
{
    private string $logDir;

    public function __construct(string $logDir)
    {
        $this->logDir = $logDir;

        if (!is_dir($this->logDir)) {
            mkdir($this->logDir, 0755, true);
        }
    }

    public function write(string $level, string $message, array $context = []): void
    {
        $file = $this->logDir . '/app-' . date('Y-m-d') . '.log';

        $data = [
            'datetime' => date('Y-m-d H:i:s'),
            'level' => strtoupper($level),
            'message' => $message,
            'context' => $context
        ];

        file_put_contents(
            $file,
            json_encode($data, JSON_UNESCAPED_UNICODE) . PHP_EOL,
            FILE_APPEND
        );
    }
}

Exemplo de uso:

<?php

$logger = new Logger(__DIR__ . '/logs');

$logger->write('info', 'API iniciada');

$logger->write('error', 'Erro ao consultar usuário', [
    'user_id' => 15,
    'route' => '/api/usuarios/15'
]);

Exemplo completo de API com logs

<?php

header('Content-Type: application/json; charset=utf-8');

class Logger
{
    private string $logDir;

    public function __construct(string $logDir)
    {
        $this->logDir = $logDir;

        if (!is_dir($this->logDir)) {
            mkdir($this->logDir, 0755, true);
        }
    }

    public function write(string $level, string $message, array $context = []): void
    {
        $file = $this->logDir . '/api-' . date('Y-m-d') . '.log';

        $data = [
            'datetime' => date('Y-m-d H:i:s'),
            'level' => strtoupper($level),
            'message' => $message,
            'context' => $context
        ];

        file_put_contents(
            $file,
            json_encode($data, JSON_UNESCAPED_UNICODE) . PHP_EOL,
            FILE_APPEND
        );
    }
}

function getClientIp(): string
{
    return $_SERVER['HTTP_CLIENT_IP']
        ?? $_SERVER['HTTP_X_FORWARDED_FOR']
        ?? $_SERVER['REMOTE_ADDR']
        ?? 'unknown';
}

$logger = new Logger(__DIR__ . '/logs');
$inicio = microtime(true);

function jsonResponse(int $statusCode, array $data): void
{
    global $logger, $inicio;

    $tempo = microtime(true) - $inicio;

    $logger->write('info', 'Resposta da API enviada', [
        'status_code' => $statusCode,
        'method' => $_SERVER['REQUEST_METHOD'] ?? '',
        'uri' => $_SERVER['REQUEST_URI'] ?? '',
        'ip' => getClientIp(),
        'duration' => round($tempo, 4)
    ]);

    http_response_code($statusCode);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

$logger->write('info', 'Requisição recebida', [
    'method' => $_SERVER['REQUEST_METHOD'] ?? '',
    'uri' => $_SERVER['REQUEST_URI'] ?? '',
    'ip' => getClientIp()
]);

try {
    $pdo = new PDO(
        'mysql:host=localhost;dbname=sua_base;charset=utf8mb4',
        'usuario',
        'senha',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    );

    $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;

    if ($id <= 0) {
        jsonResponse(422, [
            'success' => false,
            'message' => 'ID inválido.'
        ]);
    }

    $stmt = $pdo->prepare("SELECT id, nome, email FROM usuarios WHERE id = :id");
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();

    $usuario = $stmt->fetch();

    if (!$usuario) {
        jsonResponse(404, [
            'success' => false,
            'message' => 'Usuário não encontrado.'
        ]);
    }

    jsonResponse(200, [
        'success' => true,
        'data' => $usuario
    ]);

} catch (PDOException $e) {
    $logger->write('error', 'Erro PDO', [
        'error' => $e->getMessage(),
        'uri' => $_SERVER['REQUEST_URI'] ?? ''
    ]);

    jsonResponse(500, [
        'success' => false,
        'message' => 'Erro interno ao consultar os dados.'
    ]);

} catch (Throwable $e) {
    $logger->write('error', 'Erro inesperado', [
        'error' => $e->getMessage(),
        'file' => $e->getFile(),
        'line' => $e->getLine()
    ]);

    jsonResponse(500, [
        'success' => false,
        'message' => 'Erro interno no servidor.'
    ]);
}

Boas práticas para logs em PHP

Erros comuns ao criar logs em PHP

1. Não criar logs em produção

Sem logs, qualquer erro vira investigação no escuro.

2. Salvar informações sensíveis

Logs não devem armazenar senhas, tokens completos ou credenciais.

3. Deixar a pasta de logs pública

Se alguém conseguir acessar seus arquivos de log pelo navegador, informações internas podem vazar.

4. Não separar logs por data

Um único arquivo de log pode ficar grande demais e difícil de analisar.

5. Não registrar contexto do erro

Mensagem de erro sem rota, IP, horário ou usuário pode não ser suficiente para investigar o problema.

Checklist para logs em API PHP

Conclusão

Logs em PHP são fundamentais para diagnosticar erros, monitorar APIs e entender o comportamento da aplicação em produção. Com um bom sistema de logs, você consegue investigar erro 500, falhas de banco, webhooks, acessos indevidos, lentidão e problemas intermitentes.

O ideal é registrar informações úteis como data, rota, método, IP, status HTTP, tempo de execução e mensagem real do erro. Ao mesmo tempo, é importante proteger os logs e evitar salvar dados sensíveis como senhas, tokens e credenciais.

Mesmo em PHP puro, é possível criar uma estrutura simples e eficiente de logs usando arquivos separados por data, níveis de severidade e registros em formato JSON.

Perguntas frequentes sobre logs em PHP

Para que servem logs em PHP?

Logs servem para registrar erros, eventos, requisições e informações importantes da aplicação, ajudando no diagnóstico e manutenção.

Como criar um log simples em PHP?

Você pode usar a função error_log ou criar uma função própria com file_put_contents para gravar mensagens em arquivo.

É seguro salvar erros do banco no log?

Sim, desde que o arquivo de log esteja protegido e não seja acessível publicamente. O erro real não deve ser exibido para o usuário final.

Posso salvar token JWT no log?

Não é recomendado salvar tokens completos. Se necessário, registre apenas parte do token mascarado.

Onde devo salvar os arquivos de log?

O ideal é salvar fora da pasta pública do site. Se isso não for possível, bloqueie o acesso à pasta de logs pelo servidor.

Como evitar que o arquivo de log fique muito grande?

Separe logs por data, crie rotina de limpeza para arquivos antigos ou use ferramentas de rotação de logs.

Perguntas frequentes sobre logs em PHP

Para que servem logs em PHP?

Logs servem para registrar erros, eventos, requisições e informações importantes da aplicação, ajudando no diagnóstico e manutenção.

Como criar um log simples em PHP?

Você pode usar a função error_log ou criar uma função própria com file_put_contents para gravar mensagens em arquivo.

É seguro salvar erros do banco no log?

Sim, desde que o arquivo de log esteja protegido e não seja acessível publicamente. O erro real não deve ser exibido para o usuário final.

Posso salvar token JWT no log?

Não é recomendado salvar tokens completos. Se necessário, registre apenas parte do token mascarado.

Onde devo salvar os arquivos de log?

O ideal é salvar fora da pasta pública do site. Se isso não for possível, bloqueie o acesso à pasta de logs pelo servidor.

Como evitar que o arquivo de log fique muito grande?

Separe logs por data, crie rotina de limpeza para arquivos antigos ou use ferramentas de rotação de logs.