Repository navigation
The target log tail no longer picks the csvlog/jsonlog sibling over the stderr log (#3997) - #4025
Merged
Merged
Conversation
…e gap when no stderr file survives (#3997) The shared tail three PostgreSQL collectors read (pg_log_events, pg_deadlocks, pg_plan_capture) picked the newest file in log_directory regardless of extension. A target logging to both stderr and csvlog/jsonlog writes every message to two files with the same modification instant, so the pick between them was arbitrary - and a cycle that picked the sibling read nothing a stderr-format parser recognizes, reporting a quiet target instead of a misread one. The newest CTE now excludes .csv/.json siblings, mirroring StoreLogSweep.IsStderrLogFile. A second marker (PgNoStderrLogFileException) names the narrower case where logging_collector is on but no stderr-format file survives that exclusion, classified the same non-fatal way as the existing logging_collector=off case. The RDS log route had the identical bug (DescribeDBLogFiles' newest-by-LastWritten pick) and gets the same fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
Resolved TheTailerExtraction_LeftBothSiblingsSqlByteIdentical's plan-capture pin: kept dev's #4016 regex fix (tolerates a prefix token before the pid bracket) and layered this branch's second UNION ALL marker arm on top of it, rather than reverting to the pre-#4016 pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
…4004 follow-up, #3997) PgLogEventsCollector.BuildQuery now refuses without CollectorContext.LogHashKey, per #4004 merged from dev after this branch started. Missed in the merge resolution; caught by re-running this test against the live rig. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
erikdarlingdata
enabled auto-merge (squash)
September 23, 2026 12:07
…enth early-return fault arm The new PgNoStderrLogFileException arm in DarlingWorker drains nothing and fetches nothing, so it passes drain: null and fetchPhases: null like the logging_collector-off arm beside it. Both counted pins move from 10 to 11, with the arm named in the comment. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
1 task done
erikdarlingdata
added a commit
that referenced
this pull request
Sep 23, 2026
…e pin and census tests read the files it changed (#4027) #4025's lane called CollectionLogDrainForensicsStoreTests pre-existing because it reproduced alone. It reproduced because the test counts DarlingWorker's fault arms and the lane had added one; dev's CI passed it. Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
erikdarlingdata
added a commit
that referenced
this pull request
Sep 23, 2026
…ntries in their sections (#4080) Adds 42 entries and 42 link refs (#3992, #3995, #3996, #3998, #4001, #4002, #4003, #4007, #4010, #4011, #4013, #4015, #4020, #4022, #4025, #4029, #4030, #4031, #4036, #4038, #4039, #4040, #4044, #4047, #4048, #4049, #4050, #4051, #4055, #4061, #4063, #4064, #4065, #4066, #4067, #4068, #4069, #4070, #4071, #4073, #4074, #4078). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran. #3989 left 26 entries under bare 'Changed' and 'Fixed' lines above '### Added'. They move into '### Changed' and '### Fixed', below the new entries, and one blank line stays under [Unreleased]. Claude-Session: https://claude.ai/code/session_01Ua31ugERL5DmhFVRtf6keQ Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #3997.
Why
The shared log tail (
PgServerLogTail.TailCteSql) thatpg_log_events,pg_deadlocksandpg_plan_captureall open with picked the newest file in
log_directoryby modification time alone, whatever its extension. Atarget whose
log_destinationincludescsvlogorjsonlogwrites every message to a second file besidethe stderr one, and
pg_ls_logdir()'smodificationis a stat mtime with no ordering guarantee between twofiles written the same instant.
Measured live on a fresh PostgreSQL 18.6 instance started with
log_destination = 'stderr,csvlog': thesibling pair (
postgresql-<ts>.log/postgresql-<ts>.csv) carried the identical modification instant,confirming the issue's "when both carry the same timestamp the pick is arbitrary" concern is real, not
theoretical. When the sibling won, none of the three stderr-format parsers matched anything in it, so a cycle
silently read as quiet instead of misread.
What changes
PgServerLogTail.TailCteSql'snewestCTE now excludes.csv/.jsonnames (AND name !~* '\.(csv|json)$'),mirroring
StoreLogSweep.IsStderrLogFile's existing rule for the store's own log sweep - one rule for "isthis file stderr-format" instead of two that could drift.
PgNoStderrLogFileException/PgServerLogTail.NoStderrLogFileMarkerSqlname the narrower case wherelogging_collectoris on but the exclusion above leavesnewestempty. All three collectors append it as asecond
UNION ALLmarker arm beside the existinglogging_collector = offone (mutually exclusive byconstruction - one requires the setting off, the other on), and
DarlingWorker.csclassifies it the samenon-fatal
PERMISSIONSway as the existingPgLoggingCollectorOffExceptionarm, so it never inflates theerror counts collector health, the daily band and self-alerts read.
RdsLogSource.NewestLogFileAsync) had the identical bug:DescribeDBLogFilesreturnsboth the stderr file and its
.csv/.jsonsiblings under the sameFilenameContains = "postgresql"filter, and
OrderByDescending(LastWritten)alone could pick either. Same fix: exclude both suffixes beforetaking the newest.
A measured scope note, found while live-testing the "csvlog-only" case
PgNoStderrLogFileException'sNOT EXISTScheck is correct for a directory with literally zero non-csv/jsonfiles, but that is narrower in practice than "this target has never had stderr configured": PostgreSQL's
syslogger writes one small stderr-format "breadcrumb" file (
LOG: ending log output to stderr/HINT: Future log output will go to log destination "csvlog", measured at 170 bytes) the moment it determines stderr is notamong the final destinations - even on a target started with
log_destination = 'csvlog'from its very firststart.
newestfinds that file, so the exception under-fires in the most common real-world case; the threecollectors instead read it every cycle and match nothing.
Recognising a breadcrumb-only file as equivalent to none needs a design call (a size heuristic risks a false
positive on a genuinely quiet but real stderr target; a text match on the breadcrumb inherits the same
lc_messagestranslation blind spotpg_plan_capture_readiness'smessage_localefacet already tracks), soI filed #4019 with the full measurement rather than guessing at a threshold. The exclusion fix in this PR (the
issue's actual, explicitly-scoped ask) stands on its own regardless of #4019's outcome.
Lite
Lite does not read PostgreSQL logs at all - no SQL Server equivalent to gate, no Lite-side change needed. The
PerformanceMonitor.Collectorsproject is shared, soLite.Testscarries this fix's tests (marker recognitionin
PgLoggingCollectorGateTests), same as the existinglogging_collector = offcoverage there.Test plan
TheTailerExtraction_LeftBothSiblingsSqlByteIdentical(PgLogEventsPipelineTests) updated deliberatelyfor the new
newestCTE text and the second marker arm on all three collectors; reverting eitherchange (proved twice, separately) makes it fail.
ThePlanReadRefusesTheNoStderrMarkerRow/TheDeadlockReadRefusesTheNoStderrMarkerRow(Lite.Tests/PgLoggingCollectorGateTests), the eventscollector's equivalent in
PgLogEventsPipelineTests, andWorker_Records_A_NoStderrLogFile_As_Permissions_Not_Error(
PgLoggingCollectorOffTests) pinning theDarlingWorker.csclassification arm.ACsvlogSiblingWrittenLater_DoesNotWinOverTheStderrFile(RdsLogSourceTests) -fails on the pre-fix shape (proved by reverting the filter).
logging_collector = on,log_destination = 'stderr,csvlog',log_line_prefix = '%m [%p] %Q '):PgLogTailStderrSiblingLiveTests.TheSharedTail_ReadsTheStderrFile_AndAllThreeFamiliesStillFindTheirEntriesprovokes a real plan (auto_explain), a real duplicate-key log event and a real two-session deadlock, then
runs the actual production
BuildQuery()/ReadAsync()for all three collectors against the same target(which has both the
.logand.csvfiles, same-second mtime) and confirms all three find their entryfrom the stderr file.
PgLogEventsPipelineTests,PgLoggingCollectorOffTests,RdsLogSourceTests,PgLogTailStderrSiblingLiveTests).origin/dev(which landed Plan capture keeps working under a custom log prefix with fields before the pid (#4008 follow-up) #4016's plan-capture regex fix and raw_line_hash and statement_fingerprint are unkeyed hashes a store reader can use to guess the literals they hide #4004's keyed loghashes while this branch was in flight - both reconciled in the merge):
-
Darling.Tests: 13028 total, 2 failed, 31 skipped. Both failures are pre-existing and unrelated -CollectionLogDrainForensicsStoreTests.ThePeerMarkIsCapturedAtDispatchNotAtCompletion(an off-by-onecount) and
StoreSelfMetricsTickRecoveryLiveTests.AFailedStoreLogCapture_...(a connection-recoverytest matching the known "Connection is not open" flake pattern) - confirmed by re-running each alone;
neither touches this PR's files.
-
Lite.Tests: 5178 total, 0 failed.Issues filed
🤖 Generated with Claude Code
https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv