feat: add host_instance_id field for scaled-out worker attribution - #309
Open
yeongseon wants to merge 1 commit into
Open
feat: add host_instance_id field for scaled-out worker attribution#309yeongseon wants to merge 1 commit into
yeongseon wants to merge 1 commit into
Conversation
Resolve a per-worker host_instance_id from WEBSITE_INSTANCE_ID → WEBSITE_POD_NAME → CONTAINER_NAME → socket.gethostname(), mirroring the Functions host's GetInstanceId and the OpenTelemetry Azure faas.instance precedence. The value is PID-aware cached so forked workers recompute their own identity, and resolution never raises. It is stamped by ContextFilter and the LogRecordFactory, with JsonFormatter resolving directly as a fallback, and is reserved alongside the other context keys. Closes #308
There was a problem hiding this comment.
Pull request overview
Adds a new structured logging field, host_instance_id, to attribute logs to the specific Azure Functions worker instance in scale-out scenarios. The value is resolved via a host-aligned env-var precedence chain with a PID-aware per-process cache, then injected via both ContextFilter and the LogRecordFactory (with JsonFormatter providing a resolver fallback).
Changes:
- Introduces
_host_instance.pyresolver withWEBSITE_INSTANCE_ID → WEBSITE_POD_NAME → CONTAINER_NAME → socket.gethostname()precedence and PID-aware caching. - Stamps
host_instance_idinto records viaContextFilterandcontext_record_factory, and emits it fromJsonFormatter(record value or resolver fallback). - Updates reserved key sets, contract tests, and adds dedicated tests + documentation/README updates for the new field.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_host_instance.py | Adds unit tests for resolver precedence, fail-safety, PID-aware caching, formatter fallback, and emergency payload shape. |
| tests/test_contracts.py | Extends JSON contract key set to include host_instance_id. |
| src/azure_functions_logging/_json_formatter.py | Emits host_instance_id (record value or resolver fallback) and includes it in the emergency payload schema. |
| src/azure_functions_logging/_host_instance.py | New internal resolver module implementing precedence chain + PID-aware caching and test-only reset helper. |
| src/azure_functions_logging/_context.py | Injects host_instance_id in both ContextFilter and the LogRecordFactory path. |
| src/azure_functions_logging/_constants.py | Adds host_instance_id to _LIBRARY_RESERVED_KEYS so it’s protected from user extra collisions. |
| docs/usage.md | Documents host_instance_id, its resolution chain, caching behavior, and reserved-key behavior in factory mode. |
| docs/examples/json_output.md | Updates example output and field breakdown to include host_instance_id. |
| docs/configuration.md | Updates use_record_factory docs to include host_instance_id among injected/reserved fields. |
| README.md | Mentions host_instance_id injection and clarifies its relationship to cloud_RoleInstance. |
| README.ko.md | Syncs README changes and examples to include host_instance_id. |
| README.ja.md | Syncs README changes and examples to include host_instance_id. |
| README.zh-CN.md | Syncs README changes and examples to include host_instance_id. |
Suppressed comments (3)
docs/usage.md:116
- The "Fields populated from context" list omits
span_id, even though it is injected onto the record (and appears in the JSON payload). Adding it here avoids an incomplete field list.
- `invocation_id`
- `function_name`
- `trace_id`
- `cold_start`
- `host_instance_id`
docs/examples/json_output.md:41
- The "Top-level fields generated by formatter" list omits
span_id, whichJsonFormatterincludes in every payload. This makes the documented field breakdown incomplete.
- `invocation_id`
- `function_name`
- `trace_id`
- `cold_start`
- `host_instance_id` — identifies the worker instance that produced the log (resolved from `WEBSITE_INSTANCE_ID` → `WEBSITE_POD_NAME` → `CONTAINER_NAME` → `socket.gethostname()`, or `null` if none resolve)
docs/examples/json_output.md:71
- The parsing example JSON string is missing
span_id. SinceJsonFormatteroutputs it, leaving it out can cause confusion when readers compare their logs to this example.
line = '{"timestamp":"...","level":"INFO","logger":"orders","message":"order accepted","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"host_instance_id":"0d1f2a3b4c5d","exception":null,"extra":{"order_id":"ord-1001"}}'
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
104
to
107
| `use_record_factory` is an opt-in flag that also installs a global | ||
| `logging.LogRecordFactory` so context fields (`invocation_id`, `function_name`, | ||
| `trace_id`, `cold_start`) are injected at LogRecord creation time. It guarantees | ||
| `trace_id`, `cold_start`, `host_instance_id`) are injected at LogRecord creation time. It guarantees | ||
| context propagation even when handler filters are misconfigured or bypassed by |
|
|
||
| ```json | ||
| {"timestamp":"...","level":"INFO","logger":"orders","message":"service started","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"exception":null,"extra":{"service":"orders","region":"eastus"}} | ||
| {"timestamp":"...","level":"INFO","logger":"orders","message":"service started","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"host_instance_id":"0d1f2a3b4c5d","exception":null,"extra":{"service":"orders","region":"eastus"}} |
|
|
||
| ```json | ||
| {"timestamp":"2026-03-14T10:20:30.123456+00:00","level":"INFO","logger":"orders","message":"service started","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"exception":null,"extra":{"service":"orders","environment":"prod"}} | ||
| {"timestamp":"2026-03-14T10:20:30.123456+00:00","level":"INFO","logger":"orders","message":"service started","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"host_instance_id":"0d1f2a3b4c5d","exception":null,"extra":{"service":"orders","environment":"prod"}} |
Comment on lines
145
to
149
| {"timestamp": "2024-01-15T10:30:00+00:00", "level": "INFO", "logger": "function_app", | ||
| "message": "Processing order", "invocation_id": "abc-123-def", | ||
| "function_name": "process_order", "trace_id": null, "cold_start": true, | ||
| "host_instance_id": "0d1f2a3b4c5d", | ||
| "exception": null, "extra": {"order_id": "o-999"}} |
Comment on lines
145
to
149
| {"timestamp": "2024-01-15T10:30:00+00:00", "level": "INFO", "logger": "function_app", | ||
| "message": "Processing order", "invocation_id": "abc-123-def", | ||
| "function_name": "process_order", "trace_id": null, "cold_start": true, | ||
| "host_instance_id": "0d1f2a3b4c5d", | ||
| "exception": null, "extra": {"order_id": "o-999"}} |
Comment on lines
145
to
149
| {"timestamp": "2024-01-15T10:30:00+00:00", "level": "INFO", "logger": "function_app", | ||
| "message": "Processing order", "invocation_id": "abc-123-def", | ||
| "function_name": "process_order", "trace_id": null, "cold_start": true, | ||
| "host_instance_id": "0d1f2a3b4c5d", | ||
| "exception": null, "extra": {"order_id": "o-999"}} |
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
host_instance_idso logs can be attributed to the specific Azure Functions worker instance that produced them in scale-out.EnvironmentExtensions.GetInstanceIdand the OpenTelemetry Azurefaas.instancedetector:WEBSITE_INSTANCE_ID→WEBSITE_POD_NAME→CONTAINER_NAME→socket.gethostname();nullif all fail.ContextFilterand theLogRecordFactory;JsonFormatterresolves directly as a fallback for records that bypass filter/factory. Added to_LIBRARY_RESERVED_KEYSand the emergency payload.Behavior notes
host_instance_idis complementary to, but not guaranteed equal to, Application Insights'cloud_RoleInstance.host_instance_idis now a reserved key — passing it viaextra=underuse_record_factory=TrueraisesKeyError(same as other context fields).socket.gethostname()fallback may surface a container/machine name (documented).Tests & docs
tests/test_host_instance.pycovering each fallback tier, all-sources-fail →None, per-PID caching/recompute, JSON payload (record + resolver fallback), and emergency payload. Coverage 96.57% (≥95% gate).usage.md,configuration.md,examples/json_output.md, and all four READMEs (en/ko/ja/zh-CN) synced.Closes #308