description 17. Logs e Auditoria - Rastreamento de Ações Críticas

Logs são essenciais para debug, segurança e compliance. Quando algo dá errado (e sempre dá), logs são a única forma de descobrir o que aconteceu. Nesta seção, você vai implementar um sistema completo de logs e auditoria para rastrear ações críticas dos usuários.

info
LGPD/GDPR: Logs de auditoria são obrigatórios para compliance. Você precisa provar quem acessou dados pessoais e quando.

storage Estrutura de Banco de Dados

-- Tabela de Logs de Auditoria
CREATE TABLE audit_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NULL,
    ip_address VARCHAR(45) NOT NULL,
    user_agent TEXT,
    
    action VARCHAR(100) NOT NULL COMMENT 'login, logout, purchase, data_access, etc',
    resource_type VARCHAR(50) COMMENT 'user, course, order',
    resource_id INT NULL,
    
    details JSON COMMENT 'Dados adicionais da ação',
    
    severity ENUM('info', 'warning', 'error', 'critical') DEFAULT 'info',
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_user_id (user_id),
    INDEX idx_action (action),
    INDEX idx_created_at (created_at),
    INDEX idx_severity (severity)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Tabela de Erros
CREATE TABLE error_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NULL,
    endpoint VARCHAR(255) NOT NULL,
    
    error_type VARCHAR(100),
    error_message TEXT NOT NULL,
    stack_trace TEXT,
    
    request_data JSON,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_created_at (created_at),
    INDEX idx_endpoint (endpoint)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

code Backend PHP - Logger Service

