Docs›Segurança & Observabilidade›Observabilidade (logs)
Segurança & Observabilidade

Observabilidade (logs)

SQL log, request log, access log, change log, error log do PHP, dump de sessão e o dashboard de logs — conexão log.

Observabilidade (logs)

O MAD mantém sua própria trilha de auditoria — independente de qualquer log de aplicação que você adicione ao seu domínio — numa conexão Eloquent dedicada chamada log (uma das 6 conexões lógicas do framework; ver Conexões de banco). Cobre quem acessou o quê, qual SQL rodou, quais campos mudaram e o que o PHP reportou como erro, com 7 telas administrativas prontas em /app/logs/*. Para captura de exceptions/performance com sampling e envio a um receptor externo, ver Tracing (MadTrace) — os dois sistemas se complementam e até compartilham um bridge (DB::listen).

Conexão log e os models

Todos em App\Models\Log\*, conexão log, com os traits padrão (HasIdPolicy, HasMadAudit, HasMadSoftDeletes):

Model Tabela O que guarda
Access mad_log_access Sessões de login: login, sessionid, login_time/logout_time, access_ip, impersonated, e mode (web|rest|mcp — acesso via servidor MCP também vira uma linha aqui).
AccessNotification mad_log_access_notification Fila de avisos de novo login pendentes de envio (ver Autenticação).
Sql mad_log_sql 1 linha por instrução SQL persistida opcionalmente (opt-in — ver abaixo): comando, banco, tipo, IP, transaction_id, stack trace completo.
Request mad_log_request 1 linha por requisição HTTP a uma tela MAD: método, URI, query string, headers e body (como JSON).
Change mad_log_change Diff campo-a-campo de created/changed/deleted em qualquer model — quem mudou o quê, valor antigo → novo.

Sql e Change expõem um accessor log_trace_formatted que destaca em <b class="red"> os frames do stack trace que passam por app/control — é o mesmo dado que alimenta o drawer "ver trace" das telas (abaixo).

Como cada log é alimentado

Os logs de auditoria são opt-in e escritos manualmente pelos pontos do framework que decidiram chamá-los — não há um listener global ligando todos de uma vez:

  • Access — App\Service\Log\AccessLogService::registerLogin() / registerLogout(), chamado pelo pipeline de login/logout (Autenticação). O servidor MCP usa o mesmo model (registerMcpAccess()/registerMcpDenied(), mode='mcp') — ele faz auto-cura do schema (ensureMcpSchema()) porque adicionou a coluna mode depois que o migrator legado foi removido do projeto.
  • Request — App\Service\Log\RequestLogService::register($endpoint), chamado explicitamente pelas telas que querem essa trilha (não é todo request HTTP — é por opt-in de cada MadComponent).
  • Change — App\Service\Log\ChangeLogService::register($model, $old, $new), comparando o estado antes/depois de um save e gravando 1 linha por campo alterado. Também chamado explicitamente — não é um observer Eloquent global.
  • Sql — não tem um service de escrita próprio chamado pela aplicação. A persistência é feita pelo bridge DB::listen do MadTrace (MadServiceProvider::bootTrace()), e só grava quando mad.trace.sql_log=true (env MADTRACE_SQL_LOG, off por padrão) — esse flag liga a auditoria de SQL no banco além de mandar a query pro APM. Sem isso, a tela SQL Log (/app/logs/sql) fica vazia mesmo com o framework rodando normalmente. Ver Tracing para o resto do que esse bridge faz (amostragem, N+1, timing real).

As telas

Todas exigem mad.auth + mad.permission (routes/modules/logs.php):

Tela Classe Rota Tipo O que faz
Dashboard Dashboard /app/logs MadComponent KPIs de hoje/mês (acessos, SQLs) via mad-db-metric-card, com filtros [col, op, val] pré-montados.
Acessos AccessList /app/logs/acessos MadDataGrid Lista Access: login, IP, impersonação (badge), sessão. Busca manual via searchQuery closure (LIKE), não auto-filter.
Alterações ChangeView /app/logs/alteracoes MadDataGrid Lista Change: tabela, operação (badge created/changed/deleted), coluna, valor antigo/novo. Botão "ver trace" abre drawer com stack trace formatado.
SQL SqlList /app/logs/sql MadDataGrid Lista Sql (quando sql_log está ligado): comando com syntax highlight inline (transformSql), badge por tipo de statement. Botão "ver trace" abre o mesmo drawer formatado, com indentação de SQL própria (indentSql/highlightSql).
Requisições RequestList /app/logs/requisicoes MadDataGrid Lista Request: endpoint, URI, método, sessão.
Sessão SessionDumpView /app/logs/sessao MadComponent Dump de session()->all() (oculta _token/_previous/_flash) com botão para remover uma variável específica — útil para depurar estado preso.
Erro PHP PhpErrorView /app/logs/php_log MadComponent Lê as últimas ~200 linhas do error_log configurado no php.ini (ini_get_all()), agrupa por entrada e classifica por severidade (badge danger/warning/default). Sinaliza no próprio painel quando log_errors está off ou o arquivo é inacessível/não-gravável.

AccessList, ChangeView, SqlList e RequestList se cruzam por session_id: cada um expõe um filterSession(string $sessionid) chamável por link a partir de outra tela do mesmo grupo — "ver todos os SQLs desta sessão", "ver todos os acessos desta sessão" etc.

// routes/modules/logs.php
Route::middleware(['mad.auth', 'mad.permission'])->group(function () {
    MadRoutes::screen('log_dashboard', 'Dashboard');     // /app/logs
    MadRoutes::screen('access_log',    'AccessList');    // /app/logs/acessos
    // expose() (não screen()) — filterSession({sessionid}) precisa de
    // GET /{slug}/{method?}; screen() só registra GET /{slug}.
    MadRoutes::expose('change_log',    'ChangeView');    // /app/logs/alteracoes
    MadRoutes::expose('sql_log',       'SqlList');       // /app/logs/sql
    MadRoutes::expose('request_log',   'RequestList');   // /app/logs/requisicoes
    MadRoutes::screen('session_dump',  'SessionDumpView');  // /app/logs/sessao
    MadRoutes::expose('php_log',       'PhpErrorView');
});

Trace viewer compartilhado

SqlList::formatTrace() (estático) é reaproveitado por ChangeView para renderizar o stack trace num drawer comum: cabeçalho com classe/data, contexto (SQL formatado e destacado, ou "operação tabela (PK: valor)"), e os frames numerados com o caminho do arquivo, destacando frames que vêm de app/ou lib/mad/. Se você precisar de uma terceira tela que mostre stack traces no mesmo estilo, chame \App\Control\Log\SqlList::formatTrace() em vez de duplicar o parser.

Caveats

  • Sql e Request podem crescer rápido em produção com tráfego real — nenhuma das duas telas pagina por padrão além da paginação normal do MadDataGrid; não há rotina de poda automática embutida (diferente da Lixeira do GED, que tem retenção configurável). Considere uma rotina schedule() própria de limpeza se for manter sql_log ligado por muito tempo.
  • Request grava request_body como JSON do $_REQUEST bruto — inclusive de telas que recebem senha/token no payload. Diferente do MadTrace (que tem scrub_keys para mascarar password/token/cpf/... automaticamente), este log não mascara nada. Evite chamar RequestLogService::register() em telas que recebem segredos no corpo, ou trate isso antes de habilitar em produção.
  • Change não é automático. Nenhum model do framework dispara ChangeLogService::register() sozinho via observer Eloquent — é uma chamada manual da tela que quer auditoria de diff. Não espere ver mudanças de um model arbitrário aparecerem aqui sem essa chamada explícita.

Ver também

  • Tracing (MadTrace) — captura de exceptions e performance (schema separado, amostrado, com receptor externo).
  • Autenticação — o que popula Access/ AccessNotification.
  • Hardening de filtros e ordenação (grid) — as telas de log acima são MadDataGrid: coluna, operador e ORDER BY vindos do browser passam por allowlist antes de virar SQL.
  • Multi-tenancy — a conexão log faz parte do control-plane (junto com iam) e nunca é repontada por tenant, mesmo na estratégia Bridge.