Files
tracer-cloud--opensre/integrations/datadog/correlation/factory.py
T
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

115 lines
3.7 KiB
Python

"""Datadog-backed upstream-evidence provider factory."""
from __future__ import annotations
from collections.abc import Mapping
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any
from pydantic import ValidationError
from integrations.datadog.correlation.adapter import DatadogCorrelationAdapter
from integrations.datadog.correlation.provider import (
DatadogCorrelationQueries,
DatadogUpstreamEvidenceProvider,
)
if TYPE_CHECKING:
from core.domain.types.upstream import (
UpstreamEvidenceProvider,
)
def _parse_iso8601(value: str) -> datetime:
return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(UTC)
def _window_minutes(start: str, end: str) -> int:
try:
delta = _parse_iso8601(end) - _parse_iso8601(start)
return max(1, int(delta.total_seconds() // 60))
except Exception:
return 60
def datadog_avg_query(metric_name: str) -> str:
metric = metric_name.strip()
if metric.startswith(("avg:", "sum:", "min:", "max:", "count:")):
return metric
if "{" in metric and "}" in metric:
return f"avg:{metric}"
return f"avg:{metric}{{*}}"
def build_datadog_provider(
*,
integration_config: Mapping[str, Any] | None,
target_resource: str = "unknown-rds",
candidate_services: tuple[str, ...] = (),
) -> UpstreamEvidenceProvider | None:
"""Return a Datadog-backed upstream-evidence provider, or ``None``.
Callers pass the integration config and the alert-derived knobs
directly; the factory does **not** know about agent state shape.
"""
from integrations.config_models import DatadogIntegrationConfig
from integrations.datadog.client import DatadogClient
if not integration_config:
return None
try:
datadog_cfg = DatadogIntegrationConfig.model_validate(integration_config)
except ValidationError:
return None
client = DatadogClient(datadog_cfg)
def metric_query(metric_name: str, window: dict[str, Any]) -> dict[str, Any]:
start = str(window.get("from") or "")
end = str(window.get("to") or "")
if not start or not end:
return {"timestamps": [], "values": []}
query = datadog_avg_query(metric_name)
result = client.query_metrics(query, start=_parse_iso8601(start), end=_parse_iso8601(end))
if not result.get("success"):
return {"timestamps": [], "values": []}
return {
"timestamps": result.get("timestamps") or [],
"values": result.get("values") or [],
}
def log_query(query: str, window: dict[str, Any]) -> dict[str, Any]:
start = str(window.get("from") or "")
end = str(window.get("to") or "")
start_dt = _parse_iso8601(start) if start else None
end_dt = _parse_iso8601(end) if end else None
minutes = _window_minutes(start, end)
result = client.search_logs(
query,
time_range_minutes=minutes,
limit=100,
start=start_dt,
end=end_dt,
)
logs = result.get("logs") if isinstance(result, dict) else []
if not isinstance(logs, list):
logs = []
return {
"timestamps": [
str(item.get("timestamp", "")) for item in logs if isinstance(item, dict)
],
"messages": [str(item.get("message", "")) for item in logs if isinstance(item, dict)],
}
return DatadogUpstreamEvidenceProvider(
adapter=DatadogCorrelationAdapter(
metric_query_fn=metric_query,
log_query_fn=log_query,
),
queries=DatadogCorrelationQueries(
upstream_service_names=candidate_services,
),
target_resource=target_resource,
)