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 colunamodedepois 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 cadaMadComponent).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 bridgeDB::listendo MadTrace (MadServiceProvider::bootTrace()), e só grava quandomad.trace.sql_log=true(envMADTRACE_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
SqleRequestpodem crescer rápido em produção com tráfego real — nenhuma das duas telas pagina por padrão além da paginação normal doMadDataGrid; não há rotina de poda automática embutida (diferente da Lixeira do GED, que tem retenção configurável). Considere uma rotinaschedule()própria de limpeza se for mantersql_logligado por muito tempo.Requestgravarequest_bodycomo JSON do$_REQUESTbruto — inclusive de telas que recebem senha/token no payload. Diferente do MadTrace (que temscrub_keyspara mascararpassword/token/cpf/... automaticamente), este log não mascara nada. Evite chamarRequestLogService::register()em telas que recebem segredos no corpo, ou trate isso antes de habilitar em produção.Changenão é automático. Nenhum model do framework disparaChangeLogService::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 eORDER BYvindos do browser passam por allowlist antes de virar SQL. - Multi-tenancy — a conexão
logfaz parte do control-plane (junto comiam) e nunca é repontada por tenant, mesmo na estratégia Bridge.