Skip to content

feat(observability): emit guardrail findings as span events on ETW DTO path#260

Open
nikhilNava wants to merge 1 commit into
mainfrom
nikhilc/etw-guardrail-findings-span-events
Open

feat(observability): emit guardrail findings as span events on ETW DTO path#260
nikhilNava wants to merge 1 commit into
mainfrom
nikhilc/etw-guardrail-findings-span-events

Conversation

@nikhilNava

@nikhilNava nikhilNava commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Guardrail findings were dropped entirely on the ETW DTO logging path (A365EtwLogger.LogApplyGuardrailApplyGuardrailDataBuilderBaseData.ToDictionaryEtwLogProcessorExportFormatter.FormatLogDataLogJson). Unlike the Activity span path (which emits findings via ApplyGuardrailScope.RecordFindingMapEvents), this path had no findings parameter, no events carrier, and FormatLogData emitted no events field — so findings logged through it were invisible to downstream consumers.

This change brings the DTO path to parity, mirroring the prior etw-span-status work.

Changes

Scoped to the ETW DTO path — no new types in Runtime/Common, Activity path untouched.

  • IA365EtwLogger/A365EtwLogger.LogApplyGuardrail: optional IEnumerable<GuardrailFinding>? findings parameter (backward compatible).
  • ApplyGuardrailDataBuilder: maps each finding to an event dictionary (timeUnixNano/name/attributes), mirroring RecordFinding's keys, value types, and conditional inclusion.
  • BaseData: generic Events carrier; ToDictionary() emits "Events" only when non-empty.
  • BaseDataBuilder: ToUnixNanos helper (DTO-local).
  • ExportFormatter.FormatLogData: emits the Events array (omitted when absent).

Event structure parity

The emitted event JSON is pinned to match the Activity path's finding events exactly (event name microsoft.security.finding, same microsoft.security.* attribute keys, preserved double/string[] value types). A cross-path consistency test runs one finding through both paths and asserts the event objects are structurally equal (ignoring timeUnixNano), preventing drift.

Testing

  • New tests: ApplyGuardrailDataBuilderTests, ExportFormatterEventsTests (incl. cross-path consistency), BaseData events tests, ETW end-to-end LogApplyGuardrail findings tests.
  • Full solution builds with 0 warnings/errors; all 357 Observability.Runtime tests pass; dotnet format clean.

Notes

  • The A365EtwLogger.cs diff includes pre-commit-hook whitespace cleanup on untouched Log* methods; the substantive change is the findings parameter.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

@nikhilNava nikhilNava requested a review from a team as a code owner June 26, 2026 19:31
Copilot AI review requested due to automatic review settings June 26, 2026 19:31
@nikhilNava nikhilNava requested a review from a team as a code owner June 26, 2026 19:31
@github-actions

Copy link
Copy Markdown

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR brings the ETW DTO logging path for apply_guardrail into parity with the Activity span export path by allowing guardrail findings to be carried through DTOs and emitted as OTLP-style span events in the formatted ETW JSON payload.

Changes:

  • Extended IA365EtwLogger.LogApplyGuardrail / A365EtwLogger.LogApplyGuardrail with an optional findings parameter and forwarded it into DTO construction.
  • Added a generic Events carrier to DTO base data, mapped findings into OTLP-style event dictionaries in ApplyGuardrailDataBuilder, and emitted the Events array from ExportFormatter.FormatLogData when present.
  • Added focused unit/integration tests (including a cross-path structural consistency test vs. the Activity path) plus a design spec documenting the intended schema.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Etw/EtwLoggerTests.cs Verifies ETW end-to-end JSON output includes Events for findings and omits it when absent.
src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/DTOs/Builders/ApplyGuardrailDataBuilderTests.cs Validates findings → event mapping shape, conditional attribute inclusion, and value types.
src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/DTOs/BaseDataTests.cs Confirms BaseData.Events defaults empty and only serializes when populated.
src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterEventsTests.cs Ensures FormatLogData emits/omits Events correctly and enforces DTO-vs-Activity event structural parity.
src/Observability/Runtime/Etw/IA365EtwLogger.cs Adds optional findings parameter to the public ETW logger interface contract.
src/Observability/Runtime/Etw/A365EtwLogger.cs Accepts and forwards findings into the DTO builder for apply_guardrail.
src/Observability/Runtime/DTOs/Builders/BaseDataBuilder.cs Adds a DTO-side ToUnixNanos helper to timestamp DTO-emitted events consistently with the Activity path.
src/Observability/Runtime/DTOs/Builders/ApplyGuardrailDataBuilder.cs Maps GuardrailFinding instances into OTLP-style event dictionaries attached to the DTO.
src/Observability/Runtime/DTOs/BaseData.cs Introduces the Events carrier and conditionally includes it in ToDictionary().
src/Observability/Runtime/Common/ExportFormatter.cs Emits Events in FormatLogData payload when present (null omitted by serializer).
docs/superpowers/specs/2026-06-26-etw-guardrail-findings-design.md Documents the problem, goals, event schema, and test strategy for anti-drift parity.

…O path

The ETW DTO logging path (A365EtwLogger.LogApplyGuardrail -> ApplyGuardrailDataBuilder -> BaseData -> ExportFormatter.FormatLogData) previously dropped guardrail findings entirely, so they were invisible to downstream consumers despite being emitted on the Activity span path. Add an optional findings parameter that flows through to a generic Events carrier on BaseData and is emitted by FormatLogData. The event JSON is pinned to match the Activity path's finding events exactly (same event name, attribute keys, value types), enforced by a cross-path consistency test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nikhilNava nikhilNava force-pushed the nikhilc/etw-guardrail-findings-span-events branch from ec9814d to 57bdd81 Compare June 26, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants