diff --git a/.github/workflows/release-trigger.yml b/.github/workflows/release-trigger.yml index 060b3068fdf95..ab03be0ae6955 100644 --- a/.github/workflows/release-trigger.yml +++ b/.github/workflows/release-trigger.yml @@ -62,6 +62,30 @@ jobs: echo "is-stable-release=false" >> "$GITHUB_OUTPUT" fi + # TODO: this notification currently runs before the approval gate, so it + # announces a *pending* release. Once the `release` environment gate is removed + # the release starts immediately — switch `needs` back to the release step and + # update the wording in notify_slack.py to "release starting". + notify: + name: Notify pending release + needs: context + runs-on: ubuntu-latest + steps: + - name: Checkout notification script + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + sparse-checkout: .github/workflows/scripts + - name: Notify Slack of pending release + env: + SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} + SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID_AGENT_INTEGRATIONS_RELEASE }} + SOURCE_REPO: integrations-core + REF: ${{ github.event_name == 'workflow_dispatch' && inputs.source-repo-ref || github.sha }} + PACKAGES: ${{ inputs.packages || '' }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: python3 .github/workflows/scripts/notify_slack.py + approve: name: Await release approval needs: context diff --git a/.github/workflows/scripts/notify_slack.py b/.github/workflows/scripts/notify_slack.py new file mode 100644 index 0000000000000..9dfd633d8b394 --- /dev/null +++ b/.github/workflows/scripts/notify_slack.py @@ -0,0 +1,64 @@ +"""Post a 'wheel release starting' Slack notification via chat.postMessage. + +Env: SLACK_API_TOKEN, SLACK_CHANNEL_ID (both required or no-op), SOURCE_REPO, +REF, PACKAGES, RUN_URL. Slack/network errors warn and never fail the job. +""" +import json +import os +import urllib.error +import urllib.request + +SLACK_URL = "https://slack.com/api/chat.postMessage" + + +def build_text(source_repo: str, ref: str, packages: str, run_url: str) -> str: + """Return the release notification Slack message body.""" + # TODO: this fires before the approval gate, so the release is pending. Once + # the `release` environment gate is removed, reword to "Wheel release starting" + # with a "View release run" link, since the release will start immediately. + return ( + f":hourglass_flowing_sand: *Wheel release pending approval* — `{source_repo}`\n" + f"• ref: `{ref[:12] or '—'}`\n" + f"• packages: {packages.strip() or 'auto-detect from tags at HEAD'}\n" + f"• <{run_url}|Review & approve →>" + ) + + +def post(token: str, channel: str, text: str) -> None: + """Post *text* to *channel*, warning (not failing) on error.""" + data = json.dumps({"channel": channel, "text": text, "unfurl_links": False}).encode() + request = urllib.request.Request( + SLACK_URL, + data=data, + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json; charset=utf-8"}, + ) + try: + with urllib.request.urlopen(request, timeout=15) as response: + body = json.loads(response.read()) + except (urllib.error.URLError, TimeoutError, ValueError) as e: + print(f"::warning::Slack request failed: {e}") + return + if not body.get("ok"): + print(f"::warning::Slack notification failed: {body.get('error', 'unknown error')}") + + +def main() -> None: + token = os.environ.get("SLACK_API_TOKEN", "").strip() + channel = os.environ.get("SLACK_CHANNEL_ID", "").strip() + if not token or not channel: + print("Slack token or channel not configured; skipping notification.") + return + post( + token, + channel, + build_text( + os.environ.get("SOURCE_REPO", "integrations-core"), + os.environ.get("REF", ""), + os.environ.get("PACKAGES", ""), + os.environ.get("RUN_URL", ""), + ), + ) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/scripts/tests/test_notify_slack.py b/.github/workflows/scripts/tests/test_notify_slack.py new file mode 100644 index 0000000000000..59d8fa91664e7 --- /dev/null +++ b/.github/workflows/scripts/tests/test_notify_slack.py @@ -0,0 +1,41 @@ +"""Tests for notify_slack.""" +import urllib.error +from unittest.mock import patch + +import notify_slack + + +def test_build_text_lists_packages(): + text = notify_slack.build_text("integrations-core", "abcdef1234567890", '["postgres"]', "http://run") + assert "pending approval" in text + assert '["postgres"]' in text + assert "`abcdef123456`" in text + assert "http://run" in text + + +def test_build_text_auto_detect_and_dash_ref(): + text = notify_slack.build_text("marketplace", "", "", "http://run") + assert "auto-detect from tags at HEAD" in text + assert "ref: `—`" in text + + +def test_main_no_op_without_config(monkeypatch): + monkeypatch.delenv("SLACK_API_TOKEN", raising=False) + monkeypatch.setenv("SLACK_CHANNEL_ID", "C1") + with patch.object(notify_slack, "post") as post: + notify_slack.main() + post.assert_not_called() + + +def test_main_posts_when_configured(monkeypatch): + monkeypatch.setenv("SLACK_API_TOKEN", "xoxb") + monkeypatch.setenv("SLACK_CHANNEL_ID", "C1") + with patch.object(notify_slack, "post") as post: + notify_slack.main() + post.assert_called_once() + + +def test_post_warns_on_error(capsys): + with patch("urllib.request.urlopen", side_effect=urllib.error.URLError("boom")): + notify_slack.post("token", "C1", "hi") + assert "failed" in capsys.readouterr().out diff --git a/anthropic_compliance_logs/assets/dashboards/claude_compliance_logs_overview.json b/anthropic_compliance_logs/assets/dashboards/claude_compliance_logs_overview.json index e30c5db7db700..8bff245effb29 100644 --- a/anthropic_compliance_logs/assets/dashboards/claude_compliance_logs_overview.json +++ b/anthropic_compliance_logs/assets/dashboards/claude_compliance_logs_overview.json @@ -1 +1,2305 @@ -{"title":"Claude Compliance Logs","description":"","widgets":[{"id":7710000000000001,"definition":{"type":"note","content":"High-level compliance activity snapshot: total event volume, unique actors, and distinct source IPs. Use these KPIs to quickly spot anomalous spikes or drops in Claude compliance event traffic.","background_color":"orange","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":3491299028093288,"definition":{"title":"","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":3837700461261625,"definition":{"type":"image","url":"/static/images/logos/anthropic-compliance-logs_large.svg","sizing":"contain","has_background":false,"has_border":false,"vertical_align":"center","horizontal_align":"center"},"layout":{"x":0,"y":0,"width":5,"height":2}},{"id":6367919593180978,"definition":{"type":"note","content":"## Claude Compliance Logs\n\nMonitor security-relevant activity from the Anthropic Console: SSO and password sign-ins, admin API key lifecycle, organization membership changes, SSO/SAML configuration updates, and Claude chat and project access events.\n\nAll widgets scope to `source:claude-compliance-logs`. Use the template variables at the top of the dashboard to slice by organization, actor type, or event name.\n\n### Useful links\n- [Claude Compliance Logs tile](/integrations/anthropic-compliance-logs)\n- [Anthropic Console compliance API](https://support.claude.com/en/articles/13015708-access-the-compliance-api)\n- [Datadog Cloud SIEM](/security)","background_color":"white","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":2,"width":5,"height":4}}]},"layout":{"x":0,"y":1,"width":5,"height":7}},{"id":3979462846965888,"definition":{"title":"Overview","background_color":"vivid_orange","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":8528008201771328,"definition":{"title":"Total compliance events","title_size":"16","title_align":"left","time":{},"type":"query_value","requests":[{"formulas":[{"formula":"query1"}],"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","comparison":{"type":"absolute","duration":{"type":"previous_day"},"directionality":"neutral"}}],"autoscale":true,"precision":0,"timeseries_background":{"type":"bars"}},"layout":{"x":0,"y":0,"width":2,"height":2}},{"id":4289281758821028,"definition":{"title":"Unique actors","title_size":"16","title_align":"left","time":{},"type":"query_value","requests":[{"formulas":[{"formula":"query1"}],"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[],"compute":{"aggregation":"cardinality","metric":"@usr.email"},"storage":"hot"}],"response_format":"scalar","comparison":{"type":"absolute","duration":{"type":"previous_day"},"directionality":"neutral"}}],"autoscale":true,"precision":0,"timeseries_background":{"type":"bars"}},"layout":{"x":2,"y":0,"width":2,"height":2}},{"id":6094999018279701,"definition":{"title":"Unique source IPs","title_size":"16","title_align":"left","time":{},"type":"query_value","requests":[{"formulas":[{"formula":"query1"}],"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[],"compute":{"aggregation":"cardinality","metric":"@network.client.ip"},"storage":"hot"}],"response_format":"scalar","comparison":{"type":"absolute","duration":{"type":"previous_day"},"directionality":"neutral"}}],"autoscale":true,"precision":0,"timeseries_background":{"type":"bars"}},"layout":{"x":4,"y":0,"width":3,"height":2}},{"id":4105836164118366,"definition":{"title":"Events over time by event name","title_size":"16","title_align":"left","show_legend":true,"legend_layout":"auto","legend_columns":["avg","min","max","value","sum"],"type":"timeseries","requests":[{"formulas":[{"formula":"query1","alias":"events"}],"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"timeseries","style":{"palette":"dog_classic","line_type":"solid","line_width":"normal"},"display_type":"bars"}]},"layout":{"x":0,"y":2,"width":7,"height":4}}]},"layout":{"x":5,"y":1,"width":7,"height":7}},{"id":2061779778332461,"definition":{"title":"Event Breakdown","background_color":"vivid_purple","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":8277209312364770,"definition":{"type":"note","content":"Top event types and the kinds of actors generating them. A surge of `admin_api_key_actor` traffic, or unfamiliar event names, is worth investigating.","background_color":"purple","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":1780986178198448,"definition":{"title":"Top event types","title_size":"16","title_align":"left","type":"toplist","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[{"facet":"@evt.name","limit":15,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","formulas":[{"formula":"query1"}]}],"style":{"display":{"type":"stacked","legend":"automatic"}}},"layout":{"x":0,"y":1,"width":6,"height":4}},{"id":4482961882127943,"definition":{"title":"Events by actor type","title_size":"16","title_align":"left","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $event_name"},"indexes":["*"],"group_by":[{"facet":"@actor.type","limit":10,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","style":{"palette":"datadog16"},"formulas":[{"formula":"query1"}]}],"type":"sunburst","legend":{"type":"table"}},"layout":{"x":6,"y":1,"width":6,"height":4}}]},"layout":{"x":0,"y":8,"width":12,"height":6}},{"id":5669320577984303,"definition":{"title":"Identity & Access","background_color":"vivid_blue","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":7710000000000002,"definition":{"type":"note","content":"Who is generating compliance events and from where. Unusual actors or unexpected source IPs may indicate compromised credentials or unauthorized access attempts.","background_color":"blue","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":8840119324270831,"definition":{"title":"Top actors","title_size":"16","title_align":"left","type":"toplist","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs @usr.email:* $org $actor_type $event_name"},"indexes":["*"],"group_by":[{"facet":"@usr.email","limit":15,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","formulas":[{"formula":"query1"}]}],"style":{"display":{"type":"stacked","legend":"automatic"}}},"layout":{"x":0,"y":1,"width":6,"height":4}},{"id":7914509895789186,"definition":{"title":"Top source IPs","title_size":"16","title_align":"left","type":"toplist","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs @network.client.ip:* $org $actor_type $event_name"},"indexes":["*"],"group_by":[{"facet":"@network.client.ip","limit":15,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","formulas":[{"formula":"query1"}]}],"style":{"display":{"type":"stacked","legend":"automatic"}}},"layout":{"x":6,"y":1,"width":6,"height":4}}]},"layout":{"x":0,"y":14,"width":12,"height":6}},{"id":8050846558781486,"definition":{"title":"Security-Sensitive Activity","background_color":"vivid_orange","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":2924337077016093,"definition":{"type":"note","content":"API key lifecycle changes and per-organization activity volume. Unexpected key creation or deletion bursts, or activity from unknown organizations, should be reviewed against your change-management records.","background_color":"orange","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":5247376892826816,"definition":{"title":"API key lifecycle events","title_size":"16","title_align":"left","type":"toplist","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated) $org $actor_type"},"indexes":["*"],"group_by":[{"facet":"@evt.name","limit":15,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","formulas":[{"formula":"query1"}]}],"style":{"display":{"type":"stacked","legend":"automatic"}}},"layout":{"x":0,"y":1,"width":6,"height":4}},{"id":5213325474829008,"definition":{"title":"Organization activity split","title_size":"16","title_align":"left","type":"toplist","requests":[{"queries":[{"name":"query1","data_source":"logs","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"indexes":["*"],"group_by":[{"facet":"@organization_id","limit":15,"sort":{"aggregation":"count","order":"desc"},"should_exclude_missing":true}],"compute":{"aggregation":"count"},"storage":"hot"}],"response_format":"scalar","formulas":[{"formula":"query1"}]}],"style":{"display":{"type":"stacked","legend":"automatic"}}},"layout":{"x":6,"y":1,"width":6,"height":4}}]},"layout":{"x":0,"y":20,"width":12,"height":6}},{"id":280998184994345,"definition":{"title":"Admin & SSO Activity Streams","background_color":"vivid_pink","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":4657396894266658,"definition":{"type":"note","content":"High-impact security events: actions taken by admin API keys (tracked by key ID and event type), user-initiated API key lifecycle changes, and SSO/SAML configuration updates. Each entry should map to a known operator or change ticket.","background_color":"pink","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":5000000000000001,"definition":{"title":"Admin API key usage over time by action","show_legend":false,"legend_layout":"auto","legend_columns":["value","avg","sum"],"type":"timeseries","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"formulas":[{"formula":"query1"}],"response_format":"timeseries","style":{"palette":"semantic"},"display_type":"bars"}]},"layout":{"x":0,"y":1,"width":8,"height":3}},{"id":5000000000000002,"definition":{"title":"Admin API key actions breakdown","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"warm"}},"layout":{"x":8,"y":1,"width":4,"height":3}},{"id":5000000000000003,"definition":{"title":"Most active admin API key IDs","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@actor.admin_api_key_id","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"purple"}},"layout":{"x":0,"y":4,"width":4,"height":3}},{"id":5000000000000004,"definition":{"title":"API key lifecycle events by user","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated) $org"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@usr.email","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"orange"}},"layout":{"x":4,"y":4,"width":4,"height":3}},{"id":5000000000000005,"definition":{"title":"Source IPs for API key lifecycle (users)","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @actor.type:user_actor @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated OR *api_key_rotated) @network.client.ip:* $org $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@network.client.ip","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"green"}},"layout":{"x":8,"y":4,"width":4,"height":3}},{"id":6020216729703859,"definition":{"title":"Admin actions","title_size":"16","title_align":"left","requests":[{"response_format":"event_list","query":{"data_source":"logs_stream","query_string":"source:claude-compliance-logs (@actor.type:admin_api_key_actor OR @evt.name:admin_*) $org $event_name","indexes":["*"],"storage":"hot","sort":{"order":"desc","column":"timestamp"}},"columns":[{"field":"status_line","width":"auto"},{"field":"timestamp","width":"auto"},{"field":"host","width":"auto"},{"field":"@evt.name","width":"auto"},{"field":"@usr.email","width":"auto"},{"field":"@network.client.ip","width":"auto"},{"field":"content","width":"auto"}]}],"type":"list_stream"},"layout":{"x":0,"y":7,"width":12,"height":5}}]},"layout":{"x":0,"y":26,"width":12,"height":13}},{"id":6134135199469874,"definition":{"title":"Recent Activity","background_color":"vivid_blue","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":7710000000000003,"definition":{"type":"note","content":"A broad view of recent compliance activity broken down by actor type, source IP, browser, user, authentication method, and event type. Use this section for exploratory analysis and drill-down.","background_color":"blue","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":3000000000000004,"definition":{"title":"Events by actor type","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@actor.type","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"purple"}},"layout":{"x":0,"y":1,"width":4,"height":3}},{"id":4000000000000001,"definition":{"title":"Top source IPs","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @network.client.ip:* $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@network.client.ip","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"green"}},"layout":{"x":4,"y":1,"width":4,"height":3}},{"id":4000000000000002,"definition":{"title":"Top browsers","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@http.useragent_details.browser.family","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"blue"}},"layout":{"x":8,"y":1,"width":4,"height":3}},{"id":3000000000000001,"definition":{"title":"All compliance events over time by event name","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","style":{"palette":"datadog16"},"formulas":[{"formula":"query1"}],"sort":{"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"type":"sunburst","legend":{"type":"automatic"}},"layout":{"x":0,"y":4,"width":12,"height":4}},{"id":3000000000000003,"definition":{"title":"Most active users","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@usr.email","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"orange"}},"layout":{"x":0,"y":8,"width":4,"height":3}},{"id":3000000000000005,"definition":{"title":"Events by auth method","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@auth_method","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"warm"}},"layout":{"x":4,"y":8,"width":4,"height":3}},{"id":3000000000000002,"definition":{"title":"Top event types","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs $org $actor_type $event_name"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":15,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":15,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"display":{"type":"stacked","legend":"automatic"},"scaling":"relative","palette":"cool"}},"layout":{"x":8,"y":8,"width":4,"height":3}},{"id":3000000000000007,"definition":{"title":"Recent compliance events (raw log stream)","title_size":"16","title_align":"left","requests":[{"response_format":"event_list","query":{"data_source":"logs_stream","query_string":"source:claude-compliance-logs $org $actor_type $event_name","indexes":["*"],"storage":"hot","sort":{"order":"desc","column":"timestamp"}},"columns":[{"field":"status_line","width":"auto"},{"field":"timestamp","width":"auto"},{"field":"@evt.name","width":"auto"},{"field":"@actor.type","width":"auto"},{"field":"@usr.email","width":"auto"},{"field":"@auth_method","width":"auto"},{"field":"@network.client.geoip.country.name","width":"auto"},{"field":"@network.client.geoip.city.name","width":"auto"},{"field":"@http.useragent_details.browser.family","width":"auto"},{"field":"@network.client.ip","width":"auto"}]}],"type":"list_stream"},"layout":{"x":0,"y":11,"width":12,"height":5}}]},"layout":{"x":0,"y":78,"width":12,"height":17,"is_column_break":true}},{"id":1000000000000001,"definition":{"title":"SSO & SAML Activity","background_color":"vivid_green","show_title":true,"type":"group","layout_type":"ordered","widgets":[{"id":7710000000000004,"definition":{"type":"note","content":"SSO and SAML authentication events including sign-in patterns, geographic origins, and per-user activity. Unusual locations or unfamiliar user agents may signal account compromise.","background_color":"green","font_size":"14","text_align":"left","vertical_align":"top","show_tick":false,"tick_pos":"50%","tick_edge":"left","has_padding":true},"layout":{"x":0,"y":0,"width":12,"height":1}},{"id":2000000000000001,"definition":{"title":"SSO/SAML events over time by event name","show_legend":false,"legend_layout":"auto","legend_columns":["value","avg","sum"],"type":"timeseries","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"formulas":[{"formula":"query1"}],"response_format":"timeseries","style":{"palette":"semantic"},"display_type":"bars"}]},"layout":{"x":0,"y":1,"width":8,"height":3}},{"id":2000000000000002,"definition":{"title":"Top event types","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"palette":"datadog16"}},"layout":{"x":8,"y":1,"width":4,"height":3}},{"id":2000000000000003,"definition":{"title":"Top users by SSO/SAML activity","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@usr.email","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"palette":"datadog16"}},"layout":{"x":0,"y":4,"width":4,"height":3}},{"id":2000000000000004,"definition":{"title":"Top browsers","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@http.useragent_details.browser.family","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"palette":"datadog16"}},"layout":{"x":4,"y":4,"width":4,"height":3}},{"id":2000000000000005,"definition":{"title":"Events by actor type","type":"toplist","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@actor.type","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":10,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"palette":"datadog16"}},"layout":{"x":8,"y":4,"width":4,"height":3}},{"id":2000000000000006,"definition":{"title":"SSO/SAML sign-in locations","type":"geomap","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@network.client.geoip.country.iso_code","limit":50,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","formulas":[{"formula":"query1"}],"sort":{"count":50,"order_by":[{"type":"formula","index":0,"order":"desc"}]}}],"style":{"palette":"hostmap_blues","palette_flip":false},"view":{"focus":"WORLD"}},"layout":{"x":0,"y":7,"width":6,"height":4}},{"id":2000000000000007,"definition":{"title":"SSO/SAML activity by user, event, and country","type":"query_table","requests":[{"queries":[{"data_source":"logs","name":"query1","search":{"query":"source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type"},"compute":{"aggregation":"count"},"group_by":[{"facet":"@usr.email","limit":25,"sort":{"aggregation":"count","order":"desc"}},{"facet":"@evt.name","limit":10,"sort":{"aggregation":"count","order":"desc"}},{"facet":"@network.client.geoip.country.name","limit":10,"sort":{"aggregation":"count","order":"desc"}}],"indexes":["*"]}],"response_format":"scalar","sort":{"count":25,"order_by":[{"type":"formula","index":0,"order":"desc"}]},"formulas":[{"formula":"query1","cell_display_mode":"bar"}]}],"has_search_bar":"auto"},"layout":{"x":6,"y":7,"width":6,"height":4}}]},"layout":{"x":0,"y":95,"width":12,"height":12}}],"template_variables":[{"name":"org","prefix":"@organization_id","available_values":[],"default":"*"},{"name":"actor_type","prefix":"@actor.type","available_values":[],"default":"*"},{"name":"event_name","prefix":"@evt.name","available_values":[],"default":"*"}],"layout_type":"ordered","notify_list":[],"reflow_type":"fixed","tags":[]} \ No newline at end of file +{ + "title": "Claude Compliance Logs", + "description": "", + "widgets": [ + { + "id": 7710000000000001, + "definition": { + "type": "note", + "content": "High-level compliance activity snapshot: total event volume, unique actors, and distinct source IPs. Use these KPIs to quickly spot anomalous spikes or drops in Claude compliance event traffic.", + "background_color": "orange", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 3491299028093288, + "definition": { + "title": "", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 3837700461261625, + "definition": { + "type": "image", + "url": "/static/images/logos/anthropic-compliance-logs_large.svg", + "sizing": "contain", + "has_background": false, + "has_border": false, + "vertical_align": "center", + "horizontal_align": "center" + }, + "layout": { + "x": 0, + "y": 0, + "width": 5, + "height": 2 + } + }, + { + "id": 6367919593180978, + "definition": { + "type": "note", + "content": "## Claude Compliance Logs\n\nMonitor security-relevant activity from the Anthropic Console: SSO and password sign-ins, admin API key lifecycle, organization membership changes, SSO/SAML configuration updates, and Claude chat and project access events.\n\nAll widgets scope to `source:claude-compliance-logs`. Use the template variables at the top of the dashboard to slice by organization, actor type, or event name.\n\n### Useful links\n- [Claude Compliance Logs tile](/integrations/anthropic-compliance-logs)\n- [Anthropic Console compliance API](https://support.claude.com/en/articles/13015708-access-the-compliance-api)\n- [Datadog Cloud SIEM](/security)", + "background_color": "white", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 2, + "width": 5, + "height": 4 + } + } + ] + }, + "layout": { + "x": 0, + "y": 1, + "width": 5, + "height": 7 + } + }, + { + "id": 3979462846965888, + "definition": { + "title": "Overview", + "background_color": "vivid_orange", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 8528008201771328, + "definition": { + "title": "Total compliance events", + "title_size": "16", + "title_align": "left", + "time": {}, + "type": "query_value", + "requests": [ + { + "formulas": [ + { + "formula": "query1" + } + ], + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "comparison": { + "type": "absolute", + "duration": { + "type": "previous_day" + }, + "directionality": "neutral" + } + } + ], + "autoscale": true, + "precision": 0, + "timeseries_background": { + "type": "bars" + } + }, + "layout": { + "x": 0, + "y": 0, + "width": 2, + "height": 2 + } + }, + { + "id": 4289281758821028, + "definition": { + "title": "Unique actors", + "title_size": "16", + "title_align": "left", + "time": {}, + "type": "query_value", + "requests": [ + { + "formulas": [ + { + "formula": "query1" + } + ], + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [], + "compute": { + "aggregation": "cardinality", + "metric": "@usr.email" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "comparison": { + "type": "absolute", + "duration": { + "type": "previous_day" + }, + "directionality": "neutral" + } + } + ], + "autoscale": true, + "precision": 0, + "timeseries_background": { + "type": "bars" + } + }, + "layout": { + "x": 2, + "y": 0, + "width": 2, + "height": 2 + } + }, + { + "id": 6094999018279701, + "definition": { + "title": "Unique source IPs", + "title_size": "16", + "title_align": "left", + "time": {}, + "type": "query_value", + "requests": [ + { + "formulas": [ + { + "formula": "query1" + } + ], + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [], + "compute": { + "aggregation": "cardinality", + "metric": "@network.client.ip" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "comparison": { + "type": "absolute", + "duration": { + "type": "previous_day" + }, + "directionality": "neutral" + } + } + ], + "autoscale": true, + "precision": 0, + "timeseries_background": { + "type": "bars" + } + }, + "layout": { + "x": 4, + "y": 0, + "width": 3, + "height": 2 + } + }, + { + "id": 4105836164118366, + "definition": { + "title": "Events over time by event name", + "title_size": "16", + "title_align": "left", + "show_legend": true, + "legend_layout": "auto", + "legend_columns": [ + "avg", + "min", + "max", + "value", + "sum" + ], + "type": "timeseries", + "requests": [ + { + "formulas": [ + { + "formula": "query1", + "alias": "events" + } + ], + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "timeseries", + "style": { + "palette": "dog_classic", + "line_type": "solid", + "line_width": "normal" + }, + "display_type": "bars" + } + ] + }, + "layout": { + "x": 0, + "y": 2, + "width": 7, + "height": 4 + } + } + ] + }, + "layout": { + "x": 5, + "y": 1, + "width": 7, + "height": 7 + } + }, + { + "id": 2061779778332461, + "definition": { + "title": "Event Breakdown", + "background_color": "vivid_purple", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 8277209312364770, + "definition": { + "type": "note", + "content": "Top event types and the kinds of actors generating them. A surge of `admin_api_key_actor` traffic, or unfamiliar event names, is worth investigating.", + "background_color": "purple", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 1780986178198448, + "definition": { + "title": "Top event types", + "title_size": "16", + "title_align": "left", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@evt.name", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + } + } + }, + "layout": { + "x": 0, + "y": 1, + "width": 6, + "height": 4 + } + }, + { + "id": 4482961882127943, + "definition": { + "title": "Events by actor type", + "title_size": "16", + "title_align": "left", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@actor.type", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "style": { + "palette": "datadog16" + }, + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "type": "sunburst", + "legend": { + "type": "table" + } + }, + "layout": { + "x": 6, + "y": 1, + "width": 6, + "height": 4 + } + } + ] + }, + "layout": { + "x": 0, + "y": 8, + "width": 12, + "height": 6 + } + }, + { + "id": 5669320577984303, + "definition": { + "title": "Identity & Access", + "background_color": "vivid_blue", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 7710000000000002, + "definition": { + "type": "note", + "content": "Who is generating compliance events and from where. Unusual actors or unexpected source IPs may indicate compromised credentials or unauthorized access attempts.", + "background_color": "blue", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 8840119324270831, + "definition": { + "title": "Top actors", + "title_size": "16", + "title_align": "left", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs @usr.email:* $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@usr.email", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + } + } + }, + "layout": { + "x": 0, + "y": 1, + "width": 6, + "height": 4 + } + }, + { + "id": 7914509895789186, + "definition": { + "title": "Top source IPs", + "title_size": "16", + "title_align": "left", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs @network.client.ip:* $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@network.client.ip", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + } + } + }, + "layout": { + "x": 6, + "y": 1, + "width": 6, + "height": 4 + } + } + ] + }, + "layout": { + "x": 0, + "y": 14, + "width": 12, + "height": 6 + } + }, + { + "id": 8050846558781486, + "definition": { + "title": "Security-Sensitive Activity", + "background_color": "vivid_orange", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 2924337077016093, + "definition": { + "type": "note", + "content": "API key lifecycle changes and per-organization activity volume. Unexpected key creation or deletion bursts, or activity from unknown organizations, should be reviewed against your change-management records.", + "background_color": "orange", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 5247376892826816, + "definition": { + "title": "API key lifecycle events", + "title_size": "16", + "title_align": "left", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated) $org $actor_type" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@evt.name", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + } + } + }, + "layout": { + "x": 0, + "y": 1, + "width": 6, + "height": 4 + } + }, + { + "id": 5213325474829008, + "definition": { + "title": "Organization activity split", + "title_size": "16", + "title_align": "left", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "name": "query1", + "data_source": "logs", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "indexes": [ + "*" + ], + "group_by": [ + { + "facet": "@organization_id", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + }, + "should_exclude_missing": true + } + ], + "compute": { + "aggregation": "count" + }, + "storage": "hot" + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ] + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + } + } + }, + "layout": { + "x": 6, + "y": 1, + "width": 6, + "height": 4 + } + } + ] + }, + "layout": { + "x": 0, + "y": 20, + "width": 12, + "height": 6 + } + }, + { + "id": 280998184994345, + "definition": { + "title": "Admin & SSO Activity Streams", + "background_color": "vivid_pink", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 4657396894266658, + "definition": { + "type": "note", + "content": "High-impact security events: actions taken by admin API keys (tracked by key ID and event type), user-initiated API key lifecycle changes, and SSO/SAML configuration updates. Each entry should map to a known operator or change ticket.", + "background_color": "pink", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 5000000000000001, + "definition": { + "title": "Admin API key usage over time by action", + "show_legend": false, + "legend_layout": "auto", + "legend_columns": [ + "value", + "avg", + "sum" + ], + "type": "timeseries", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "formulas": [ + { + "formula": "query1" + } + ], + "response_format": "timeseries", + "style": { + "palette": "semantic" + }, + "display_type": "bars" + } + ] + }, + "layout": { + "x": 0, + "y": 1, + "width": 8, + "height": 3 + } + }, + { + "id": 5000000000000002, + "definition": { + "title": "Admin API key actions breakdown", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "warm" + } + }, + "layout": { + "x": 8, + "y": 1, + "width": 4, + "height": 3 + } + }, + { + "id": 5000000000000003, + "definition": { + "title": "Most active admin API key IDs", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @actor.type:admin_api_key_actor $org $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@actor.admin_api_key_id", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "purple" + } + }, + "layout": { + "x": 0, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 5000000000000004, + "definition": { + "title": "API key lifecycle events by user", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated) $org" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@usr.email", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "orange" + } + }, + "layout": { + "x": 4, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 5000000000000005, + "definition": { + "title": "Source IPs for API key lifecycle (users)", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @actor.type:user_actor @evt.name:(*api_key_created OR *api_key_deleted OR *api_key_updated OR *api_key_rotated) @network.client.ip:* $org $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@network.client.ip", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "green" + } + }, + "layout": { + "x": 8, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 6020216729703859, + "definition": { + "title": "Admin actions", + "title_size": "16", + "title_align": "left", + "requests": [ + { + "response_format": "event_list", + "query": { + "data_source": "logs_stream", + "query_string": "source:claude-compliance-logs (@actor.type:admin_api_key_actor OR @evt.name:admin_*) $org $event_name", + "indexes": [ + "*" + ], + "storage": "hot", + "sort": { + "order": "desc", + "column": "timestamp" + } + }, + "columns": [ + { + "field": "status_line", + "width": "auto" + }, + { + "field": "timestamp", + "width": "auto" + }, + { + "field": "host", + "width": "auto" + }, + { + "field": "@evt.name", + "width": "auto" + }, + { + "field": "@usr.email", + "width": "auto" + }, + { + "field": "@network.client.ip", + "width": "auto" + }, + { + "field": "content", + "width": "auto" + } + ] + } + ], + "type": "list_stream" + }, + "layout": { + "x": 0, + "y": 7, + "width": 12, + "height": 5 + } + } + ] + }, + "layout": { + "x": 0, + "y": 26, + "width": 12, + "height": 13 + } + }, + { + "id": 6134135199469874, + "definition": { + "title": "Recent Activity", + "background_color": "vivid_blue", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 7710000000000003, + "definition": { + "type": "note", + "content": "A broad view of recent compliance activity broken down by actor type, source IP, browser, user, authentication method, and event type. Use this section for exploratory analysis and drill-down.", + "background_color": "blue", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 3000000000000004, + "definition": { + "title": "Events by actor type", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@actor.type", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "purple" + } + }, + "layout": { + "x": 0, + "y": 1, + "width": 4, + "height": 3 + } + }, + { + "id": 4000000000000001, + "definition": { + "title": "Top source IPs", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @network.client.ip:* $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@network.client.ip", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "green" + } + }, + "layout": { + "x": 4, + "y": 1, + "width": 4, + "height": 3 + } + }, + { + "id": 4000000000000002, + "definition": { + "title": "Top browsers", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@http.useragent_details.browser.family", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "blue" + } + }, + "layout": { + "x": 8, + "y": 1, + "width": 4, + "height": 3 + } + }, + { + "id": 3000000000000001, + "definition": { + "title": "All compliance events over time by event name", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "style": { + "palette": "datadog16" + }, + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "type": "sunburst", + "legend": { + "type": "automatic" + } + }, + "layout": { + "x": 0, + "y": 4, + "width": 12, + "height": 4 + } + }, + { + "id": 3000000000000003, + "definition": { + "title": "Most active users", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@usr.email", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "orange" + } + }, + "layout": { + "x": 0, + "y": 8, + "width": 4, + "height": 3 + } + }, + { + "id": 3000000000000005, + "definition": { + "title": "Events by auth method", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@auth_method", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "warm" + } + }, + "layout": { + "x": 4, + "y": 8, + "width": 4, + "height": 3 + } + }, + { + "id": 3000000000000002, + "definition": { + "title": "Top event types", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs $org $actor_type $event_name" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 15, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 15, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "display": { + "type": "stacked", + "legend": "automatic" + }, + "scaling": "relative", + "palette": "cool" + } + }, + "layout": { + "x": 8, + "y": 8, + "width": 4, + "height": 3 + } + }, + { + "id": 3000000000000007, + "definition": { + "title": "Recent compliance events (raw log stream)", + "title_size": "16", + "title_align": "left", + "requests": [ + { + "response_format": "event_list", + "query": { + "data_source": "logs_stream", + "query_string": "source:claude-compliance-logs $org $actor_type $event_name", + "indexes": [ + "*" + ], + "storage": "hot", + "sort": { + "order": "desc", + "column": "timestamp" + } + }, + "columns": [ + { + "field": "status_line", + "width": "auto" + }, + { + "field": "timestamp", + "width": "auto" + }, + { + "field": "@evt.name", + "width": "auto" + }, + { + "field": "@actor.type", + "width": "auto" + }, + { + "field": "@usr.email", + "width": "auto" + }, + { + "field": "@auth_method", + "width": "auto" + }, + { + "field": "@network.client.geoip.country.name", + "width": "auto" + }, + { + "field": "@network.client.geoip.city.name", + "width": "auto" + }, + { + "field": "@http.useragent_details.browser.family", + "width": "auto" + }, + { + "field": "@network.client.ip", + "width": "auto" + } + ] + } + ], + "type": "list_stream" + }, + "layout": { + "x": 0, + "y": 11, + "width": 12, + "height": 5 + } + } + ] + }, + "layout": { + "x": 0, + "y": 78, + "width": 12, + "height": 17, + "is_column_break": true + } + }, + { + "id": 1000000000000001, + "definition": { + "title": "SSO & SAML Activity", + "background_color": "vivid_green", + "show_title": true, + "type": "group", + "layout_type": "ordered", + "widgets": [ + { + "id": 7710000000000004, + "definition": { + "type": "note", + "content": "SSO and SAML authentication events including sign-in patterns, geographic origins, and per-user activity. Unusual locations or unfamiliar user agents may signal account compromise.", + "background_color": "green", + "font_size": "14", + "text_align": "left", + "vertical_align": "top", + "show_tick": false, + "tick_pos": "50%", + "tick_edge": "left", + "has_padding": true + }, + "layout": { + "x": 0, + "y": 0, + "width": 12, + "height": 1 + } + }, + { + "id": 2000000000000001, + "definition": { + "title": "SSO/SAML events over time by event name", + "show_legend": false, + "legend_layout": "auto", + "legend_columns": [ + "value", + "avg", + "sum" + ], + "type": "timeseries", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "formulas": [ + { + "formula": "query1" + } + ], + "response_format": "timeseries", + "style": { + "palette": "semantic" + }, + "display_type": "bars" + } + ] + }, + "layout": { + "x": 0, + "y": 1, + "width": 8, + "height": 3 + } + }, + { + "id": 2000000000000002, + "definition": { + "title": "Top event types", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "palette": "datadog16" + } + }, + "layout": { + "x": 8, + "y": 1, + "width": 4, + "height": 3 + } + }, + { + "id": 2000000000000003, + "definition": { + "title": "Top users by SSO/SAML activity", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@usr.email", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "palette": "datadog16" + } + }, + "layout": { + "x": 0, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 2000000000000004, + "definition": { + "title": "Top browsers", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@http.useragent_details.browser.family", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "palette": "datadog16" + } + }, + "layout": { + "x": 4, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 2000000000000005, + "definition": { + "title": "Events by actor type", + "type": "toplist", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@actor.type", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 10, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "palette": "datadog16" + } + }, + "layout": { + "x": 8, + "y": 4, + "width": 4, + "height": 3 + } + }, + { + "id": 2000000000000006, + "definition": { + "title": "SSO/SAML sign-in locations", + "type": "geomap", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@network.client.geoip.country.iso_code", + "limit": 50, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "formulas": [ + { + "formula": "query1" + } + ], + "sort": { + "count": 50, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + } + } + ], + "style": { + "palette": "hostmap_blues", + "palette_flip": false + }, + "view": { + "focus": "WORLD" + } + }, + "layout": { + "x": 0, + "y": 7, + "width": 6, + "height": 4 + } + }, + { + "id": 2000000000000007, + "definition": { + "title": "SSO/SAML activity by user, event, and country", + "type": "query_table", + "requests": [ + { + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": { + "query": "source:claude-compliance-logs @evt.name:(*sso_* OR *saml_*) $org $actor_type" + }, + "compute": { + "aggregation": "count" + }, + "group_by": [ + { + "facet": "@usr.email", + "limit": 25, + "sort": { + "aggregation": "count", + "order": "desc" + } + }, + { + "facet": "@evt.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + }, + { + "facet": "@network.client.geoip.country.name", + "limit": 10, + "sort": { + "aggregation": "count", + "order": "desc" + } + } + ], + "indexes": [ + "*" + ] + } + ], + "response_format": "scalar", + "sort": { + "count": 25, + "order_by": [ + { + "type": "formula", + "index": 0, + "order": "desc" + } + ] + }, + "formulas": [ + { + "formula": "query1", + "cell_display_mode": "bar" + } + ] + } + ], + "has_search_bar": "auto" + }, + "layout": { + "x": 6, + "y": 7, + "width": 6, + "height": 4 + } + } + ] + }, + "layout": { + "x": 0, + "y": 95, + "width": 12, + "height": 12 + } + } + ], + "template_variables": [ + { + "name": "org", + "prefix": "@organization_id", + "available_values": [], + "default": "*" + }, + { + "name": "actor_type", + "prefix": "@actor.type", + "available_values": [], + "default": "*" + }, + { + "name": "event_name", + "prefix": "@evt.name", + "available_values": [], + "default": "*" + } + ], + "layout_type": "ordered", + "notify_list": [], + "reflow_type": "fixed", + "tags": [] +} diff --git a/gpu/metadata.csv b/gpu/metadata.csv index ba099a8a41b83..aa7a20f1038c3 100644 --- a/gpu/metadata.csv +++ b/gpu/metadata.csv @@ -43,6 +43,11 @@ gpu.memory.free,gauge,16,byte,,Unallocated device memory (in bytes).,0,gpu,memor gpu.memory.limit,gauge,16,byte,,"Total device memory (framebuffer). This is always the device-level memory limit; the `pid` and `container_id` tags are present to enable per-process and per-container utilization formulas, but the value itself does not change.",0,gpu,memory.limit,, gpu.memory.reserved,gauge,16,byte,,Device memory (in bytes) reserved for system use (driver or firmware).,0,gpu,memory.reserved,, gpu.memory.temperature,gauge,16,degree celsius,,Temperature of the memory chip,0,gpu,memory.temperature,, +gpu.nccl.collective.algo_bandwidth_gbps,gauge,16,gigabyte,second,Algorithmic bandwidth of a collective operation per rank,0,gpu,nccl.collective.algo_bandwidth_gbps,, +gpu.nccl.collective.bus_bandwidth_gbps,gauge,16,gigabyte,second,Bus bandwidth of a collective operation per rank,0,gpu,nccl.collective.bus_bandwidth_gbps,, +gpu.nccl.collective.exec_time_us,gauge,16,microsecond,,Execution time of a collective operation per rank,0,gpu,nccl.collective.exec_time_us,, +gpu.nccl.collective.msg_size_bytes,gauge,16,byte,,Message size of a collective operation per rank,0,gpu,nccl.collective.msg_size_bytes,, +gpu.nccl.rank.seconds_since_last_event,gauge,16,second,,Seconds since the last NCCL event was received for a rank. Used for hang detection.,0,gpu,nccl.rank.seconds_since_last_event,, gpu.nvlink.ber.effective,gauge,16,,,NVLink effective error counter total for all links (errors not corrected by FEC/recovery mechanisms).,0,gpu,nvlink.ber.effective,, gpu.nvlink.ber.symbol,gauge,16,,,Symbol bit error rate for all NVLINK links,0,gpu,nvlink.ber.symbol,, gpu.nvlink.count.active,gauge,16,,,Number of active nvlinks for the device,0,gpu,nvlink.count.active,, diff --git a/postgres/changelog.d/24265.fixed b/postgres/changelog.d/24265.fixed new file mode 100644 index 0000000000000..f8c8393c89ec4 --- /dev/null +++ b/postgres/changelog.d/24265.fixed @@ -0,0 +1 @@ +Migrate the Postgres check's tag handling to the shared `TagManager`. diff --git a/postgres/datadog_checks/postgres/postgres.py b/postgres/datadog_checks/postgres/postgres.py index ce16d65f97d7e..2a937c710e35e 100644 --- a/postgres/datadog_checks/postgres/postgres.py +++ b/postgres/datadog_checks/postgres/postgres.py @@ -140,7 +140,7 @@ def __init__(self, name, init_config, instances): for warning in validation_result.warnings: self.log.warning(warning) - self._tags = list(self._config.tags) + self.tag_manager.set_tags_from_list(self._config.tags, replace=True) self.add_core_tags() # Submit the initialization health event in case the `check` method is never called @@ -151,9 +151,6 @@ def __init__(self, name, init_config, instances): self.log.error("Configuration validation failed: %s", validation_result.errors) raise validation_result.errors[0] - # Keep a copy of the tags without the internal resource tags so they can be used for paths that don't - # go through the agent internal metrics submission processing those tags - self._non_internal_tags = copy.deepcopy(self.tags) self.set_resource_tags() self.pg_settings = {} self._warnings_by_code = {} @@ -185,7 +182,6 @@ def __init__(self, name, init_config, instances): self.check_initializations.append(self.load_system_identifier) self.check_initializations.append(self.initialize_is_aurora) self.check_initializations.append(self._query_manager.compile_queries) - self.tags_without_db = [t for t in copy.copy(self.tags) if not t.startswith("db:")] self.autodiscovery = self._build_autodiscovery() self._dynamic_queries = [] # _database_instance_emitted: limit the collection and transmission of the database instance metadata @@ -246,8 +242,8 @@ def _build_autodiscovery(self): return discovery @property - def tags(self): - return self._tags + def tags_without_db(self): + return self.tag_manager.get_tags(include_db=False) @property def dbms(self): @@ -258,40 +254,41 @@ def add_core_tags(self): """ Add tags that should be attached to every metric/event but which require check calculations outside the config. """ - self.tags.append("database_hostname:{}".format(self.database_hostname)) - self.tags.append("database_instance:{}".format(self.database_identifier)) + self.tag_manager.set_tag("database_hostname", self.database_hostname, replace=True) + self.tag_manager.set_tag("database_instance", self.database_identifier, replace=True) def set_resource_tags(self): if self._config.gcp.project_id and self._config.gcp.instance_id: - self.tags.append( - "dd.internal.resource:gcp_sql_database_instance:{}:{}".format( - self._config.gcp.project_id, self._config.gcp.instance_id - ) + self.tag_manager.set_tag( + "dd.internal.resource", + "gcp_sql_database_instance:{}:{}".format(self._config.gcp.project_id, self._config.gcp.instance_id), ) if self._config.aws.instance_endpoint: - self.tags.append( - "dd.internal.resource:aws_rds_instance:{}".format( - self._config.aws.instance_endpoint, - ) + self.tag_manager.set_tag( + "dd.internal.resource", + "aws_rds_instance:{}".format(self._config.aws.instance_endpoint), ) elif AWS_RDS_HOSTNAME_SUFFIX in self.resolved_hostname: # allow for detecting if the host is an RDS host, and emit # the resource properly even if the `aws` config is unset - self.tags.append("dd.internal.resource:aws_rds_instance:{}".format(self.resolved_hostname)) + self.tag_manager.set_tag( + "dd.internal.resource", + "aws_rds_instance:{}".format(self.resolved_hostname), + ) if self._config.azure.deployment_type and self._config.azure.fully_qualified_domain_name: deployment_type = self._config.azure.deployment_type # some `deployment_type`s map to multiple `resource_type`s resource_type = AZURE_DEPLOYMENT_TYPE_TO_RESOURCE_TYPE.get(deployment_type) if resource_type: - self.tags.append( - "dd.internal.resource:{}:{}".format(resource_type, self._config.azure.fully_qualified_domain_name) + self.tag_manager.set_tag( + "dd.internal.resource", + "{}:{}".format(resource_type, self._config.azure.fully_qualified_domain_name), ) # finally, tag the `database_instance` resource for this instance # metrics intake will use this tag to add all the tags for the instance - self.tags.append( - "dd.internal.resource:database_instance:{}".format( - self.database_identifier, - ) + self.tag_manager.set_tag( + "dd.internal.resource", + "database_instance:{}".format(self.database_identifier), ) def _new_query_executor(self, queries, db): @@ -1191,7 +1188,7 @@ def _send_database_instance_metadata(self): "collection_interval": self._config.database_instance_collection_interval, 'dbms_version': self.dbms_version, 'integration_version': __version__, - "tags": [t for t in self._non_internal_tags if not t.startswith('db:')], + "tags": self.tag_manager.get_tags(include_internal=False, include_db=False), "timestamp": time() * 1000, "cloud_metadata": self.cloud_metadata, "metadata": { @@ -1213,11 +1210,9 @@ def check(self, _): # Resend the initialization event. The submitter will debounce it self._submit_initialization_health_event() - tags = copy.copy(self.tags) - self.tags_without_db = [t for t in copy.copy(self.tags) if not t.startswith("db:")] - # Reset _non_internal_tags to prevent stale dynamic tags (e.g., replication_role) from accumulating - self._non_internal_tags = [t for t in copy.copy(self.tags) if not t.startswith("dd.internal")] - tags_to_add = [] + # Tags computed before connecting, used for the service check if an early failure occurs. + # Recomputed below once the dynamic tags (version, cluster name, etc.) have been set. + tags = self.tag_manager.get_tags() try: # Check version self._connect() @@ -1228,24 +1223,20 @@ def check(self, _): self.wal_level = self._get_wal_level() # Add raw version as a tag - tags.append(f'postgresql_version:{self.raw_version}') - tags_to_add.append(f'postgresql_version:{self.raw_version}') + self.tag_manager.set_tag('postgresql_version', self.raw_version, replace=True) # Add system identifier as a tag if self.system_identifier: - tags.append(f'system_identifier:{self.system_identifier}') - tags_to_add.append(f'system_identifier:{self.system_identifier}') + self.tag_manager.set_tag('system_identifier', str(self.system_identifier), replace=True) # Add cluster name if it was set if self.cluster_name: - tags.append(f'postgresql_cluster_name:{self.cluster_name}') - tags_to_add.append(f'postgresql_cluster_name:{self.cluster_name}') + self.tag_manager.set_tag('postgresql_cluster_name', self.cluster_name, replace=True) if self._config.tag_replication_role: - replication_role_tag = "replication_role:{}".format(self._get_replication_role()) - tags.append(replication_role_tag) - tags_to_add.append(replication_role_tag) - self._update_tag_sets(tags_to_add) + self.tag_manager.set_tag('replication_role', self._get_replication_role(), replace=True) + + tags = self.tag_manager.get_tags() self._send_database_instance_metadata() self.log.debug("Running check against version %s: is_aurora: %s", str(self.version), str(self.is_aurora)) @@ -1316,7 +1307,3 @@ def _run_automatic_diagnostics(self): self.log.exception("Error during automatic diagnostics: %s", e) finally: self.log.info("Automatic diagnostics completed") - - def _update_tag_sets(self, tags): - self._non_internal_tags = list(set(self._non_internal_tags) | set(tags)) - self.tags_without_db = list(set(self.tags_without_db) | set(tags)) diff --git a/postgres/tests/common.py b/postgres/tests/common.py index a2cb1e1c67734..ac1afb123e7a9 100644 --- a/postgres/tests/common.py +++ b/postgres/tests/common.py @@ -46,13 +46,16 @@ POSTGRES_VERSION = os.environ.get('POSTGRES_VERSION', None) POSTGRES_IMAGE = "alpine" POSTGRES_LOCALE = os.environ.get('POSTGRES_LOCALE', "UTF8") +POSTGRES_IMAGE_TAG = os.environ.get('POSTGRES_IMAGE_TAG', None) REPLICA_CONTAINER_1_NAME = 'compose-postgres_replica-1' REPLICA_CONTAINER_2_NAME = 'compose-postgres_replica2-1' REPLICA_LOGICAL_1_NAME = 'compose-postgres_logical_replica-1' USING_LATEST = False -if POSTGRES_VERSION is not None: +if POSTGRES_IMAGE_TAG is not None: + POSTGRES_IMAGE = POSTGRES_IMAGE_TAG + "-alpine" +elif POSTGRES_VERSION is not None: USING_LATEST = POSTGRES_VERSION.endswith('latest') POSTGRES_IMAGE = POSTGRES_VERSION + "-alpine" diff --git a/postgres/tests/test_pg_integration.py b/postgres/tests/test_pg_integration.py index 9120411d7c88b..44736314a5c27 100644 --- a/postgres/tests/test_pg_integration.py +++ b/postgres/tests/test_pg_integration.py @@ -377,36 +377,16 @@ def format_with_error(value, **kwargs): def test_can_connect_service_check(aggregator, integration_check, pg_instance): - # First: check run with a valid postgres instance check = integration_check(pg_instance) - - check.run() - expected_tags = _get_expected_tags(check, pg_instance, with_db=True) - aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.OK, tags=expected_tags) - aggregator.reset() - - # Second: keep the connection open but an unexpected error happens during check run orig_db = check.db - # Second: keep the connection open but an unexpected error happens during check run - with pytest.raises(AttributeError): - check.db = mock.MagicMock(side_effect=AttributeError('foo')) - check.check(pg_instance) - - # Since we can't connect to the host, we can't gather the replication role + # Before we ever connect successfully, none of the dynamically retrieved tags + # (version, replication role, system identifier, cluster name) have been cached. tags_without_role = _get_expected_tags( check, pg_instance, with_db=True, with_version=False, with_sys_id=False, with_cluster_name=False, role=None ) - aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.CRITICAL, tags=tags_without_role) - aggregator.reset() - # Third: connection still open but this time no error - check.db = orig_db - check.check(pg_instance) - aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.OK, tags=expected_tags) - - # Forth: connection health check failed - with pytest.raises(DatabaseHealthCheckError): + def broken_connection(): db = mock.MagicMock() db.cursor().__enter__().execute.side_effect = psycopg.OperationalError('foo') @@ -414,12 +394,45 @@ def test_can_connect_service_check(aggregator, integration_check, pg_instance): def mock_db(): yield db - check.db = mock_db - check.check(pg_instance) + return mock_db + # First: the very first check run fails to connect. Since there has never been a + # successful run, the dynamically retrieved tags are not available yet. + with pytest.raises(DatabaseHealthCheckError): + check.db = broken_connection() + check.check(pg_instance) aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.CRITICAL, tags=tags_without_role) aggregator.reset() + # Second: a successful run populates and caches the dynamically retrieved tags. + check.db = orig_db + check.run() + expected_tags = _get_expected_tags(check, pg_instance, with_db=True) + aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.OK, tags=expected_tags) + aggregator.reset() + + # Third: the connection fails again, but the tags cached from the previous + # successful run are retained on the critical service check. + with pytest.raises(DatabaseHealthCheckError): + check.db = broken_connection() + check.check(pg_instance) + aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.CRITICAL, tags=expected_tags) + aggregator.reset() + + # Fourth: another successful run, the tags are still present. + check.db = orig_db + check.run() + aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.OK, tags=expected_tags) + aggregator.reset() + + # Fifth: the connection health check fails, but all the previously cached tags + # are still reported on the critical service check. + with pytest.raises(DatabaseHealthCheckError): + check.db = broken_connection() + check.check(pg_instance) + aggregator.assert_service_check('postgres.can_connect', count=1, status=PostgreSql.CRITICAL, tags=expected_tags) + aggregator.reset() + def test_connections_metrics(aggregator, integration_check, pg_instance): check = integration_check(pg_instance) @@ -1120,12 +1133,13 @@ def test_replication_role_tag_reflects_current_role_after_promotion(aggregator, check.run() # Verify the role tag reflects master - replication_tags = [t for t in check._non_internal_tags if t.startswith('replication_role:')] + non_internal_tags = check.tag_manager.get_tags(include_internal=False) + replication_tags = [t for t in non_internal_tags if t.startswith('replication_role:')] assert replication_tags == ['replication_role:master'], f"Expected master role tag, got: {replication_tags}" - # Verify dd.internal tags are not in _non_internal_tags - internal_tags = [t for t in check._non_internal_tags if t.startswith('dd.internal')] - assert internal_tags == [], f"dd.internal tags should not be in _non_internal_tags: {internal_tags}" + # Verify dd.internal tags are not in the non-internal tags + internal_tags = [t for t in non_internal_tags if t.startswith('dd.internal')] + assert internal_tags == [], f"dd.internal tags should not be in non-internal tags: {internal_tags}" # Verify the metadata event has the correct role dbm_metadata = aggregator.get_event_platform_events("dbm-metadata") @@ -1146,7 +1160,8 @@ def test_replication_role_tag_reflects_current_role_after_promotion(aggregator, check.run() # After role change, only the current role should be present - replication_tags = [t for t in check._non_internal_tags if t.startswith('replication_role:')] + non_internal_tags = check.tag_manager.get_tags(include_internal=False) + replication_tags = [t for t in non_internal_tags if t.startswith('replication_role:')] assert len(replication_tags) == 1, ( f"Expected exactly 1 replication_role tag, got {len(replication_tags)}: {replication_tags}" ) @@ -1155,8 +1170,8 @@ def test_replication_role_tag_reflects_current_role_after_promotion(aggregator, ) # Verify dd.internal tags are still excluded after role change - internal_tags = [t for t in check._non_internal_tags if t.startswith('dd.internal')] - assert internal_tags == [], f"dd.internal tags should not be in _non_internal_tags: {internal_tags}" + internal_tags = [t for t in non_internal_tags if t.startswith('dd.internal')] + assert internal_tags == [], f"dd.internal tags should not be in non-internal tags: {internal_tags}" # Verify the metadata event reflects the new role only dbm_metadata = aggregator.get_event_platform_events("dbm-metadata")