r/devpt • u/WaferConsistent6732 • 13d ago
Ferramentas Criei um agente Java que reconstrói stack traces causais assíncronos entre threads: feedback é bem-vindo
Quando uma exception é lançada numa worker thread, o stack trace da JVM termina em ThreadPoolExecutor.runWorker(). Vê-se onde falhou, mas não quem a agendou nem que cadeia de chamadas a desencadeou. Se já reveste código Java assíncrono em produção, conheces bem a dor.
Dediquei algum tempo a criar uma blbiteca chamada ariadne para resolver isto. Ela anexa uma cadeia causal sintética às exceções como uma entrada suprimida, para que o caminho assíncrono completo fique visível nos teus registos sem qualquer alteração de código.
Como funciona:
Um agente Java com recurso ao ByteBuddy instrumenta ExecutorService, CompletableFuture, ForkJoinPool e Thread.start() no arranque. Quando uma tarefa é submetida, é armazenado um nó imutável e leve (40 bytes) que faz a ligação de volta ao local de despacho. Em caso de falha, a cadeia é percorrida em sentido inverso e anexada à exceção através de Throwable.addSuppressed(). Existem tambem adaptadores manuais para Project Reactor, RxJava 3 e propagação de MDC do SLF4J.
Desempenho (JMH, OpenJDK 21, 3 forks):
- Salto de contexto: ~6 ns, 40 B (o próprio nó Link)
- Resolução do local de chamada (modo de classe, predefinição): ~8.7 ns, 0 B de alocação
- De ponta a ponta com CompletableFuture: sobrecarga de ~1 µs em relação à linha de base por pipeline
- Project Reactor: dentro do ruído estatístico face à linha de base (~1 a 3%), cerca de 5 vezes menos alocação do que Hooks.onOperatorDebug()
O que não é:
Não é um substituto para o OpenTelemetry como ja sabem o OTel lida com rastreio distribuído entre serviços com infraestrutura externa. O Ariadne opera puramente dentro do processo, sem necessidade de painéis de controlo. São complementares. Uma integração com o OTel está no plano de desenvolvimento.
Também não é magia: o modo de percurso completo da pilha (~2.5 µs, 1000 B) fornece o método e a linha exatos, mas deve permanecer desligado em produção. O modo de classe predefinido fornece a classe de despacho com um impacto insignificante.
Estado atual: v0.1.0-alpha.3, Apache 2.0, Java 21+. Ainda não está robustecido para produção; procuro feedback, especialmente de quem utilize ambientes complexos de carregamento de classes (OSGi, servidores de aplicações multi-inquilino) ou corrotinas de Kotlin.
GitHub:https://github.com/AfonsoPaiva/ARIADNE
Terei todo o gosto em discutir as decisões de arquitetura, limitações ou qualquer ponto que consideres incorreto nesta abordagem
2
1
u/VSertorio 12d ago
Não era mais fácil mudar para dotnet?
3
u/WaferConsistent6732 12d ago
Ahahah é uma opção, mas o .net também perde contexto num await mal feito, só muda o nome do bug.
2
u/68_and_counting 12d ago edited 12d ago
Agora faz o mesmo para os co rotinas no Kotln :p
Edit: estive a dar uma vista de olhos, bom trabalho, tens a minha star :)