fix(context): keep cold-start state stable across generator resumes
Maintainers usually reply within 1 day
@yeongseon is already working on this.
Since Oct 18, 2026.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 56/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- observability
Research direction
Read the sync and async generator resume paths in src/azure_functions_logging/_decorator.py alongside cold-start consumption in src/azure_functions_logging/_context.py. Run the existing generator and cold-start tests, then add coverage that inspects emitted log records for resumed and interleaved generators. Done means cold-start values stay stable per invocation while caller context is restored at suspension and completion, including close and error paths.
Written by the indexing model from the issue text.
Description
Context
The first generator invocation's cold_start field switches from True to False after its first yield. Its later records retain the same invocation ID but now describe that invocation as warm. This contradicts the documented per-invocation meaning of cold_start and makes filters/queries for the first invocation miss most of the stream. Priority is medium because it corrupts observability metadata without changing the handler's business result.
Reviewed commit: 5343aa68b745e94a40041941f5f35915181ec4ff (azure-functions-logging 0.15.3). Reproduced locally on Python 3.12.14 using the reviewed checkout.
Complete standalone reproduction:
import logging
from types import SimpleNamespace
from azure_functions_logging import setup_logging, with_context
records = []
class Capture(logging.Handler):
def emit(self, record):
records.append((record.getMessage(), record.invocation_id, record.cold_start))
original_factory = logging.getLogRecordFactory()
setup_logging(logger_name="repro.cold", use_record_factory=True, level=logging.INFO)
logger = logging.getLogger("repro.cold")
logger.handlers = [Capture()]
@with_context
def stream(context):
logger.info("first")
yield 1
logger.info("second")
yield 2
logger.info("done")
try:
list(stream(SimpleNamespace(invocation_id="inv-A")))
print(records)
finally:
logging.setLogRecordFactory(original_factory)
Observed in a fresh process: [('first', 'inv-A', True), ('second', 'inv-A', False), ('done', 'inv-A', False)].
Expected: all three records have the same cold-start state (True for the first invocation); a later invocation has False. The same per-resume reinjection strategy is used by async generators and needs the same invariant.
Root cause / fix direction: each generator advance opens a new logging_context(ctx), and each entry calls inject_context(), which consumes _check_cold_start() again. Suspension must restore the caller's values, but resumption must reuse the generator invocation's previously selected cold-start value. Capture invocation metadata once per generator execution and reapply it safely per resume, without retaining cross-context reset tokens or reverting the isolation fixes.
Acceptance Checklist
- All records from the first sync and async generator invocation carry
cold_start=True, including records after yields and during cleanup. - Later invocations consistently carry
False, including interleaved generators. - Reapplying generator context does not consume another invocation's cold-start decision.
- Caller context is restored at every suspension and after completion/close/error.
- Preserve cross-task async close safety and existing ordinary sync/coroutine cold-start tests.
- Verify actual log records, rather than only reading internal ContextVars.
Out of scope
- Redefining cold start as a platform-level Azure metric.
- Changing ordinary explicitly nested
logging_context()semantics. - Adding new public decorator parameters.
References
- Sync generator reinjection at every advance.
- Async generator reinjection.
- Cold-start consumption in context injection.
- Documented first-invocation meaning.
- Closed #562: synchronous suspension boundaries.
- Duplicate check: repository searches for generator cold-start behavior found no equivalent report; the original suspension-leakage fix is retained.
- Dominant language
- Python
- Stars
- 15
- Forks
- 0
- Avg merge
- 7h 53m
- Merged PRs (30d)
- 90
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from yeongseon/azure-functions-logging-python
-
documentation good first issue priority: low size: XS
Difficulty 1/5 1-3 hours Newbie friendliness 88/100
yeongseon/azure-functions-logging-python#647 ·
Maintainers usually reply within 1 day
-
ci priority: high size: S
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
yeongseon/azure-functions-logging-python#625 · 2 comments ·
Maintainers usually reply within 1 day
-
fix(deps): cap untested future azure-functions SDK majorsPossibly taken @bfs2021 claimed this 6 days ago. Openbug good first issue priority: medium size: XS
Difficulty 3/5 1-2 days Newbie friendliness 25/100
yeongseon/azure-functions-logging-python#646 ·
Maintainers usually reply within 1 day
All issues in yeongseon/azure-functions-logging-python
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
521xueweihan/HelloGitHub#4237 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
jejjohnson/gaussx#721 ·
Maintainers usually reply within 1 day
-
mock-core-configs/etc/mock/rocky-10-riscv64.cfg contains the wrong architecture in config_opts['root']Possibly taken @jasonmontleon claimed this 4 days ago. Open
Difficulty 1/5 Under an hour Newbie friendliness 94/100
rpm-software-management/mock#1834 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 75/100
elastic/elastic-otel-python#683 ·
Maintainers usually reply within 1 day
-
ci-rerun: collect.py fails a whole pr-babysit cycle on one transient GitHub 5xx and never retriesOpenfollow-up
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day