feat(difc): structured logging for every filtered item#2066
Conversation
Add per-item structured logging when DIFC filtering removes objects from tool responses. Each filtered item is logged as a JSON entry containing: - server_id, tool_name, description (guard label) - secrecy_tags, integrity_tags (DIFC labels) - reason (human-readable denial reason from evaluator) - author_association, author_login, html_url, number, sha (GitHub metadata) Implementation: - FilteredItemDetail struct pairs each filtered LabeledItem with its denial reason from the evaluator - FilterCollection now captures EvaluationResult.Reason per denied item - New difc_log.go in server package extracts metadata from raw JSON data and logs structured JSON entries via the file logger - Entries logged at INFO level with [DIFC-FILTERED] prefix for grep This enables post-processing reports that show exactly what was filtered, why, and provide enough context to link back to the GitHub object. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@pelikhan will mint a new gateway when this merges |
There was a problem hiding this comment.
Pull request overview
Adds structured, per-item audit logging when DIFC filtering removes objects from tool responses, enabling post-processing/reporting on what was denied and why.
Changes:
- Introduces
difc.FilteredItemDetailto retain denial reasons alongside filteredLabeledItems. - Extends
Evaluator.FilterCollectionto captureEvaluationResult.Reasonper denied item. - Adds
internal/server/difc_log.goand integrates per-item structured logging from Phase 5 filtering inUnifiedServer.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/server/unified.go | Calls new per-item logging helper when Phase 5 filtering removes items. |
| internal/server/difc_log.go | New structured log entry builder + per-item logging implementation. |
| internal/difc/resource.go | Adds FilteredItemDetail and updates filtered collection type to store details. |
| internal/difc/evaluator.go | Populates FilteredItemDetail.Reason for denied items during filtering. |
| internal/difc/resource_test.go | Updates unit tests to reflect the new filtered item detail structure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| func logFilteredItems(serverID, toolName string, filtered *difc.FilteredCollectionLabeledData) { | ||
| for _, detail := range filtered.Filtered { | ||
| entry := buildFilteredItemLogEntry(serverID, toolName, detail) | ||
|
|
||
| entryJSON, err := json.Marshal(entry) | ||
| if err != nil { | ||
| logger.LogWarnWithServer(serverID, "difc", | ||
| "[DIFC-FILTERED] %s | %s | description=%s | reason=%s (json marshal failed: %v)", | ||
| serverID, toolName, entry.Description, entry.Reason, err) | ||
| continue | ||
| } | ||
|
|
||
| logger.LogInfoWithServer(serverID, "difc", | ||
| "[DIFC-FILTERED] %s", string(entryJSON)) | ||
| } |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Added internal/server/difc_log_test.go (commit 79fe8e7) with 8 tests covering the structured logging path:
TestLogFilteredItems_EmitsValidJSONWithExpectedFields— initializes realFileLogger+ServerFileLogger, callslogFilteredItems, then reads both the per-server log andmcp-gateway.log, parses the JSON from the[DIFC-FILTERED]lines, and asserts every expected field (server_id,tool_name,description,reason,secrecy_tags,integrity_tags,author_login,author_association,html_url,number)TestLogFilteredItems_MultipleItems— verifies one log line per filtered itemTestLogFilteredItems_EmptyFiltered— verifies no output when the filtered list is emptyTestBuildFilteredItemLogEntry_WithNilLabels— nil labels don't panic; raw data fields are still extractedTestBuildFilteredItemLogEntry_ExtractAuthorLogin_UserObject/AuthorObject— exercises bothuser.loginandauthor.loginpathsTestBuildFilteredItemLogEntry_ExtractNumberField_JsonNumber— exercises thejson.Numberbranch inextractNumberFieldTestBuildFilteredItemLogEntry_NonMapData— non-map item data doesn't panic
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ng format (dash) Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… format (#2067) `TestPlaywrightMCPServer/ToolsRegistered` was failing because the test contained stale hard-coded expectations that no longer match the current `mcp/playwright` image behavior. ## Changes - **Tool count**: Updated expected count from `22` → `21` to match the tools the current playwright image actually registers - **Tool name separator**: Updated from triple-underscore (`playwright___browser_close`) → dash (`playwright-browser_close`) to match the current gateway naming convention ```go // Before if !strings.Contains(stderrStr, "Registered 22 tools from playwright") && !strings.Contains(stderrStr, "Registered tool: playwright___browser_close") { // After if !strings.Contains(stderrStr, "Registered 21 tools from playwright") && !strings.Contains(stderrStr, "Registered tool: playwright-browser_close") { ``` <!-- START COPILOT CODING AGENT SUFFIX --> <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > Fix the failing GitHub Actions workflow integration-test > Analyze the workflow logs, identify the root cause of the failure, and implement a fix. > Job ID: 67483962994 > Job URL: https://github.com/github/gh-aw-mcpg/actions/runs/23218086781/job/67483962994 </details> <!-- START COPILOT CODING AGENT TIPS --> --- 📱 Kick off Copilot coding agent tasks wherever you are with [GitHub Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android.
- TestLogFilteredItems_EmitsValidJSONWithExpectedFields: initializes real file/server loggers, calls logFilteredItems, then parses the emitted JSON from both per-server and unified logs, verifying the [DIFC-FILTERED] prefix and all expected fields (server_id, tool_name, description, reason, secrecy_tags, integrity_tags, author_login, author_association, html_url, number) - TestLogFilteredItems_MultipleItems: verifies one log line per filtered item - TestLogFilteredItems_EmptyFiltered: verifies no output when filtered list is empty - TestBuildFilteredItemLogEntry_WithNilLabels: nil labels don't panic, metadata still extracted - TestBuildFilteredItemLogEntry_ExtractAuthorLogin_UserObject: exercises user.login path - TestBuildFilteredItemLogEntry_ExtractAuthorLogin_AuthorObject: exercises author.login path - TestBuildFilteredItemLogEntry_ExtractNumberField_JsonNumber: exercises json.Number path - TestBuildFilteredItemLogEntry_NonMapData: non-map item data doesn't panic Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
- [x] Identify lint failure: `internal/server/difc_log_test.go` was not gofmt-formatted - [x] Run `gofmt -w` to fix formatting - [x] Verify `make lint` passes <!-- START COPILOT CODING AGENT SUFFIX --> <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > Fix the failing GitHub Actions workflow lint > Analyze the workflow logs, identify the root cause of the failure, and implement a fix. > Job ID: 67489714827 > Job URL: https://github.com/github/gh-aw-mcpg/actions/runs/23218548623/job/67489714827 </details> <!-- START COPILOT CODING AGENT TIPS --> --- 🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. [Learn more about Advanced Security.](https://gh.io/cca-advanced-security)
Summary
Adds per-item structured logging when DIFC filtering removes objects from tool responses. This enables post-processing reports that show exactly what was filtered, why, and provide enough context to link back to the GitHub object.
Changes
FilteredItemDetailstruct (resource.go): Pairs each filteredLabeledItemwith the denial reason from the evaluatorFilterCollection(evaluator.go): Now capturesEvaluationResult.Reasonper denied itemdifc_log.go(new):logFilteredItems()extracts metadata from raw JSON data and emits structured JSON log entries via the file loggerunified.go): CallslogFilteredItems()whenever items are filtereddifc_log_test.go(new): Unit tests verifying the structured log output format, JSON validity, expected fields, and per-server/unified log routingLog Entry Format
Each filtered item produces a JSON entry logged at
[DIFC-FILTERED]prefix:{ "server_id": "github", "tool_name": "list_issues", "description": "issue:repo#42", "reason": "integrity too low", "secrecy_tags": ["private:org/repo"], "integrity_tags": ["none"], "author_association": "NONE", "author_login": "user", "html_url": "https://github.com/...", "number": "42" }Entries are written to both per-server log files and the unified
mcp-gateway.logfor easy grepping.Testing
FilteredItemDetailtype changedifc_log_test.goassert thatlogFilteredItemsemits valid JSON with the[DIFC-FILTERED]prefix, writes to both per-server and unified logs, and handles edge cases (nil labels, non-map data, empty filtered list, multiple items)make agent-finishedpasses (format, build, lint, unit + integration tests)📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.