Skip to content

Commit 8e530f0

Browse files
Pigbibicodex
andcommitted
feat: localize operational runtime notifications
Co-Authored-By: Codex <noreply@openai.com>
1 parent 18d284f commit 8e530f0

5 files changed

Lines changed: 101 additions & 36 deletions

File tree

.github/workflows/execution-report-heartbeat.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jobs:
5757
CLOUD_RUN_SERVICES: ${{ secrets.CLOUD_RUN_SERVICES }}
5858
CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON || secrets.CLOUD_RUN_SERVICE_TARGETS_JSON }}
5959
CLOUD_SCHEDULER_MAIN_TIME: ${{ vars.CLOUD_SCHEDULER_MAIN_TIME }}
60+
NOTIFY_LANG: ${{ vars.NOTIFY_LANG || 'zh' }}
6061
GLOBAL_TELEGRAM_CHAT_ID: ${{ secrets.GLOBAL_TELEGRAM_CHAT_ID }}
6162
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
6263
TELEGRAM_TOKEN_SECRET_NAME: ${{ vars.TELEGRAM_TOKEN_SECRET_NAME }}

.github/workflows/runtime-guard.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
CLOUD_RUN_SERVICES: ${{ secrets.CLOUD_RUN_SERVICES }}
5656
CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON || secrets.CLOUD_RUN_SERVICE_TARGETS_JSON }}
5757
CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }}
58+
NOTIFY_LANG: ${{ vars.NOTIFY_LANG || 'zh' }}
5859
GLOBAL_TELEGRAM_CHAT_ID: ${{ secrets.GLOBAL_TELEGRAM_CHAT_ID }}
5960
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
6061
TELEGRAM_TOKEN_SECRET_NAME: ${{ vars.TELEGRAM_TOKEN_SECRET_NAME }}

scripts/cloud_run_runtime_guard.py

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
import urllib.request
1515
from typing import Any
1616

17+
from quant_platform_kit.common.operational_notification_localization import (
18+
format_operational_alert,
19+
operational_notification_text,
20+
resolve_operational_notification_locale,
21+
)
22+
1723

