Repository navigation
fix(opal-server): cut per-update and policy-push INFO lines, configure master logging first (0.9.x) - #975
Open
CarlosMion wants to merge 15 commits into
Open
fix(opal-server): cut per-update and policy-push INFO lines, configure master logging first (0.9.x)#975CarlosMion wants to merge 15 commits into
CarlosMion wants to merge 15 commits into
Conversation
capture_records() collects every record at DEBUG and, through a real INFO sink, what a sink at OPAL's default level receives, with the global credential patcher disabled. Tests that pin a log line's level use it to assert both the emitted level and that the line is absent at the deployed level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
when_ready now calls configure_logs() before importing the scopes task and before preload_scopes. Until now nothing configured logging in the gunicorn master, so its logs - the preload's per-scope git lines, "Finished pre loading scopes..." and anything logged on import - went through loguru's default handler: DEBUG level, plaintext, no credential scrubbing. The client images share this file, so their master is configured too before when_ready returns. preload_scopes' docstring now states that it expects logging to be configured. The tests run the real configure_logs() against a loguru whose only sink stands in for that default handler, with SCOPES on and off, and each has a positive control where configure_logs is skipped. Port of the master change in #973 to release/0.9.x. Part of PER-16361. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"[pid] Publishing data update to topics: ..." was logged at INFO for every data update, with its entries as a structured kwarg. It is now DEBUG; its text and kwargs are unchanged. The "No topics were provided" warning in the same function stays at WARNING. The tutorials and the single-topic compose example quoted the line as default-level output; they now show the remaining lines and point to OPAL_LOG_LEVEL=DEBUG for the per-update line. Port of the master change in #973 to release/0.9.x. Part of PER-16361. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scopes policy push logged four to five INFO lines. Keep
NewCommitsCallbacks.trigger_notification's "Triggering policy update
for scope" at INFO and move the rest of the chain to DEBUG:
- "scope '...': Found new commits" (NewCommitsCallbacks.on_update)
- "Publishing to topics" (ScopedServerSideTopicPublisher.publish)
- "Refresh scope" (POST /scopes/{id}/refresh)
- "Webhook listener triggered (N)" (BasePolicyWatcherTask._on_webhook)
- "Repo was fetched after refresh request"
(GitPolicyFetcher._should_fetch)
Message texts are unchanged, as are their WARN/ERROR siblings and
"Target branch was not found" (INFO). The tests pin each level, and
exactly one OPAL INFO line per incremental push both from on_update and
end to end from ScopesService.sync_scope against a local clone.
Port of the master change in #973 to release/0.9.x.
Part of PER-16361.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove the unused `as e` binding and the placeholder-less f-string (flake8 F841, F541). The logged message is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Worker threads that earlier tests leave behind can log at any moment - here a late concurrent.futures callback error reached the default handler stand-in through a root InterceptHandler and failed test_when_ready_scopes_on_no_record_reaches_default_handler in a full run. when_ready and the stubbed preload run on the test's thread, and a real gunicorn master is a fresh process, so both the stand-in and the configured output now keep only that thread's records. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The per-update line is now DEBUG and the docs point readers there, so assert its logged entries carry the redacted URL; removing the call-site redact_url used to pass every test. - The master preload test's fake RedisDB now logs a credentialed REDIS_URL as a kwarg, as RedisDB does, so the masking of the master's own log setup is pinned for that line too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mment Matches the docstring above it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
configure_logs() in the master also takes over stdlib logging (a root InterceptHandler), so third-party records now reach the configured sink at their level; say so in when_ready's comment and pin it, with a control where configure_logs is skipped. The master-logging fixture now starts from a fresh process's root logger (no leftover handlers, level WARNING), so an InterceptHandler an earlier test left cannot route stdlib on its own. Also give the Redis line its own unmasked control arm and word the fake RedisDB comment so it holds where RedisDB already redacts at the call site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The control arm asserted the configured stream stayed empty, which holds whenever configure_logs is skipped. It now asserts no stdlib record reaches loguru at all, which fails if the fixture's fresh root logger keeps a handler an earlier test left. The test also logs an INFO record, the level whose routing the master gains. Smaller cleanups: name the saved root logger state, drop a redundant addHandler guard, give the git line its own unmasked control, keep the thread-filter notes on what they filter, and tighten wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot stopped reviewing on behalf of
CarlosMion due to an error
October 22, 2026 14:44
master_logs replaced loguru's core and stripped the root handlers before its try block, so a failing monkeypatch after the swap skipped the teardown and left the rest of the session logging to a dead stand-in. The config patches now run first and the swap sits inside the try. The docstring also says which pytest handlers stay detached afterwards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Hinted commit hash was not found" stays INFO by decision, and the PR lists it as kept, but no test caught a demotion. The new test reaches it through a real clone. The on_update parametrization gets plain ids in place of the error-message node ids. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fastapi_websocket_pubsub logs "calling subscription callbacks" at DEBUG across the >=1.0.0,<2 range release/0.9.x pins, so a server at the default level never prints those two sample lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The root-logger teardown keeps pytest's handlers and puts the saved ones back, mirroring the setup's split. A redundant hasattr assert goes: the next line reads both attributes. The hinted-hash test takes the clone from the fetcher instead of rebuilding its path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Fixes Issue
PER-16361 (opal-server log volume). This ports #973 to
release/0.9.x, the line the 0.9.x images are built from. Three changes.Changes proposed
1. The per-data-update line is DEBUG
opal_server/data/data_update_publisher.py::DataUpdatePublisher.publish_data_updates:2. The gunicorn master configures logging before it logs anything
scripts/gunicorn_conf.py::when_readynow callsconfigure_logs()first, before it importsopal_server.scopes.taskand runspreload_scopes().preload_scopes' docstring now states that precondition.On this line nothing configured logging in the master until now. Its output, including the SCOPES preload, went through loguru's default handler: DEBUG level, plaintext,
diagnose=Trueand no credential scrubbing. After this change:LOG_LEVEL=INFO. These include "Sync scope:", "Initializing git fetcher", "Repo found at", "Fetching remote", "Fetch completed" and the backoff-skip lines.LOG_SERIALIZE=true) and carry their real level.redis_utils' "Connecting to Redis: {url}" DEBUG line, which on this line passes the URL unredacted. At INFO that line is not emitted at all.gunicorn_conf.py, so their master is configured too beforewhen_readyreturns at theImportError.GUNICORN_CONFand callpreload_scopes()from it, callconfigure_logs()first.Side effects:
configure_logs()also takes over stdlib logging in the master (rootInterceptHandlerat level 0), so third-party records reach the configured sink at their own level, INFO included. Example:datadog.dogstatsd"Error submitting packet" WARNINGs, where no statsd agent is reachable. Workers already do this.LOG_TO_FILE=true, the master opens its own file sink.configure_logs()error now fails in the master, so gunicorn exits with code 1. Before, a worker failed to boot and gunicorn exited with code 3.3. One OPAL INFO line per scopes policy push
opal_server/scopes/service.py::NewCommitsCallbacks.trigger_notificationopal_server/scopes/service.py::NewCommitsCallbacks.on_updateopal_common/topics/publisher.py::ScopedServerSideTopicPublisher.publishopal_server/scopes/api.py::init_scope_router.refresh_scopeopal_server/policy/watcher/task.py::BasePolicyWatcherTask._on_webhookopal_server/git_fetcher.py::GitPolicyFetcher._should_fetchopal_server/policy/watcher/task.py::BasePolicyWatcherTask._on_webhookopal_server/scopes/service.py::NewCommitsCallbacks.on_updateopal_server/policy/watcher/callbacks.py::create_policy_updateopal_server/git_fetcher.py::GitPolicyFetcher._cloneopal_server/git_fetcher.py::GitPolicyFetcher._should_fetchPOST /scopes/{id}/data/update, so together with change 1, data updates have no OPAL INFO line at the default level. UseOPAL_LOG_LEVEL=DEBUGto see them.GitPolicySource.Docs
The two tutorials quoted the data-update line as default-level output. They now show the remaining lines and mention
OPAL_LOG_LEVEL=DEBUG. The compose example's comments drop the line. The healthcheck sample also drops two "calling subscription callbacks" lines, whichfastapi_websocket_pubsublogs at DEBUG.Also
scopes/task.py::_periodic_pollingdrops an unusedexcept … as eand a placeholder-less f-string (flake8 F841/F541). The message is unchanged.scripts/gunicorn_conf.py::when_ready.Not addressed (flagged)
git_fetcher.py::GitPolicyFetcher.fetch_and_notify_on_changes) causes most per-restart WARNING lines on a large fleet, and the workers log it too. Its text is unchanged here.scopes/task.py::trigger's WARN uses%s, so the data is never interpolated.refresh_scope's ERROR says "Unauthorized to delete scope".Differences from #973
scopes/task.pyonly gains the docstring precondition. This line has no fix: redact auth values in error logs, callback reports and server responses #968, so there was noconfigure_logs()call inpreload_scopesto remove.server_log_redaction_test.pydoesn't exist on this line. Thepreload_*_test.pyfiles never stubbedconfigure_logs.test_data_update_publisher.pygains_RecordingPublisherand the level test. Master's pydantic-v2 serialization test (fix(deps): support Starlette 1.x to address CVE-2026-48710 (#956) #959) is not ported.entriesURLs, credentialedREDIS_URL) and the stdlib-routing pin. Their test files are identical on both branches.Tests
opal_common/tests/log_capture.py: records at DEBUG and through a real INFO sink.opal_server/tests/gunicorn_master_logging_test.py:configure_logs()against a fresh loguru state whose only sink stands in for the default handler;REDIS_URLlogged as a kwarg the wayRedisDBdoes;preload_scopesdoesn't reconfigure logging;configure_logs.opal_server/tests/scopes_push_log_levels_test.py:on_updateand end to end viaScopesService.sync_scopeagainst a local clone;refresh_scope,_should_fetch's three lines and "Could not clone repo at".test_data_update_publisher.py(which also checks redaction of the DEBUG entries and the WARN URL),webhook_task_cleanup_test.pyandopal_common/tests/scoped_publisher_log_level_test.py.pytest packages/opal-server packages/opal-common: 568 passed;Check List (Check all the applicable boxes)
Note to reviewers
opal_client/tests/data_updater_test.py::test_data_updater_with_report_callbackfails intermittently on macOS. It fails the same way on an untouchedrelease/0.9.x, and this PR doesn't touch the client.🤖 Generated with Claude Code