Suas ideias em realidade digital!
>
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.
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:
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:
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.
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.
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.
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
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
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.
X-Forwarded-For. Use com cuidado e valide conforme sua infraestrutura.
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.
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.
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"
}
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.
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.
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.
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) . '...';
}
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.
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.
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.
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'
]);
<?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.'
]);
}
INFO, WARNING e ERROR;Sem logs, qualquer erro vira investigação no escuro.
Logs não devem armazenar senhas, tokens completos ou credenciais.
Se alguém conseguir acessar seus arquivos de log pelo navegador, informações internas podem vazar.
Um único arquivo de log pode ficar grande demais e difícil de analisar.
Mensagem de erro sem rota, IP, horário ou usuário pode não ser suficiente para investigar o problema.
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.
Logs servem para registrar erros, eventos, requisições e informações importantes da aplicação, ajudando no diagnóstico e manutenção.
Você pode usar a função error_log ou criar uma função própria com file_put_contents para gravar mensagens em arquivo.
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.
Não é recomendado salvar tokens completos. Se necessário, registre apenas parte do token mascarado.
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.
Separe logs por data, crie rotina de limpeza para arquivos antigos ou use ferramentas de rotação de logs.
Logs servem para registrar erros, eventos, requisições e informações importantes da aplicação, ajudando no diagnóstico e manutenção.
Você pode usar a função error_log ou criar uma função própria com file_put_contents para gravar mensagens em arquivo.
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.
Não é recomendado salvar tokens completos. Se necessário, registre apenas parte do token mascarado.
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.
Separe logs por data, crie rotina de limpeza para arquivos antigos ou use ferramentas de rotação de logs.