Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions tests/test_summarize_counters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import csv
import tempfile
import unittest
from pathlib import Path

from tools.summarize_counters import summarize


class SummarizeCountersTest(unittest.TestCase):
def test_counts_metric_types_and_duplicates(self):
with tempfile.TemporaryDirectory() as tmpdir:
path = Path(tmpdir) / "counters.csv"
with path.open("w", newline="", encoding="utf-8") as handle:
writer = csv.writer(handle)
writer.writerow(["1", "Gauge", "mx_gpu_temp"])
writer.writerow(["2", "Gauge", "mx_gpu_temp"])
writer.writerow(["3", "Counter", "mx_error_count"])
writer.writerow(["4", "Gauge", ""])
writer.writerow(["5", "", "mx_ignored"])

summary = summarize(path)

self.assertEqual(summary["metric_count"], 3)
self.assertEqual(summary["type_counts"], {"Counter": 1, "Gauge": 2})
self.assertEqual(summary["duplicate_metric_names"], ["mx_gpu_temp"])


if __name__ == "__main__":
unittest.main()
60 changes: 60 additions & 0 deletions tools/summarize_counters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Summarize mx-exporter counter CSV files as JSON."""

from __future__ import annotations

import argparse
import csv
import json
from collections import Counter
from pathlib import Path


def summarize(path: Path) -> dict[str, object]:
type_counts: Counter[str] = Counter()
metric_names: list[str] = []
with path.open(newline="", encoding="utf-8") as handle:
for row in csv.reader(handle):
if not row or not "".join(row).strip() or row[0].lstrip().startswith("#"):
continue
if len(row) < 3:
continue
metric_type = row[1].strip()
metric_name = row[2].strip()
if metric_type and metric_name:
type_counts[metric_type] += 1
metric_names.append(metric_name)
Comment on lines +22 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

如果 metric_name 为空,当前逻辑仍会增加 type_counts[metric_type] 的计数,但不会将该指标加入 metric_names。这会导致最终输出的 metric_count(即 len(metric_names))与 type_counts 的各项之和不一致。建议仅在 metric_namemetric_type 均非空时才进行统计,以保证数据的一致性。

Suggested change
metric_type = row[1].strip()
metric_name = row[2].strip()
type_counts[metric_type] += 1
if metric_name:
metric_names.append(metric_name)
metric_type = row[1].strip()
metric_name = row[2].strip()
if metric_type and metric_name:
type_counts[metric_type] += 1
metric_names.append(metric_name)

duplicates = sorted(name for name, count in Counter(metric_names).items() if count > 1)
return {
"path": str(path),
"metric_count": len(metric_names),
"type_counts": dict(sorted(type_counts.items())),
"duplicate_metric_names": duplicates,
}


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"path",
nargs="?",
type=Path,
default=Path(__file__).resolve().parents[1] / "mx_exporter" / "default-counters.csv",
)
parser.add_argument("--output", type=Path, help="write summary JSON to this path")
args = parser.parse_args()

if not args.path.is_file():
parser.error(f"input file does not exist: {args.path}")

payload = summarize(args.path)
Comment on lines +45 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

如果指定的 path 文件不存在(例如默认路径不存在,或者用户输入了错误的路径),程序在调用 summarize(args.path) 时会抛出 FileNotFoundError 堆栈信息。作为 CLI 工具,建议在解析参数后先检查文件是否存在,并使用 parser.error() 提供更友好的错误提示。

    args = parser.parse_args()

    if not args.path.is_file():
        parser.error(f"Input file does not exist: {args.path}")

    payload = summarize(args.path)

text = json.dumps(payload, indent=2, ensure_ascii=False)
if args.output:
args.output.write_text(text + "\n", encoding="utf-8")
else:
print(text)
return 0


if __name__ == "__main__":
raise SystemExit(main())