1824
ERROR_SEVERITIES = {"ERROR", "CRITICAL", "ALERT", "EMERGENCY"}
1925
FAILURE_WORDS = (
@@ -60,6 +66,14 @@ def _env_bool(name: str, default: bool = False) -> bool:
6066
return value in {"1", "true", "yes", "y", "on"}
6167

6268

69+
def _notification_locale() -> str:
70+
return resolve_operational_notification_locale(os.environ.get("NOTIFY_LANG"))
71+
72+
73+
def _notice(key: str, /, **values: object) -> str:
74+
return operational_notification_text(_notification_locale(), key, **values)
75+
76+
6377
def _load_services() -> list[str]:
6478
services = []
6579
enabled_target_services = []
@@ -669,7 +683,8 @@ def main() -> int:
669683
services = _load_services()
670684
except RuntimeError as exc:
671685
services = []
672-
issues.append(f"service configuration error: {exc}")
686+
issues.append(_notice("runtime_guard_service_configuration_error"))
687+
details.append(f"service configuration error: {exc}")
673688
scheduler_pattern = (
674689
os.environ.get("RUNTIME_GUARD_SCHEDULER_JOB_PATTERN")
675690
or _scheduler_job_pattern_for_services(services)
@@ -683,7 +698,8 @@ def main() -> int:
683698
try:
684699
entries = _run_gcloud_logging(project, log_filter, limit)
685700
except RuntimeError as exc:
686-
issues.append(f"Cloud Run log query failed for {service}: {exc}")
701+
issues.append(_notice("runtime_guard_cloud_run_log_query_failed", service=service))
702+
details.append(f"Cloud Run log query failed for {service}: {exc}")
687703
continue
688704
queried_services.add(service)
689705
failures = [entry for entry in entries if _is_failure(entry)]
@@ -692,7 +708,13 @@ def main() -> int:
692708
success_count_by_service[service] = service_success_count
693709
success_count += service_success_count
694710
if failures:
695-
issues.append(f"{len(failures)} Cloud Run failure log(s) for {service}")
711+
issues.append(
712+
_notice(
713+
"runtime_guard_cloud_run_failure_logs",
714+
count=len(failures),
715+
service=service,
716+
)
717+
)
696718
details.extend(_summarize(entry) for entry in failures[:5])
697719

698720
if services and require_success:
@@ -702,8 +724,11 @@ def main() -> int:
702724
queried_services,
703725
):
704726
issues.append(
705-
f"no successful Cloud Run request found for {service} "
706-
f"in the last {lookback_minutes} minutes"
727+
_notice(
728+
"runtime_guard_no_successful_request",
729+
service=service,
730+
lookback_minutes=lookback_minutes,
731+
)
707732
)
708733

709734
if check_scheduler and scheduler_pattern:
@@ -732,10 +757,16 @@ def main() -> int:
732757
continue
733758
failures.append(entry)
734759
if failures:
735-
issues.append(f"{len(failures)} Cloud Scheduler failure log(s)")
760+
issues.append(
761+
_notice(
762+
"runtime_guard_scheduler_failure_logs",
763+
count=len(failures),
764+
)
765+
)
736766
details.extend(_summarize(entry) for entry in failures[:5])
737767
except RuntimeError as exc:
738-
issues.append(f"Cloud Scheduler log query failed: {exc}")
768+
issues.append(_notice("runtime_guard_scheduler_log_query_failed"))
769+
details.append(f"Cloud Scheduler log query failed: {exc}")
739770
elif check_scheduler:
740771
print("Skipping Cloud Scheduler check because no scheduler job pattern could be derived.", file=sys.stderr)
741772

@@ -752,18 +783,15 @@ def main() -> int:
752783
f"{os.environ['GITHUB_SERVER_URL']}/{os.environ['GITHUB_REPOSITORY']}"
753784
f"/actions/runs/{os.environ['GITHUB_RUN_ID']}"
754785
)
755-
message_lines = [
756-
f"[Runtime Guard] {name}",
757-
f"Project: {project}",
758-
f"Lookback: {lookback_minutes} minutes",
759-
"Issues:",
760-
*[f"- {issue}" for issue in issues],
761-
]
762-
if details:
763-
message_lines.extend(["Details:", *details[:10]])
764-
if run_url:
765-
message_lines.append(f"Workflow: {run_url}")
766-
message = "\n".join(message_lines)
786+
message = format_operational_alert(
787+
locale=_notification_locale(),
788+
alert_type="runtime_guard",
789+
name=name,
790+
context={"project": project, "lookback_minutes": lookback_minutes},
791+
issues=issues,
792+
technical_details=details[:10],
793+
workflow_url=run_url,
794+
)
767795
print(message)
768796
_send_telegram(message[:3900])
769797
return 1 if fail_workflow else 0

scripts/execution_report_heartbeat.py

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
from typing import Any
1515
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
1616

17+
from quant_platform_kit.common.operational_notification_localization import (
18+
format_operational_alert,
19+
operational_notification_text,
20+
resolve_operational_notification_locale,
21+
)
22+
1723
try:
1824
from scripts.runtime_heartbeat_policy import (
1925
filter_due_targets,
@@ -76,6 +82,14 @@ def _env_bool(name: str, default: bool = False) -> bool:
7682
return value in {"1", "true", "yes", "y", "on"}
7783

7884

85+
def _notification_locale() -> str:
86+
return resolve_operational_notification_locale(os.environ.get("NOTIFY_LANG"))
87+
88+
89+
def _notice(key: str, /, **values: object) -> str:
90+
return operational_notification_text(_notification_locale(), key, **values)
91+
92+
7993
def _enabled_value(value: Any, *, default: bool = True) -> bool:
8094
if value is None:
8195
return default
@@ -1035,7 +1049,14 @@ def main(now: dt.datetime | None = None) -> int:
10351049
project=project,
10361050
)
10371051
except RuntimeError as exc:
1038-
message = f"[Execution Report Heartbeat] {name}\nScheduler policy error: {exc}"
1052+
message = format_operational_alert(
1053+
locale=_notification_locale(),
1054+
alert_type="execution_report_heartbeat",
1055+
name=name,
1056+
context={"lookback_hours": lookback_hours},
1057+
issues=[_notice("heartbeat_scheduler_policy_error")],
1058+
technical_details=[str(exc)],
1059+
)
10391060
print(message)
10401061
_send_telegram(message)
10411062
return 1 if fail_workflow else 0
@@ -1173,36 +1194,44 @@ def main(now: dt.datetime | None = None) -> int:
11731194
return 0
11741195

