mirror of
https://github.com/hanzoai/datastore.git
synced 2026-09-26 13:55:07 +00:00
PrometheusMetricsWriter emits DatastoreProfileEvents_, DatastoreMetrics_, DatastoreAsyncMetrics_, DatastoreErrorMetric_ and Datastore_Info, while four consumers still asked for the ClickHouse spelling, so each was reading a string its own server had stopped writing. The three integration tests failed outright, the prometheus endpoint one twice over, since its allowed-prefix assertion rejects every metric line before the query counter is ever looked up. The keeper stress harness was quieter and worse: an allowlist matching nothing reads as a run that produced no metrics rather than as an error. The keeper-stress-analysis skill keeps the ClickHouse spelling on purpose. It queries keeper_stress_tests.keeper_metrics_ts on play.clickhouse.com, where those names are stored data written by upstream nightlies rather than anything this server emits, and that table holds no Datastore rows at all. Renaming there would turn a query that matches into one that matches nothing. Co-authored-by: Hanzo Dev <[email protected]>
82 lines
2.4 KiB
Python
82 lines
2.4 KiB
Python
import json
|
|
import re
|
|
|
|
_METRIC_RE = re.compile(
|
|
r"^([a-zA-Z_:][a-zA-Z0-9_:]*)(\{([^}]*)\})?\s+([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)\s*$"
|
|
)
|
|
_LABEL_RE = re.compile(r"\s*([a-zA-Z_][a-zA-Z0-9_]*)=\"([^\"]*)\"\s*")
|
|
|
|
# Keeper's PrometheusMetricsWriter emits: DatastoreProfileEvents_*, DatastoreMetrics_*,
|
|
# DatastoreAsyncMetrics_*, DatastoreHistogramMetrics_*, DatastoreDimensionalMetrics_*.
|
|
# Also allow legacy/alternative prefixes.
|
|
DEFAULT_PREFIXES = (
|
|
"clickhouse_keeper_",
|
|
"ClickHouseKeeper_",
|
|
"DatastoreProfileEvents_",
|
|
"DatastoreMetrics_",
|
|
"DatastoreAsyncMetrics_",
|
|
"DatastoreHistogramMetrics_",
|
|
"DatastoreDimensionalMetrics_",
|
|
"keeper_",
|
|
"raft_",
|
|
)
|
|
|
|
|
|
def _unescape_label_value(s):
|
|
"""Unescape Prometheus label value (\\, \", \n, \t, \r)."""
|
|
escape_map = {"n": "\n", "t": "\t", "r": "\r", '"': '"', "\\": "\\"}
|
|
out = []
|
|
i = 0
|
|
while i < len(s):
|
|
if s[i] == "\\" and i + 1 < len(s):
|
|
out.append(escape_map.get(s[i + 1], s[i + 1]))
|
|
i += 2
|
|
else:
|
|
out.append(s[i])
|
|
i += 1
|
|
return "".join(out)
|
|
|
|
|
|
def parse_prometheus_text(
|
|
text, allow_prefixes=DEFAULT_PREFIXES, name_allowlist=None, exclude_label_keys=None
|
|
):
|
|
if not text:
|
|
return []
|
|
|
|
name_allow = set(name_allowlist) if name_allowlist else None
|
|
excl_label_keys = set(exclude_label_keys) if exclude_label_keys else None
|
|
rows = []
|
|
|
|
for line in text.splitlines():
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
m = _METRIC_RE.match(line.strip())
|
|
if not m:
|
|
continue
|
|
name, _, label_blob, val = m.groups()
|
|
|
|
if allow_prefixes and not any(name.startswith(p) for p in allow_prefixes):
|
|
continue
|
|
if name_allow and name not in name_allow:
|
|
continue
|
|
|
|
labels = {}
|
|
if label_blob:
|
|
for lm in _LABEL_RE.finditer(label_blob):
|
|
labels[lm.group(1)] = _unescape_label_value(lm.group(2))
|
|
|
|
if excl_label_keys and any(k in excl_label_keys for k in labels):
|
|
continue
|
|
|
|
try:
|
|
value = float(val)
|
|
except Exception:
|
|
continue
|
|
|
|
rows.append({
|
|
"name": name,
|
|
"value": value,
|
|
"labels_json": json.dumps(labels, ensure_ascii=False),
|
|
})
|
|
return rows
|