Files
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

248 lines
6.4 KiB
Python

"""CloudWatch log groups management."""
from typing import Any
from botocore.exceptions import ClientError
from tests.shared.infrastructure_sdk.deployer import (
DEFAULT_REGION,
get_boto3_client,
get_standard_tags_dict,
)
def create_log_group(
name: str,
retention_days: int = 7,
stack_name: str | None = None,
region: str = DEFAULT_REGION,
) -> dict[str, Any]:
"""Create log group.
Args:
name: Log group name.
retention_days: Log retention in days (1, 3, 5, 7, 14, 30, 60, 90, etc.).
stack_name: Stack name for tagging.
region: AWS region.
Returns:
Dictionary with log group info: name, arn.
"""
logs_client = get_boto3_client("logs", region)
try:
logs_client.create_log_group(logGroupName=name)
except ClientError as e:
if e.response["Error"]["Code"] != "ResourceAlreadyExistsException":
raise
# Set retention
logs_client.put_retention_policy(
logGroupName=name,
retentionInDays=retention_days,
)
# Add tags
if stack_name:
logs_client.tag_log_group(
logGroupName=name,
tags=get_standard_tags_dict(stack_name),
)
# Get ARN
response = logs_client.describe_log_groups(logGroupNamePrefix=name, limit=1)
arn = (
response["logGroups"][0]["arn"]
if response["logGroups"]
else f"arn:aws:logs:{region}:*:log-group:{name}"
)
return {
"name": name,
"arn": arn,
}
def delete_log_group(name: str, region: str = DEFAULT_REGION) -> None:
"""Delete log group.
Args:
name: Log group name.
region: AWS region.
"""
logs_client = get_boto3_client("logs", region)
try:
logs_client.delete_log_group(logGroupName=name)
except ClientError as e:
if e.response["Error"]["Code"] != "ResourceNotFoundException":
raise
def get_log_group(name: str, region: str = DEFAULT_REGION) -> dict[str, Any] | None:
"""Get log group details.
Args:
name: Log group name.
region: AWS region.
Returns:
Log group details or None if not found.
"""
logs_client = get_boto3_client("logs", region)
try:
response = logs_client.describe_log_groups(logGroupNamePrefix=name, limit=1)
for group in response.get("logGroups", []):
if group["logGroupName"] == name:
return {
"name": group["logGroupName"],
"arn": group["arn"],
"retention_days": group.get("retentionInDays"),
"stored_bytes": group.get("storedBytes", 0),
}
return None
except ClientError:
return None
def get_recent_logs(
log_group_name: str,
log_stream_name: str | None = None,
start_time_ms: int | None = None,
end_time_ms: int | None = None,
limit: int = 100,
region: str = DEFAULT_REGION,
) -> list[dict[str, Any]]:
"""Get recent log events.
Args:
log_group_name: Log group name.
log_stream_name: Optional log stream name filter.
start_time_ms: Start time in milliseconds since epoch.
end_time_ms: End time in milliseconds since epoch.
limit: Maximum number of events to return.
region: AWS region.
Returns:
List of log events with timestamp, message, and stream name.
"""
logs_client = get_boto3_client("logs", region)
params: dict[str, Any] = {
"logGroupName": log_group_name,
"limit": limit,
"interleaved": True,
}
if log_stream_name:
params["logStreamNames"] = [log_stream_name]
if start_time_ms:
params["startTime"] = start_time_ms
if end_time_ms:
params["endTime"] = end_time_ms
try:
response = logs_client.filter_log_events(**params)
except ClientError as e:
if e.response["Error"]["Code"] == "ResourceNotFoundException":
return []
raise
return [
{
"timestamp": event["timestamp"],
"message": event["message"],
"log_stream": event.get("logStreamName"),
}
for event in response.get("events", [])
]
def put_log_events(
log_group_name: str,
log_stream_name: str,
messages: list[str],
region: str = DEFAULT_REGION,
) -> None:
"""Write log events to a stream.
Args:
log_group_name: Log group name.
log_stream_name: Log stream name (created if doesn't exist).
messages: List of log messages.
region: AWS region.
"""
import time
logs_client = get_boto3_client("logs", region)
# Ensure stream exists
try:
logs_client.create_log_stream(
logGroupName=log_group_name,
logStreamName=log_stream_name,
)
except ClientError as e:
if e.response["Error"]["Code"] != "ResourceAlreadyExistsException":
raise
# Put events
timestamp = int(time.time() * 1000)
events = [{"timestamp": timestamp + i, "message": msg} for i, msg in enumerate(messages)]
logs_client.put_log_events(
logGroupName=log_group_name,
logStreamName=log_stream_name,
logEvents=events,
)
def list_log_streams(
log_group_name: str,
prefix: str | None = None,
limit: int = 50,
region: str = DEFAULT_REGION,
) -> list[dict[str, Any]]:
"""List log streams in a group.
Args:
log_group_name: Log group name.
prefix: Optional stream name prefix filter.
limit: Maximum streams to return.
region: AWS region.
Returns:
List of log stream details.
"""
logs_client = get_boto3_client("logs", region)
params: dict[str, Any] = {
"logGroupName": log_group_name,
"orderBy": "LastEventTime",
"descending": True,
"limit": limit,
}
if prefix:
params["logStreamNamePrefix"] = prefix
try:
response = logs_client.describe_log_streams(**params)
except ClientError as e:
if e.response["Error"]["Code"] == "ResourceNotFoundException":
return []
raise
return [
{
"name": stream["logStreamName"],
"first_event_timestamp": stream.get("firstEventTimestamp"),
"last_event_timestamp": stream.get("lastEventTimestamp"),
"stored_bytes": stream.get("storedBytes", 0),
}
for stream in response.get("logStreams", [])
]