fix(context): keep cold-start state stable across generator resumes

Closed
#586 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@yeongseon is already working on this.

Since Oct 18, 2026.

  • #596 by @yeongseon — open
  • #600 by @yeongseon — merged

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

bug needs-triage priority:medium

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

Dominant language
Python
Stars
15
Forks
0
Avg merge
7h 53m
Merged PRs (30d)
90

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from yeongseon/azure-functions-logging-python

All issues in yeongseon/azure-functions-logging-python

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.