-
-
Notifications
You must be signed in to change notification settings - Fork 2
Add custom resource attributes and span links to OtelTraceSink #476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
bmdhodl
wants to merge
3
commits into
main
Choose a base branch
from
feat/otel-sink-attrs-links
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+370
−4
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # QA_REPORT — OtelTraceSink enhancement | ||
|
|
||
| **Verdict: PASS (green check)** | ||
|
|
||
| Independent review pass (no separate `code-reviewer` subagent type available in | ||
| this environment; reviewer performed a fresh diff-only read against the task | ||
| spec, WORK_PLAN, RESEARCH, and repo guardrails). | ||
|
|
||
| ## Checks | ||
|
|
||
| | Check | Result | | ||
| |---|---| | ||
| | Backward compatibility | PASS — `resource_attributes` defaults `None`; 9 original tests pass unchanged | | ||
| | `start_span(links=)` correctness | PASS — empty list when no links; real OTel accepts the kwarg | | ||
| | Thread safety | PASS — `_build_links` uses `self._lock`, consistent with file | | ||
| | `Link` import guarded | PASS — added inside existing `_HAS_OTEL` try-block | | ||
| | Malformed input handling | PASS — non-list `links`, non-dict entries, missing span_id, unknown span_id, and non-dict attrs are all skipped safely | | ||
| | Scope vs NARROW mandate | PASS — no new module, no new dependency, no metrics, stays in `otel.py` | | ||
| | Secrets added | NONE | | ||
| | Denylist paths touched | NONE (only `sdk/agentguard/sinks/otel.py`, `sdk/tests/test_otel_sink.py`, repo-root MD artifacts) | | ||
| | Test coverage regression | NONE — 752-test suite passes; malformed-link regression coverage is included | | ||
| | Diff matches WORK_PLAN scope | PASS | | ||
| | Diff size | ~297 lines, well under 400 LOC gate | | ||
|
|
||
| ## Notes | ||
| - Pre-existing ruff F401 unused imports in `test_otel_sink.py` (`call`, | ||
| `MagicMock`, `patch`, `sys`) were left untouched — out of scope. CI avoids | ||
| them because the lint command targets production paths, not because Ruff | ||
| globally excludes tests. Production file `otel.py` passes `ruff check` | ||
| cleanly. | ||
| - `sdk/traces.jsonl` is a stray artifact from an unrelated test file; not | ||
| staged, not part of this change. | ||
|
|
||
| ## Issues | ||
| None blocking. Approved to proceed to PR. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # RESEARCH — OtelTraceSink enhancement | ||
|
|
||
| ## Verified facts | ||
| - `opentelemetry.trace.Link` exists in opentelemetry-api 1.x and takes | ||
| `(context: SpanContext, attributes: dict)`. Imported alongside the existing | ||
| `SpanKind`/`StatusCode` import, guarded by `_HAS_OTEL`. | ||
| - `Tracer.start_span(...)` accepts a `links` kwarg (list of `Link`). The fake | ||
| tracer in `tests/test_otel_sink.py` was extended to accept it. | ||
| - `Span.get_span_context()` returns the `SpanContext` a `Link` needs. | ||
| - Existing sink stores OTel spans by `span_id` in `self._spans` (otel.py:80). | ||
| Span links reuse that same registry — link targets must already be tracked. | ||
| - The repo lint path targets production code, so the 4 pre-existing F401 unused | ||
| imports in `test_otel_sink.py` are not CI-checked and are out of scope for | ||
| this task. The production file `agentguard/sinks/otel.py` passes `ruff check` | ||
| cleanly. | ||
| - Roadmap `ops/03-ROADMAP_NOW_NEXT_LATER.md:76` explicitly lists this exact | ||
| Later item: "OtelTraceSink supports custom resource attributes and span links | ||
| without pulling the SDK toward generic observability positioning." | ||
|
|
||
| ## Decisions | ||
| - Resource attributes are stamped as per-span attributes (`agentguard.resource.*`) | ||
| rather than constructing an OTel `Resource`, because the `Resource` belongs to | ||
| the `TracerProvider` the caller owns — the sink does not create the provider. | ||
| - All values coerced to `str` to avoid OTel attribute-type validation failures | ||
| when a caller passes ints/bools. | ||
|
|
||
| ## Test evidence | ||
| - `python -m pytest tests/test_otel_sink.py -q` → 20 passed. | ||
| - `python -m pytest tests/ -q` → 752 passed (no regressions). | ||
| - `python -m ruff check agentguard/sinks/otel.py` → All checks passed. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # WORK_PLAN — Narrow OtelTraceSink enhancement | ||
|
|
||
| ## Problem statement | ||
| The roadmap Later bucket sanctions enhancing the existing `OtelTraceSink` so it | ||
| supports custom resource attributes and span links, without pulling AgentGuard | ||
| toward a broad observability product. This task adds those two capabilities | ||
| inside the existing sink only — no new exporter module, no new dependency. | ||
|
|
||
| ## Approach | ||
| - `OtelTraceSink.__init__` gains an optional `resource_attributes` dict. Entries | ||
| are coerced to strings and stamped onto every span this sink emits, prefixed | ||
| `agentguard.resource.*`. Per-span attribute prefixing is the API-compatible | ||
| way to surface resource-level context without owning the TracerProvider's | ||
| OTel `Resource` (which would push the sink into provider-setup territory). | ||
| - Span links: a span-start event may carry a `links` list of | ||
| `{"span_id": ..., "attributes": {...}}` entries. On span start, links that | ||
| reference an already-tracked AgentGuard span are converted to OTel `Link` | ||
| objects via the span's `get_span_context()` and passed to `start_span(links=)`. | ||
| Links to unknown/malformed entries are skipped. | ||
|
|
||
| ## Files touched | ||
| - `sdk/agentguard/sinks/otel.py` — implementation + docstrings. | ||
| - `sdk/tests/test_otel_sink.py` — fake OTel `Link`, fake span context, new tests. | ||
|
|
||
| ## What "done" looks like | ||
| - [x] `resource_attributes` constructor arg, backward-compatible (defaults None). | ||
| - [x] Resource attrs stamped on every span; non-string values coerced. | ||
| - [x] `links` on span-start produce OTel `Link` objects to tracked spans. | ||
| - [x] Malformed/unknown links skipped without crashing, including truthy | ||
| non-list `links` values and non-dict link attributes. | ||
| - [x] All 9 original tests still pass; new tests cover both features. | ||
| - [x] No new dependencies; core SDK stays zero-dep. | ||
|
|
||
| ## Risks / assumptions | ||
| - Assumes OTel `Link` is importable from `opentelemetry.trace` (it is, since | ||
| opentelemetry-api 1.x) — guarded behind the existing `_HAS_OTEL` try/except. | ||
| - Assumes `start_span` accepts a `links` kwarg (it does in opentelemetry-api). | ||
| - Span links only resolve to spans this sink currently tracks; cross-process | ||
| links are out of scope (and out of the narrow mandate). |
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.