Repository navigation
chore(octopus): log repeated GraphQL responses and slot lists briefly instead of in full - #5428
Merged
Merged
Conversation
…ead of in full The Octopus GraphQL logging was about 30% of a typical log: every request logged its fixed query text and every response its full body, poll after poll, and the saving sessions response (about 22 KB) was then logged a second time as "Fetched saving sessions data". Now a request or response already logged in full within the hour is logged as one line saying when it was last logged in full, and is logged in full again at least hourly so the full text is never only in a rotated-out log file. The duplicate saving sessions line is dropped. The decision is a new RepeatLogGate (utils.py): it keeps only a hash of each text per key, so several queries sharing one request context (one per Intelligent device) each keep their own entry, and drops entries older than the interval so its memory stays bounded. An error response is always logged in full, with the request that caused it. The JWT stays redacted on every line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd each Happy Hour skip hourly The slot list (about 16 lines) was logged on every plan though it rarely changes; it now goes through the same RepeatLogGate, logged in full when it changes and at least hourly, and as one line saying when it was last logged in full otherwise. Tariff comparison runs, whose slot prices differ, keep their own entries rather than forcing the live list to be logged again. An empty list is not logged, as before. The "Not offering Weekend Happy Hour" line is logged once an hour per event code rather than on every poll of the event list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… when gating GraphQL logs A 200 whose body could not be read (unreadable JSON, a KT-CT-1199 rate limit, read retries used up) or carried no data key still ends in "Failed to retrieve data from graphql query", but the gate treated it as a success: a repeat logged the request and response as brief lines, so the full request was not beside the failure. Now any response the function will not return data for is logged in full with its request, as an errors response already was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ot only its code The hourly gate on "Not offering Weekend Happy Hour" used the event code as its text, so every Happy Hour with no code hashed the same and only the first was logged each hour. Gate on the event id with the code, so each event is logged hourly as described. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on a brief repeat line The gate keeps several texts per key, so a response or slot list can change and change back within the hour (A, B, then A again). The brief line for the third was worded "unchanged since logged in full at" the time A was logged, which is false: it changed in between. "Same as" is true in every case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…log gate, and correct its comment A body of data null with no errors key returns None to the caller, which then reports the query as failed, but the gate treated it as a success and logged a repeat briefly without the request. Count a missing or null data value as a failure. The comment also claimed every gated failure returns no data; a partial error returned with data under ignore_errors is logged in full too, so say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Early 401/403 responses are not logged as promised, and failure-side request logging leaves stale gate timestamps.
2 open findings
What changed in this PR
Reduces repeated Octopus API and Intelligent-slot logging while retaining periodic diagnostic detail.
Changes:
- Adds reusable repeat-log gating.
- Applies gating to GraphQL, slot, and Happy Hour logs.
- Adds tests and updates documentation fixtures.
| File | Description |
|---|---|
apps/predbat/utils.py |
Adds RepeatLogGate. |
apps/predbat/const.py |
Defines the hourly interval. |
apps/predbat/octopus.py |
Gates repeated Octopus logs. |
apps/predbat/predbat.py |
Initializes the slot-log gate. |
apps/predbat/tests/test_octopus_logging.py |
Tests GraphQL log gating. |
apps/predbat/tests/test_rate_add_io_slots.py |
Tests slot-log gating. |
apps/predbat/tests/test_octopus_saving_event_type.py |
Tests Happy Hour logging. |
apps/predbat/tests/test_web_mcp.py |
Updates the large-log fixture. |
docs/components.md |
Updates the logging example. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…log gate The request re-logged in full beside a failure went straight to the log, so the gate kept the older full-log time. The next brief line named that older time, and once the hour ran out from it the request was logged in full again, though it had been logged in full more recently. Route it through the gate with always_full so the gate's record matches the log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first-attempt 401/403 branch either returns (an edge/WAF block) or retries with a fresh token before reaching the response logger, so the response body was never logged. Read it once there and log it through the gate with always_full. A second-attempt 401/403 is not logged here: it reaches async_read_response_retry, which logs its body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first-attempt 401/403 path and the normal path each built the same gated response log call. Move it into one local helper so the format and gate key cannot drift apart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A retry only follows a failure, and that failure has just logged the request in full. The retry's gate rightly logs the request briefly, pointing at that line, but its own 401/403 then logged the full request a second time. Skip the beside-failure re-log on a retry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…spell knows CI's cspell rejected 'relog' in test_octopus_logging.py. Renaming the test query avoids adding a made-up word to the dictionary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chalfontchubby
added a commit
that referenced
this pull request
Oct 12, 2026
…rning-status Conflicts resolved: - apps/predbat/const.py: kept both new constants - this branch's COMPONENT_ERROR_STATUS_PREFIX/SUFFIX and main's REPEAT_FULL_LOG_SECONDS (#5428). - apps/predbat/predbat.py: const import takes both sides' additions. - apps/predbat/output.py: const import keeps this branch's multi-line form plus main's new RUN_EVERY (used by history_slot/history_refresh_minutes). The calculate_yesterday() publish_html_plan call auto-merged with both status_warnings and main's refresh_minutes. - docs/inverter-setup.md: took main's reformatted LuxPower freeze automations and re-applied this branch's four 'Hold charging' exact matches as startswith('Hold charging'). 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.

Summary
On a live system with an Octopus account, about 40% of
predbat.logwas the same text logged again and again. The biggest single item was the saving sessions GraphQL response, about 22 KB, logged in full on every poll and then a second time as "Fetched saving sessions data". This logs a repeat as one short line instead, without losing anything.The decision is one small class,
RepeatLogGate(utils.py). For each key it keeps a hash of each text logged in full, not the text itself, so:The JWT stays redacted on every line.
Not changed
write_and_poll ... No write needed, which shows the control is being checked.ignore_errorsquery, so a query that fails the same way every poll is not shortened.Changes
apps/predbat/utils.py:RepeatLogGate.apps/predbat/const.py:REPEAT_FULL_LOG_SECONDS(3600).apps/predbat/octopus.py: the GraphQL logging, the slot list and the Happy Hour line go through the gate.apps/predbat/predbat.py: the slot-list gate inreset().docs/components.md: theget_logexample line no longer quotes the dropped line.test_octopus_logging(repeat, change, hourly re-log, the hourly interval restarting after a full log beside a failure, two queries per context, errors, responses with no body or no data, the gate itself, token redaction),test_rate_add_io_slots(slot list),test_octopus_saving_event_type(Happy Hour, including events with no code),test_octopus_waf_block(first-attempt 401/403 body logged once, request logged once across a failed retry), andtest_web_mcp(fixture line).Test plan
🤖 Generated with Claude Code