// services/Logger.php
db = $db;
    }
    
    /**
     * Registrar ação de auditoria
     */
    public function audit(
        string $action,
        ?int $userId = null,
        ?string $resourceType = null,
        ?int $resourceId = null,
        array $details = [],
        string $severity = 'info'
    ): void {
        $sql = "
            INSERT INTO audit_log (
                user_id, ip_address, user_agent, 
                action, resource_type, resource_id, 
                details, severity
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            $userId,
            $this->getIpAddress(),
            $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown',
            $action,
            $resourceType,
            $resourceId,
            json_encode($details),
            $severity
        ]);
    }
    
    /**
     * Registrar erro
     */
    public function error(
        string $message,
        ?string $errorType = null,
        ?string $stackTrace = null,
        ?int $userId = null,
        array $requestData = []
    ): void {
        $sql = "
            INSERT INTO error_log (
                user_id, endpoint, error_type, 
                error_message, stack_trace, request_data
            ) VALUES (?, ?, ?, ?, ?, ?)
        ";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            $userId,
            $_SERVER['REQUEST_URI'] ?? 'Unknown',
            $errorType,
            $message,
            $stackTrace,
            json_encode($requestData)
        ]);
    }
    
    /**
     * Buscar logs de um usuário
     */
    public function getUserAudit(int $userId, int $limit = 50): array {
        $sql = "
            SELECT *
            FROM audit_log
            WHERE user_id = ?
            ORDER BY created_at DESC
            LIMIT ?
        ";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$userId, $limit]);
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Buscar logs críticos recentes
     */
    public function getCriticalLogs(int $hours = 24): array {
        $sql = "
            SELECT *
            FROM audit_log
            WHERE severity = 'critical'
                AND created_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
            ORDER BY created_at DESC
        ";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$hours]);
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Obter IP do usuário (considerando proxies)
     */
    private function getIpAddress(): string {
        $headers = [
            'HTTP_CF_CONNECTING_IP', // Cloudflare
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_REAL_IP',
            'REMOTE_ADDR'
        ];
        
        foreach ($headers as $header) {
            if (isset($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                
                // X-Forwarded-For pode ter múltiplos IPs
                if (str_contains($ip, ',')) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }
        
        return 'Unknown';
    }
}

// ===== EXEMPLOS DE USO =====

// Login bem-sucedido
$logger->audit(
    action: 'user_login',
    userId: $user['id'],
    severity: 'info'
);

// Compra realizada
$logger->audit(
    action: 'course_purchased',
    userId: $userId,
    resourceType: 'course',
    resourceId: $courseId,
    details: [
        'amount' => $amount,
        'payment_method' => 'pix'
    ],
    severity: 'info'
);

// Tentativa de acesso não autorizado
$logger->audit(
    action: 'unauthorized_access_attempt',
    userId: $userId,
    resourceType: 'course',
    resourceId: $courseId,
    severity: 'warning'
);

// Mudança de permissões
$logger->audit(
    action: 'role_changed',
    userId: $adminId,
    resourceType: 'user',
    resourceId: $targetUserId,
    details: [
        'old_role' => 'user',
        'new_role' => 'moderator',
        'changed_by' => $adminId
    ],
    severity: 'critical'
);

api API de Consulta de Logs

// api/admin/logs.php
hasRole($_SESSION['user_id'], 'admin')) {
    http_response_code(403);
    echo json_encode(['error' => 'Acesso negado']);
    exit;
}

$db = getDBConnection();
$logger = new App\Services\Logger($db);

$action = $_GET['action'] ?? 'all';
$userId = $_GET['user_id'] ?? null;
$severity = $_GET['severity'] ?? null;
$limit = min((int)($_GET['limit'] ?? 100), 1000);

try {
    $conditions = [];
    $params = [];
    
    if ($userId) {
        $conditions[] = "user_id = ?";
        $params[] = $userId;
    }
    
    if ($severity) {
        $conditions[] = "severity = ?";
        $params[] = $severity;
    }
    
    if ($action !== 'all') {
        $conditions[] = "action = ?";
        $params[] = $action;
    }
    
    $where = !empty($conditions) ? "WHERE " . implode(' AND ', $conditions) : '';
    
    $sql = "
        SELECT 
            al.*,
            u.name as user_name,
            u.email as user_email
        FROM audit_log al
        LEFT JOIN users u ON al.user_id = u.id
        {$where}
        ORDER BY al.created_at DESC
        LIMIT ?
    ";
    
    $params[] = $limit;
    
    $stmt = $db->prepare($sql);
    $stmt->execute($params);
    
    $logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    echo json_encode([
        'success' => true,
        'logs' => $logs,
        'total' => count($logs)
    ]);
    
} catch (Exception $e) {
    error_log("Erro ao buscar logs: " . $e->getMessage());
    http_response_code(500);
    echo json_encode(['error' => 'Erro ao buscar logs']);
}

monitor Monitoramento em Tempo Real

// Frontend React - Painel de Logs (Admin)
import { useState, useEffect } from 'react';
import axios from 'axios';

const API_URL = import.meta.env.VITE_API_URL;

export const LogsPanel = () => {
    const [logs, setLogs] = useState([]);
    const [loading, setLoading] = useState(true);
    const [filter, setFilter] = useState({
        severity: 'all',
        action: 'all',
        limit: 100
    });

    useEffect(() => {
        fetchLogs();
        
        // Auto-refresh a cada 30 segundos
        const interval = setInterval(fetchLogs, 30000);
        return () => clearInterval(interval);
    }, [filter]);

    const fetchLogs = async () => {
        try {
            const params = new URLSearchParams();
            if (filter.severity !== 'all') params.append('severity', filter.severity);
            if (filter.action !== 'all') params.append('action', filter.action);
            params.append('limit', filter.limit);

            const response = await axios.get(
                `${API_URL}/admin/logs?${params}`,
                { withCredentials: true }
            );

            setLogs(response.data.logs);
        } catch (error) {
            console.error('Erro ao buscar logs:', error);
        } finally {
            setLoading(false);
        }
    };

    const getSeverityColor = (severity) => {
        const colors = {
            info: 'blue',
            warning: 'orange',
            error: 'red',
            critical: 'purple'
        };
        return colors[severity] || 'gray';
    };

    return (
        

Logs de Auditoria

{loading ? (
Carregando logs...
) : ( {logs.map(log => ( ))}
Data/Hora Usuário Ação Detalhes Severidade IP
{new Date(log.created_at).toLocaleString()} {log.user_name || 'Sistema'}
{log.user_email}
{log.action} {log.details && (
{JSON.stringify(JSON.parse(log.details), null, 2)}
)}
{log.severity} {log.ip_address}
)}
); };

check_circle O que Logar?

✅ Sempre Logue

  • Login/Logout
  • Compras e transações
  • Mudanças de permissões
  • Acesso a dados sensíveis
  • Tentativas de acesso não autorizado
  • Erros críticos
  • Mudanças em configurações

❌ Nunca Logue

  • Senhas (nem hasheadas)
  • Tokens de autenticação
  • Dados de cartão de crédito
  • Informações muito sensíveis (CPF, etc)
  • Dados desnecessários
rocket_launch
Próximo Passo: Logs implementados! Agora vamos otimizar performance com cache para aplicação ficar ainda mais rápida.