11751196
issues = []
1197+
technical_details = []
11761198
if list_errors:
1177-
issues.extend(f"list failed: {item}" for item in list_errors[:3])
1199+
issues.append(_notice("heartbeat_list_failed"))
1200+
technical_details.extend(list_errors[:3])
11781201
if not sorted_objects:
1179-
issues.append(f"no report object updated in the last {lookback_hours:g} hours")
1202+
issues.append(_notice("heartbeat_no_recent_report", lookback_hours=f"{lookback_hours:g}"))
11801203
elif required_keys:
11811204
missing = [key for key in required_keys if key not in accepted_by_service]
11821205
issues.append(
1183-
"missing acceptable report for runtime target(s): "
1184-
+ ", ".join(required_labels[key] for key in missing)
1206+
_notice(
1207+
"heartbeat_missing_acceptable_report",
1208+
targets=", ".join(required_labels[key] for key in missing),
1209+
)
11851210
)
11861211
else:
1187-
issues.append(f"no acceptable report among {min(len(sorted_objects), max_reports)} recent report object(s)")
1212+
issues.append(
1213+
_notice(
1214+
"heartbeat_no_acceptable_report",
1215+
count=min(len(sorted_objects), max_reports),
1216+
)
1217+
)
11881218

11891219
run_url = ""
11901220
if os.environ.get("GITHUB_SERVER_URL") and os.environ.get("GITHUB_REPOSITORY") and os.environ.get("GITHUB_RUN_ID"):
11911221
run_url = (
11921222
f"{os.environ['GITHUB_SERVER_URL']}/{os.environ['GITHUB_REPOSITORY']}"
11931223
f"/actions/runs/{os.environ['GITHUB_RUN_ID']}"
11941224
)
1195-
message_lines = [
1196-
f"[Execution Report Heartbeat] {name}",
1197-
f"Lookback: {lookback_hours:g} hours",
1198-
"Issues:",
1199-
*[f"- {issue}" for issue in issues],
1200-
]
1201-
if inspected:
1202-
message_lines.extend(["Recent reports:", *inspected[:max_reports]])
1203-
if run_url:
1204-
message_lines.append(f"Workflow: {run_url}")
1205-
message = "\n".join(message_lines)
1225+
message = format_operational_alert(
1226+
locale=_notification_locale(),
1227+
alert_type="execution_report_heartbeat",
1228+
name=name,
1229+
context={"lookback_hours": f"{lookback_hours:g}"},
1230+
issues=issues,
1231+
recent_reports=inspected[:max_reports],
1232+
technical_details=technical_details,
1233+
workflow_url=run_url,
1234+
)
12061235
print(message)
12071236
_send_telegram(message[:3900])
12081237
return 1 if fail_workflow else 0

tests/test_cloud_run_runtime_guard.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
from scripts import cloud_run_runtime_guard as guard
99

1010

11+
def test_runtime_guard_notification_language_is_configurable(monkeypatch):
12+
monkeypatch.setenv("NOTIFY_LANG", "zh-CN")
13+
14+
assert guard._notice("runtime_guard_scheduler_log_query_failed") == "Cloud Scheduler 日志查询失败"
15+
16+
1117
def test_cloud_run_log_filter_includes_region_when_available():
1218
log_filter = guard._cloud_run_log_filter(
1319
"interactive-brokers-live-u1599-tqqq-service",

0 commit comments

Comments
 (0)