T-1365 Send queued logs when the JVM exits - #31
Merged
Merged
Conversation
… or stop() overlaps a flush Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PetrHeinz
marked this pull request as ready for review
September 25, 2026 14:41
This was referenced Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The sender runs on a daemon thread, so an application that returns from
mainwithout stopping logback takes the JVM down with whatever is still queued in the appender: the docs' own "start logging" example (log a line, return) never sends anything unless the batch interval happens to fire first. And even when logback is stopped explicitly,stop()returned immediately whenever a flush was already in progress on another thread, leaving the in-flight batch and anything queued behind it to the daemon thread that the exiting JVM then kills.Two changes in
LogtailAppender:start()registers a JVM shutdown hook that callsstop(), andstop()deregisters it again so contexts stopped normally (logback's own<shutdownHook/>, Spring Boot, tests) don't accumulate hooks. Whenstop()runs from a hook, ours or logback's,removeShutdownHookthrowsIllegalStateException; that is the one case deliberately ignored.isFlushingflag becomes aReentrantLockheld for the whole flush, including the sub-batch loop that used to be a recursion.flush()still skips when another flush holds the lock;stop()now blocks on it, marks the appender stopped so no new events slip in, and sends everything left. Subclasses that read the protectedisFlushingfield can useflushLock.isLocked()instead.The test decorator's private lock existed only to wait for an asynchronous flush, so it now waits on the appender's lock instead.
The first commit is the two failing tests on their own and is expected to fail CI: one spawns a JVM that logs a line against a local HTTP server and returns from
main, the other holds a flush inside the request and checks thatstop()waits for it. The second commit makes them pass.🤖 Generated with Claude Code