refactor: give every component a named logger over one shared sink - #24
Merged
Merged
Conversation
Log sites reached spdlog directly, so each one wrote wherever the default logger happened to point, and a logger built before the host installed its sinks stayed attached to whatever was there at the time. utils::GetLogger(name) hands out a logger per component over a single fan-out sink. Where output goes is decided once, by AddLogSink, and applies to loggers that already exist as well as ones created later - so the bring-up loggers the backend creates at DLL attach write to the same places as everything else. Names are dotted and follow the source tree. The host adds a rotating file sink next to the ini, named per profile so concurrent instances do not share a file, and flushes every second. Loggers flush themselves from warn: forcing one per record would put an fflush on every line inside the shared sink lock, which a script logging in a loop would make everyone else pay. Default volume is unchanged - the framework logger at debug, everything else at spdlog default. Raw spdlog:: calls are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 16, 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.
Log sites reached
spdlogdirectly, so each one wrote wherever the default logger happened to point at the time. Worse, a logger built before the host installed its sinks stayed attached to whatever was there when it was created, which meant the backend's bring-up logging (created at DLL attach) was orphaned from the sinks the host installs later.What changed
utils::GetLogger(name)hands out one logger per component, all sharing a single fan-out sink. Where output goes is decided once, byAddLogSink, and applies to loggers that already exist as well as ones created afterwards — so bring-up loggers and late loggers write to the same places. Names are dotted and follow the source tree (hooks.manager,core.proxy,script.cache, …).The host adds a rotating file sink next to the ini, named per profile so concurrent instances don't share a file, and flushes every second.
On flushing
Loggers flush themselves from
warn, not from every record. Forcing a flush per record puts anfflushinside the shared sink's lock, so a script logging in a loop would stall everything else that logs — including the game thread. The one-second periodic flush bounds what a crash can lose instead.The trade-off is explicit: an
infoline can now be lost for up to a second across a hard crash, where previously it was already on disk.What did not change
Default volume. The framework's own logger stays at
debug, everything else at spdlog's default (info). This layer does not raise the volume of anything on its own — there's a test for exactly that.Raw
spdlog::calls are gone fromsrc/.Verification
clang-format clean; clang-tidy clean (114 files, full no-cache run). Two tests cover logger identity and the default level. The test suite itself has not been run by me.
🤖 Generated with Claude Code