Repository navigation
feat(serviceevents): add ServiceEvents feature - #763
Conversation
9da40e0 to
0cfc256
Compare
0cfc256 to
c8435e1
Compare
|
Claude finished @vastin's task in 4m 27s —— View job Reviewing PR #763
Code Review SummaryThis is a well-structured, large feature addition (~21k lines) with good crash-safety discipline. The 1. Region extraction is fragile (low severity)File: region = (logs_endpoint or "").lower().split(".")[1]This extracts the region by splitting on dots and taking index match = re.match(AWS_LOGS_OTLP_ENDPOINT_PATTERN, (logs_endpoint or "").lower())
is_cw_endpoint = bool(match)
...
region = match.group(1)2. Traceback reference retention in investigation data (medium severity — memory)File: "traceback_info": (exc_type, exc_value, exc_traceback),Storing a reference to Consider formatting the traceback string immediately and storing only the string, or explicitly "traceback_info": None, # formatted eagerly below
"stack_trace": "".join(traceback.format_exception(exc_type, exc_value, exc_traceback)),3.
|
Add the ServiceEvents feature: function-level invocation metrics, HTTP endpoint tracking, per-exception error counts, deployment events, and incident snapshots, emitted as OTLP logs and metrics. - ServiceEvents package and unit tests - Contract-test apps, test suite, and mock-collector infrastructure - Configurator wiring to initialize ServiceEvents when enabled
c8435e1 to
48e592e
Compare
|
Claude finished @vastin's task in 7m 30s —— View job Reviewing PR #763 (post-sync)
Code Review — Post-Sync AnalysisThis review focuses on the current state of the 1. Traceback reference retention in
|
- #1 use shared botocore session in CloudWatch SigV4 log exporter instead of an undeclared boto3 dependency; fall back to a plain OTLP exporter when botocore is absent (was silently disabling all of ServiceEvents via the broad-except in initialize()). - #4 inject the Django middleware before building the stack so load_middleware runs exactly once on first startup, no longer double-initializing every third-party middleware. - #5 keep the import after a leading module docstring in the AST transform so instrumented modules retain __doc__. - #6 guard the FastAPI __init__ hook so a telemetry failure can never break app construction; correct the misleading middleware-ordering comment to document the innermost limitation. Memory/clarity hardening from the automated review: - format the captured traceback to a string eagerly in the per-function monitor instead of stashing the (exc_type, exc_value, exc_traceback) tuple, which pinned the whole frame chain alive in the ContextVar on the non-incident path; the incident collector consumes the string. - drop the dead self.exception_info attribute (never read; held a redundant traceback reference). - replace the stale hardcoded "0.14.2" sdk_version defaults with "" (always overridden by ServiceEventsConfig.sdk_version = ADOT_VERSION). Adds/updates regression tests for each. 708 ServiceEvents unit tests pass; black/isort/flake8/pylint (10.00/10)/codespell clean.
|
Claude finished @vastin's task in 6m 27s —— View job Reviewing PR #763 (post-sync)
Code Review — Post-Fix Analysis (commit
|
jj22ee
left a comment
There was a problem hiding this comment.
Added some comments generated through Claude based on AI review.
Investigation ContextVar leak: begin_investigation() seeds a dict every request, but only the incident path cleared it (via get_investigation_data). On the normal path the dict — and any captured traceback — lingered on pooled worker threads until the next request. Added clear_investigation_data() and call it unconditionally in every framework's request-finalize path (Flask teardown, FastAPI middleware finally, Django _finalize_request). Django settings.MIDDLEWARE side effect: instead of permanently rewriting the global settings.MIDDLEWARE, inject the ServiceEvents middleware only for the single load_middleware build and restore the customer's original MIDDLEWARE afterward. The chain is still built exactly once with all hooks registered, but settings.MIDDLEWARE is left exactly as the customer defined it (no persistent global change for code/introspection that reads it). C2: insert the PythonServiceEventsMonitor import after the module's mandatory preamble (docstring + any `from __future__`) so it never displaces a statement Python requires to come first. Extracted into a directly-tested _preamble_insert_index helper and a shared _is_docstring_expr predicate (no longer duplicated with the transformer). C3: guard ServiceEvents Flask hook registration so a telemetry failure during Flask.__init__ cannot break customer app construction (original __init__ still runs outside the guard). C6/C7: collapse the last-resort route label to a single <unmatched> sentinel in Flask and FastAPI (matching Django) so unmatched 404 / scanner traffic can't explode endpoint metric cardinality via raw paths. The sentinel now lives in a shared instrumentation/_constants module so all three frameworks reference one source of truth.
|
Claude finished @vastin's task in 3m 58s —— View job Reviewing PR #763 (post-sync)
Code Review — Post-Fix Analysis (commit
|
jj22ee
left a comment
There was a problem hiding this comment.
Apart from the bot's concern regarding unintended suppression of original application exception, lgtm from my side
…l exception When the wrapped ASGI app raises, the middleware sends a best-effort 500 response before re-raising. Those send() calls were unguarded: if send() itself raised (client disconnect, broken pipe, ASGI shutdown mid-error), that new exception replaced the original application exception and the re-raise never ran — altering which exception the host app sees. Wrap the error-response send in try/except so the failure is swallowed and the original exception always propagates, upholding the invariant that telemetry must never alter the host application's control flow.
|
Claude finished @vastin's task in 2m 13s —— View job Reviewing PR #763 (post-sync)
Code Review — Post-Fix Analysis (commit
|
… correlation/dedup hardening (#812) ## Description Refines the ServiceEvents endpoint span processor and incident-snapshot pipeline for the Python distro. Builds on the initial ServiceEvents feature (#763). Highlights: - **Framework-agnostic span processor** — the endpoint span processor is now unconditional and framework-agnostic; the `use_span_processor` flag and legacy per-framework hooks are removed. Renamed to `ServiceEventsSpanProcessor` to align with the Java distro. - **Incident trace correlation gated on SAMPLED** — an incident snapshot carries `trace_id`/`span_id` only when the request's trace was actually sampled, so no unresolvable correlation link is emitted. - **Incident flush interval derived from `max_same_error`** — `max(10s, 60s / N)`, maximizing the in-batch sampled-upgrade window. - **Hardened incident dedup/rate-limiting** — the dedup hash recovers the error identity (type + message) from investigation data instead of collapsing to route-only; the rate-limit slot is reserved atomically; gates are ordered dedup → rate → record so a rejected request never poisons the dedup map. - **Stable HTTP semconv fallback** — ingress operation names fall back to `url.path`/`url.full` and `http.request.method` (legacy-first ordering preserved) so stable-only-semconv apps still track unmatched requests. ## Testing - `tox` full matrix — all environments pass (2317+ unit tests per env, plus langchain/crewai integration envs). - Validated end-to-end against live CloudWatch on EC2 (5/5 ServiceEvents validators: deployment-event, incident-snapshot, incident-snapshot-latency, endpoint-error-metric, function-call-metric). ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [x] Refactor / code hardening ## Checklist - [x] Unit tests added/updated and passing - [x] `CHANGELOG.md` updated
Description of changes
This PR adds the ServiceEvents feature to the ADOT Python distro: a set of
higher-level telemetry signals derived from function-level and HTTP endpoint
activity, emitted via OTLP.
ServiceEvents is opt-in and bundled-gated behind Application Signals
(
OTEL_AWS_SERVICE_EVENTS_ENABLED/OTEL_AWS_APPLICATION_SIGNALS_ENABLED); itis disabled in AWS Lambda.
Summary
When active, ServiceEvents emits function-call duration metrics, HTTP endpoint
summaries, per-exception error counts, deployment events, and incident snapshots.
Function-level instrumentation is scoped by an explicit allowlist, and endpoint
tracking is wired through framework middleware for Flask, FastAPI, and Django.
What's included
Testing
tox -e lintandtox -e spellcheckpass.By submitting this pull request, I confirm that my contribution is made under