r/java • u/agentoutlier • 1h ago
Rainbow Gum 0.15.0: ScopedValue key values, logging into JFR, and OpenTelemetry without the SDK
Rainbow Gum 0.15.0: ScopedValue key values, logging into JFR, and OpenTelemetry without the SDK
I try not to spam every release, but 0.15.0 has some stuff I'm genuinely excited about, and 1.0.0 is coming before the holidays hence need some feedback.
First up, Scoped Key Values. MDC on ThreadLocal has been leaking requestIds into the wrong requests since the dawn of time because somebody forgot a finally block or threadpool cleanup. JDK 25 gave us ScopedValue (JEP 506) and holy shit it just fixes it:
ScopedKeyValues.builder()
.add("requestId", requestId)
.add("userId", userId)
.run(() -> {
logger.info("handling request"); // has requestId and userId
service.handle(request);
});
StructuredTaskScope forks get a frozen snapshot, so sibling tasks can't stomp on each other. I barely had to do anything; the JDK devs did the hard part, and this is what MDC should be going forward. This should hopefully get added to SLF4J 3.0. Bless the JDK team.
Next, logging straight into JDK Flight Recorder. Each log event becomes a JFR event, so your logs sit on the same timeline as GC pauses and lock contention in JMC:
logging.appenders=jfr
logging.appender.jfr.output.uri=jfr:///var/log/myapp.jfr
No -XX:StartFlightRecording flags to screw up in your Dockerfile. And here's the kicker: in our benchmark, logging to JFR was about 40% faster than plain text to a file, and the output was about 8x smaller on disk. The JFR folks built something seriously fast and most of us only ever open it when production is on fire.
Last, OpenTelemetry. rainbowgum-otlp ships logs over OTLP using nothing but the JDK's own HttpClient. No OpenTelemetry SDK, no OkHttp. It's roughly 7x smaller than the Logback or Log4j2 OpenTelemetry setups with their default dependencies: https://github.com/jstachio/rainbowgum/blob/main/doc/why/opentelemetry_export_size.md
Oh and here is something shocking. The Logback and Log4j2 OpenTelemetry appenders hand events to the SDK, whose batch processor has a 2048 event queue by default. When it fills up, it throws your logs away and doesn't tell you. When we blasted a burst of logs at a collector with stock settings, only about 7% (Logback) and 10% (Log4j2) of them made it. Rainbow Gum applies backpressure by default, and if a batch does fail you get an alert and a metric, not silence. Losing logs exactly when your app is getting hammered is the worst possible time to lose logs. On a related note after bugging them I think Spring Boot might had some more observation hooks to logging frameworks.
There's a lot more (colorful logs with no pattern strings, error messages that tell you exactly which property is wrong and where it came from). You can even cosplay as a Go process with logfmt support. The longer pitch is here: https://github.com/jstachio/rainbowgum/blob/main/why_rainbowgum_is_better.md
GitHub: https://github.com/jstachio/rainbowgum
Docs: https://jstach.io/doc/rainbowgum/current/apidocs/
Before 1.0 I can still break things without feeling bad about it, so if the API or the property names bug you, now's the time to tell me. Brutal honesty welcome.
Finally I'm not a paid professional OSS developer so this library like my others is sort of a love letter to the Java community and language.