Nesta página
Propriedade de arquitetura
O COADF P-4 é publicado na íntegra. Um identificador é criado quando um documento dá entrada e viaja com cada etapa de processamento, cada avaliação de confiança, cada decisão humana e a saída publicada. A trilha é somente de inserção, uma consulta reconstrói a cadeia, e a cadeia se exporta em JSON para quem não construiu o sistema. O esquema publicado da trilha de auditoria nomeia os campos: trace_id, timestamp, event_type, actor, input, output, decision, source_hash e immutable.
Duas propriedades a sustentam. Continuidade: cada etapa carrega a mesma identidade, em cada fronteira que atravessa. Persistência: o registro de cada etapa sobrevive, e ninguém consegue editá-lo depois para que a resposta pareça melhor.
Por que importa
A pergunta a que o P-4 responde chega tarde e de fora. Meses depois da publicação, alguém pergunta de onde veio um valor, o que o leu e quem olhou para ele. A resposta precisa poder ser reconstruída por uma pessoa que não construiu o sistema, a partir de registros que ninguém poderia ter reescrito nesse meio-tempo.
Um trace distribuído não é uma trilha de auditoria de negócio
Esta é a distinção que a maioria das implementações borra, porque as ferramentas se parecem: as duas têm um identificador, as duas atravessam serviços, as duas mostram etapas em sequência. Elas respondem a perguntas diferentes e fazem promessas diferentes.
| Aspecto | Trace distribuído | Trilha de auditoria |
|---|---|---|
| Finalidade | Explicar uma execução: latência, erros, estrutura de chamadas | Estabelecer o que aconteceu com uma transação, e quem agiu |
| Identidade | Um trace por requisição ou job, carregado no cabeçalho W3C traceparent | Um trace_id por transação de negócio, gerado na entrada |
| Tempo de vida | A duração da requisição; retenção medida em dias | Tanto quanto os registros que ela justifica |
| Amostragem | A amostragem é normal e permitida | Sem amostragem: uma entrada ausente é um defeito |
| Mutabilidade | Pipelines filtram, descartam e reescrevem por configuração | Somente de inserção: uma correção é uma nova entrada |
| Conteúdo | Spans, tempos, atributos escolhidos para diagnóstico | Eventos de negócio: entrada, saída, decisão, ator |
A especificação W3C Trace Context define o cabeçalho traceparent, e os SDKs do OpenTelemetry propagam tracecontext e baggage por padrão. Essa identidade é por execução. No OpenTelemetry, um trace que não é amostrado não é exportado, e o filter processor do Collector descarta a telemetria que corresponde a uma condição. Os dois são comportamento correto para diagnóstico e desqualificante para evidência.
Os dois se correlacionam: o trace_id de auditoria é gravado em cada span como atributo, e o identificador do trace distribuído, nos logs. Nenhum dos dois deve virar o outro. Uma única transação de negócio costuma abranger vários traces distribuídos: uma requisição de entrada, um job de processamento assíncrono, uma decisão tomada dias depois, a saída.
A correlação copia o identificador de auditoria para a telemetria, e a telemetria atravessa outras fronteiras: exportadores, fornecedores, regras de acesso e retenção normalmente mais frouxas que as do armazenamento de auditoria. O identificador deve ser classificado antes de sair: uma referência opaca, nunca dado pessoal nem segredo. Onde o identificador de auditoria carrega significado próprio, a correlação passa por uma referência separada e não sensível.
Arquitetura de exemplo · Não normativa
Traces distribuídos e uma trilha de auditoria
- Correlação pelo trace_id de auditoria
Descrição textual. Faixa superior, traces distribuídos: quatro traces separados, um para uma requisição de recepção, um para um job de processamento, um para uma decisão tomada dias depois e um para uma saída. Eles não estão ligados entre si. Faixa inferior, a trilha de auditoria: quatro entradas ilustrativas, recepção, processamento, decisão e saída, todas com o mesmo trace_id, persistidas separadamente da telemetria e não amostradas. Uma nota na faixa diz que os eventos são ilustrativos, não um fluxo de trabalho exigido pelo COADF. Uma linha tracejada liga cada trace à entrada que lhe corresponde, rotulada como um atributo de span que carrega o trace_id de auditoria.
Estratégias de implementação válidas
Gerar uma vez, na entrada
O identificador é criado onde a transação entra e em nenhum outro lugar. Todo componente posterior o exige e nenhum gera um. Um identificador ausente é um erro, nunca um motivo para criar um novo, e um valor padrão num modelo de mensagem é a forma mais comum de quebrar essa regra sem que ninguém perceba.
Carregá-lo explicitamente em cada fronteira
- No processo: um argumento de função ou o contexto da requisição.
- Entre serviços: um campo no contrato da requisição ou da mensagem, onde um esquema pode exigi-lo.
- Em trabalho agendado: um argumento do job.
- Em repouso: uma coluna em cada linha armazenada.
Bibliotecas de propagação carregam o contexto de execução por conta própria. A identidade de auditoria faz parte do contrato da aplicação e pertence ao payload.
Persistir somente por inserção
Várias realizações são válidas, e elas se combinam:
- Persistência somente de inserção. O repositório oferece inserir e ler, e nada mais.
- Privilégios do banco de dados. O papel da aplicação tem
INSERTeSELECTna trilha, e nenhumUPDATE,DELETEouTRUNCATE. - Triggers que recusam reescritas, como defesa em profundidade para sessões que detêm direitos mais amplos, com um trigger em nível de instrução para
TRUNCATE, porque no PostgreSQLTRUNCATEnão dispara triggersON DELETE. - Um modelo de eventos imutável, no qual uma correção é um novo evento que substitui um anterior.
- Uma convenção de substituição. O valor atual de um atributo é a entrada mais recente para ele; o histórico são todas elas.
Event sourcing é uma forma de obter um histórico somente de inserção, e não a única; ver a nota técnica.
Gravar a entrada junto com a mudança
A entrada de auditoria é gravada na mesma transação de banco de dados que a mudança de estado que ela descreve, ou por meio de um outbox transacional, para que nenhuma das duas possa existir sem a outra.
Tornar as novas tentativas idempotentes
O horário da etapa é marcado quando a etapa roda, e a mesma entrada é reenviada numa nova tentativa. As entradas recebem uma chave única natural, para que uma escrita repetida seja absorvida em vez de duplicada. No PostgreSQL, ON CONFLICT DO NOTHING pula uma linha que conflita com uma restrição ou um índice único em vez de lançar um erro, e é exatamente por isso que não pode ser a resposta inteira: um conflito nem sempre é uma nova tentativa. A mesma chave com conteúdo diferente é um segundo escritor ou um bug. A entrada armazenada deve ser comparada antes de a escrita ser tratada como nova tentativa, e a falha deve ser ruidosa quando elas diferem.
Reconstruir com uma consulta, exportar em JSON
Se a reconstrução precisa de alguém que conheça o sistema, o P-4 não vale. Ordena-se por carimbo de tempo, e declara-se o que essa ordem não resolve: entradas com o mesmo carimbo de tempo não são ordenadas por nenhum dos campos publicados.
O que um trigger não oferece
Privilégios e triggers são controles dentro da própria fronteira de confiança do banco de dados. No PostgreSQL, um superusuário ignora todas as verificações de permissão, e o dono da tabela pode desativar seus triggers. Uma tabela somente de inserção é, portanto, um controle forte contra a aplicação e fraco contra seus administradores. A detectabilidade de adulteração diante de insiders privilegiados exige um controle fora do banco de dados, e é uma propriedade separada da persistência somente de inserção. Nenhuma das duas é, por si só, uma afirmação jurídica sobre evidência.
Modos de falha
O identificador gerado de novo no meio do caminho
Um consumidor ou worker gera um identificador novo quando o campo está ausente, muitas vezes por um valor padrão num modelo de mensagem. O resultado são duas metades de uma transação e nenhuma consulta que as una.
O trace existe só nos logs
Logs são rotacionados, amostrados, não estruturados, e escritos por código que os trata como diagnóstico. Uma cadeia que só pode ser refeita a partir de logs não pode ser refeita de forma confiável.
Uma fronteira assíncrona perde o contexto
Pools de threads, executores e brokers não carregam nada a menos que algo copie o contexto. Em Python,
asyncio.to_threadé documentado como propagador do contexto atual;run_in_executornão documenta nada disso, e o próprioto_threaddo CPython copia o contexto explicitamente antes de chamá-lo. No Spring, um executor precisa de um task decorator que propague o contexto.Linhas armazenadas não podem ser relacionadas à sua evidência
Nenhum
source_hash, ou um hash de algo diferente dos bytes que foram de fato lidos, de modo que o mesmo insumo nunca pode ser reconhecido de novo.Novas tentativas produzem entradas ambíguas
Um carimbo de tempo obtido no momento da inserção transforma cada nova tentativa numa entrada nova e diferente, e a trilha passa a dizer que uma etapa aconteceu duas vezes.
Um conflito descartado como nova tentativa
Uma chave única e
ON CONFLICT DO NOTHING, e nada mais: uma segunda entrada com a mesma chave e uma decisão diferente desaparece sem erro, e a trilha fica com a que chegou primeiro.Uma correção sobrescreve
Um
UPDATEdestrói o que se considerava antes, e a trilha deixa de conseguir explicar por que uma saída anterior disse o que disse.Uma fronteira de serviço inicia um novo trace
Um gateway remove um cabeçalho que não conhece, uma biblioteca cliente não propaga, um job em lote começa do nada. Cada um é invisível até alguém tentar reconstruir.
O backend de tracing usado como trilha de auditoria
Sua amostragem, sua retenção e sua pipeline mutável são adequadas ao diagnóstico e inadequadas à evidência.
Relógios de parede usados para ordenar etapas entre hosts
Carimbos de tempo de máquinas diferentes só ordenam entradas na medida em que seus relógios concordam. Dentro de uma transação, um único escritor, ou uma ordem atribuída por um único escritor, é mais confiável que o horário de relógio de vários hosts.
Verificação
Teste de integração
Passa quando: De uma saída publicada de volta às suas fontes: uma consulta devolve todas as entradas, todas com o mesmo
trace_id, com osource_hashde cada documento lido.Prova de dentes: Remover o identificador de um salto assíncrono numa branch descartável. O teste de reconstrução precisa falhar, e não devolver uma cadeia mais curta que parece completa.
Teste de integração
Passa quando: Uma correção acrescenta: depois dela, as duas entradas existem, e a posterior substitui a anterior.
Prova de dentes: Trocar a inserção por uma atualização: o teste falha.
Teste de integração
Passa quando: Uma nova tentativa idêntica é absorvida, e a mesma chave com conteúdo diferente é recusada, não absorvida.
Prova de dentes: Tratar todo conflito como nova tentativa sem comparar: o teste que grava conteúdo diferente sob a mesma chave falha.
Teste de integração
Passa quando: Contra o banco de dados real, o papel da aplicação não consegue atualizar, apagar nem truncar a trilha.
Prova de dentes: Conceder
UPDATEao papel num banco de dados descartável: o teste falha.Teste de contrato
Passa quando: Todo esquema de mensagem que atravessa uma fronteira assíncrona exige o identificador de auditoria.
Prova de dentes: Tornar o campo opcional: o teste de contrato falha.
Teste de ponta a ponta
Passa quando: Uma requisição que se desdobra num job e numa mensagem mantém uma única identidade de auditoria, verificada na trilha persistida e não nos logs.
Evidência manual
Passa quando: Exportar uma cadeia em JSON e pedir a uma pessoa que não construiu o sistema que reconstrua, só a partir dela, o histórico da saída.
Realizações alternativas
- Event sourcing. O event store é o histórico, e o estado é derivado dele. Propriedades de auditoria fortes, e um grande compromisso de arquitetura.
- Change data capture. Mudanças de linha lidas do log do banco de dados. Útil para replicação; registra o que mudou, não quem decidiu nem por quê, então não substitui as entradas de auditoria de negócio.
- Armazenamento de objetos de gravação única para cadeias exportadas, onde a retenção precisa sobreviver ao banco de dados.
- Armazenamentos de ledger ou com detectabilidade de adulteração, onde a adulteração por insiders faz parte do modelo de ameaças.
Limitações
- O padrão não decide quais etapas são relevantes para uma regulamentação específica ou um produto específico.
- Persistência somente de inserção não é detectabilidade de adulteração, e nenhuma das duas é uma conclusão jurídica sobre evidência.
- Ele não define prazos de retenção.
- Ele depende de que todo componente honre o contrato. Um componente que não o honra é encontrado pelo teste de reconstrução, não impedido por ele.
Fontes
- W3C: Trace Context, the traceparent header · especificação · W3C Recommendation, Level 1 · Verificado em 2026-09-11
- OpenTelemetry: SDK environment variables: OTEL_PROPAGATORS · especificação · Specification 1.60.0 · Verificado em 2026-09-11
- OpenTelemetry: Sampling · documentação oficial · Verificado em 2026-09-11
- OpenTelemetry: Transforming telemetry · documentação oficial · Verificado em 2026-09-11
- PostgreSQL Global Development Group: TRUNCATE · documentação oficial · PostgreSQL 18 · Verificado em 2026-09-11
- PostgreSQL Global Development Group: INSERT: ON CONFLICT · documentação oficial · PostgreSQL 18 · Verificado em 2026-09-11
- PostgreSQL Global Development Group: Role attributes · documentação oficial · PostgreSQL 18 · Verificado em 2026-09-11
- PostgreSQL Global Development Group: ALTER TABLE: DISABLE TRIGGER · documentação oficial · PostgreSQL 18 · Verificado em 2026-09-11
- Python Software Foundation: asyncio: Task and to_thread · documentação da linguagem · Python 3.14 · Verificado em 2026-09-11
- CPython: Lib/asyncio/threads.py · repositório do projeto · CPython 3.14.7 · Verificado em 2026-09-11
- Spring Framework: Observability support: context propagation · documentação oficial · Spring Framework 7.0 · Verificado em 2026-09-11
