Java error logs were built in the ’90s for a human with grep — and for that reader they work. But the reader is changing: an AI assistant or an automated agent reads an error with no muscle memory, and every line is context and token cost. stacktale captures the failure at the source, structured for that reader — alongside your untouched logs.
The Java stack trace hasn't changed since 1995. The reader has.
For thirty years the format was aimed at one reader: a human at a terminal with grep and a hunch. Then, quietly, the most frequent reader of an error log became an AI assistant — and nobody redesigned the log for it. Every pasted stack trace becomes an interrogation: what happened before? which values? which version? — ten messages of the AI asking for context that existed at the moment of the error and was thrown away.
Post-processing can't fix this — by the time the log is written, the story is already gone. The only place to capture it is at the source. That's the whole idea, and it's why a 1-line stack trace from 1995 and a stacktale report are not the same artifact at all.
A checkout dies on an NPE. The root cause — a cache miss returning null — happened three log lines earlier, buried under other threads.
ERROR o.a.c.c.C.[dispatcherServlet] java.lang.NullPointerException: Cannot invoke "Customer.getEmail()" because "customer" is null at com.shop.OrderService.confirm(OrderService.java:87) at jdk.internal.reflect.DirectMethodHandle... at org.springframework.web.method.support.In... at org.springframework.web.servlet.mvc.method... at org.springframework.web.servlet.Dispatcher... at org.apache.catalina.core.ApplicationFilter... at org.apache.catalina.core.StandardWrapper... … 49 more framework frames … (what happened before? which values? which version? — you'll be asked all of it)
━━━ ERROR #cddfa41c ━━━ thread=exec-5 NullPointerException: "customer" is null at OrderService.confirm(:87) ← YOUR CODE log: "checkout failed for order {}" args=[889] mdc: traceId=70e1dfe7 fields: orderId=889 retryable=false story (traceId=70e1dfe7, last 4, 412ms): INFO OrderController POST /orders/889/confirm INFO CustomerClient fetch customer 555 → 404 WARN CustomerCache miss 555, returns null ERROR OrderService checkout failed 889 ← stack (distilled, 2 of 56): OrderService.confirm(:87) ← culprit … 54 collapsed (spring ×24, tomcat ×13) env: app=shop-api 1.4.2 | java 21 | linux ━━━ END #cddfa41c ━━━
Counted with a real tokenizer on the artifacts of an actual dogfooding session — a Spring Boot app hit over HTTP until it failed seven ways.
a classic log grows with traffic · a report file grows only with distinct errors
Captured at the source and never thrown away — because by the time the log is written, the story is already gone.
-javaagent capturing method args at the throw site.<dependency> <groupId>io.github.gabrielbbaldez</groupId> <artifactId>stacktale-spring-boot-starter</artifactId> <version>0.3.1</version> </dependency>
That's it. Reports land in errors-ai.log; your console stays exactly as it was.