← All code samples

SRE → triage alerts

Mark an alert as a false positive or ignored, list investigation groups, and resolve finished threads from code.

Step What the SDK does
1. List list_alerts / list_investigation_groups
2. Triage ignore_alert or set_alert_attention
3. Close resolve_investigations / cancel_investigations

Use --dry-run on the sample to inspect without writing.

Minimal

from stackgen import StackgenClient, StackgenConfig

client = StackgenClient(
    StackgenConfig(
        base_url="https://app.stackgen.com",
        api_token="stackgen_…",
        workspace_id="<workspace-uuid>",
    )
)

client.sre.set_alert_attention(alert_id, "false_positive", reason="known flake")
# or: client.sre.ignore_alert(alert_id, reason="noise")

groups = client.sre.list_investigation_groups(page_size=20)
print(groups)

client.sre.resolve_investigations([investigation_id])

How to run

export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>

python examples/sre/triage-alerts.py --dry-run
python examples/sre/triage-alerts.py --alert-id <uuid> --false-positive --resolve

Complete script

#!/usr/bin/env python3
"""SRE: triage an alert and close finished investigation threads.

Documented at:
  https://appcd-dev.github.io/stackgen-sdk/code-samples/sre-triage-alerts/

Credentials (flags override env):
  STACKGEN_URL, STACKGEN_TOKEN, STACKGEN_WORKSPACE_ID (or STACKGEN_ORG_ID / STACKGEN_PROJECT)

Usage:

  python examples/sre/triage-alerts.py --dry-run

  python examples/sre/triage-alerts.py --alert-id <uuid> --false-positive

  python examples/sre/triage-alerts.py --alert-id <uuid> --ignore-reason noise --resolve
"""

from __future__ import annotations

import argparse
import json
import os
import sys

from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError


def _org_id(cli_value: str | None) -> str:
    return (
        (cli_value or "").strip()
        or os.environ.get("STACKGEN_WORKSPACE_ID", "").strip()
        or os.environ.get("STACKGEN_ORG_ID", "").strip()
        or os.environ.get("STACKGEN_PROJECT", "").strip()
        or os.environ.get("STACKGEN_PROJECT_ID", "").strip()
    )


def _config(args: argparse.Namespace) -> StackgenConfig:
    base = (args.base_url or os.environ.get("STACKGEN_URL", "")).strip()
    token = (args.api_token or os.environ.get("STACKGEN_TOKEN", "")).strip()
    org = _org_id(args.workspace_id)
    if not base or not token:
        raise SystemExit(
            "set --base-url/--api-token or STACKGEN_URL / STACKGEN_TOKEN"
        )
    return StackgenConfig(base_url=base, api_token=token, workspace_id=org)


def _alert_rows(payload: object) -> list[dict]:
    if not isinstance(payload, dict):
        return []
    rows = payload.get("alerts") or payload.get("items") or []
    return [r for r in rows if isinstance(r, dict)]


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base-url", default="")
    parser.add_argument("--api-token", default="")
    parser.add_argument("--workspace-id",
        "--org-id", default="")
    parser.add_argument("--alert-id", default="")
    parser.add_argument("--q", default="", help="Alert search when --alert-id is omitted")
    parser.add_argument("--ignore-reason", default="")
    parser.add_argument(
        "--false-positive",
        action="store_true",
        help="Set attention to false_positive",
    )
    parser.add_argument(
        "--resolve",
        action="store_true",
        help="Resolve completed/failed/draft investigations for the alert",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="List candidates and exit without writing",
    )
    args = parser.parse_args()

    client = StackgenClient(_config(args))
    alert_id = args.alert_id.strip()

    if not alert_id:
        listed = client.sre.list_alerts(status="active", q=args.q or None, page_size=5)
        print("alerts:", json.dumps(listed, indent=2, default=str))
        rows = _alert_rows(listed)
        if rows:
            alert_id = str(rows[0].get("id") or "")

    groups = client.sre.list_investigation_groups(page_size=5)
    print("groups:", json.dumps(groups, indent=2, default=str))

    if args.dry_run:
        if alert_id:
            print(f"alert_id={alert_id}", file=sys.stderr)
        return 0

    if not alert_id:
        print("error: pass --alert-id or ensure list_alerts returns a row", file=sys.stderr)
        return 1

    if args.false_positive:
        updated = client.sre.set_alert_attention(
            alert_id,
            "false_positive",
            reason=args.ignore_reason or None,
        )
        print("attention:", json.dumps(updated, indent=2, default=str))
    elif args.ignore_reason:
        updated = client.sre.ignore_alert(alert_id, reason=args.ignore_reason)
        print("ignored:", json.dumps(updated, indent=2, default=str))
    elif not args.resolve:
        print(
            "pass --false-positive, --ignore-reason TEXT, --resolve, or --dry-run",
            file=sys.stderr,
        )
        return 2

    if args.resolve:
        inv = client.sre.list_investigations(alert_id=alert_id, page_size=50)
        print("investigations:", json.dumps(inv, indent=2, default=str))
        rows: list[dict] = []
        if isinstance(inv, dict):
            rows = [r for r in (inv.get("investigations") or []) if isinstance(r, dict)]
        done = [
            str(r["id"])
            for r in rows
            if r.get("id")
            and str(r.get("status") or "") in {"completed", "failed", "draft"}
        ]
        if done:
            resolved = client.sre.resolve_investigations(done)
            print("resolved:", json.dumps(resolved, indent=2, default=str))
        else:
            print("resolve: no completed/failed/draft threads", file=sys.stderr)

    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except StackgenError as exc:
        print(f"error: {exc}", file=sys.stderr)
        if "401" in str(exc) or "missing identity" in str(exc).lower():
            print(
                "hint: SRE /app/sre routes need the edge to inject workspace "
                "identity (X-Appcd-Org).",
                file=sys.stderr,
            )
        raise SystemExit(1)

Notes

  • Operators set status to ignored or active. Resolution from the source sync sets resolved. Do not send status and attention in the same patch.
  • Attention values: needs_attention, needs_review, false_positive, others.
  • fork_investigation starts a new thread that keeps the parent RCA context.
  • list_remediation_checks returns post-remediation verification rows.

Related: Investigate alert · Pull alerts