fix: add opencode smoke observability probes
This commit is contained in:
@@ -321,7 +321,7 @@ export function searchScript(observability: ObservabilityConfig, target: Observa
|
||||
const proxyPrefix = `/api/v1/namespaces/${target.namespace}/services/http:${observability.traceBackend.serviceName}:http/proxy`;
|
||||
const searchProxyPath = `${proxyPrefix}${searchPath}`;
|
||||
const grepLiteral = options.grep === null ? "None" : JSON.stringify(options.grep);
|
||||
const effectiveQuery = inferSearchTempoQuery(options);
|
||||
const effectiveQuery = options.query ?? inferSearchTempoQuery(options);
|
||||
const queryLiteral = effectiveQuery === null ? "None" : JSON.stringify(effectiveQuery);
|
||||
const pathLiteral = options.path === null ? "None" : JSON.stringify(options.path);
|
||||
const statusLiteral = options.status === null ? "None" : String(options.status);
|
||||
@@ -383,6 +383,16 @@ IMPORTANT_ATTRS = [
|
||||
"returnedMessages", "totalMessages", "roleSequencePrefix",
|
||||
"consecutiveUserPrefix", "adjacentSameRoleCount", "userCount",
|
||||
"agentCount",
|
||||
"opencode.proxy.phase", "opencode.proxy.streaming",
|
||||
"opencode.proxy.ticket_accepted", "opencode.proxy.sse.directory_rewrite_enabled",
|
||||
"opencode.proxy.sse.directory_rewrite_from",
|
||||
"opencode.proxy.sse.directory_rewrite_to",
|
||||
"opencode.provider.sse.content_chunks",
|
||||
"opencode.provider.sse.content_chars",
|
||||
"opencode.provider.sse.output_data_lines",
|
||||
"opencode.provider.sse.done_lines",
|
||||
"opencode.provider.sse.json_errors",
|
||||
"opencode.provider.sse.reasoning_only_choices_dropped",
|
||||
"http.target", "http.url", "url.path",
|
||||
"db.system", "db.operation.name", "db.sql.table", "db.query.arg_count",
|
||||
"db.index.expected", "db.pool.max_open", "db.pool.open_connections",
|
||||
@@ -542,8 +552,57 @@ def compact_span(span, service, resource_attrs, scope_name):
|
||||
def grep_matches_text(text):
|
||||
return GREP is not None and GREP.lower() in text.lower()
|
||||
|
||||
def parse_grep_key_value():
|
||||
if GREP is None:
|
||||
return None, None
|
||||
match = re.match(r"^([A-Za-z0-9_.-]+)=(.+)$", GREP)
|
||||
if not match:
|
||||
return None, None
|
||||
key = match.group(1)
|
||||
if key.startswith("span."):
|
||||
key = key[5:]
|
||||
raw = match.group(2).strip()
|
||||
if len(raw) >= 2 and ((raw[0] == raw[-1] == '"') or (raw[0] == raw[-1] == "'")):
|
||||
raw = raw[1:-1]
|
||||
lowered = raw.lower()
|
||||
if lowered == "true":
|
||||
return key, True
|
||||
if lowered == "false":
|
||||
return key, False
|
||||
if re.match(r"^-?(?:0|[1-9][0-9]*)$", raw):
|
||||
try:
|
||||
return key, int(raw)
|
||||
except Exception:
|
||||
pass
|
||||
if re.match(r"^-?(?:0|[1-9][0-9]*)[.][0-9]+$", raw):
|
||||
try:
|
||||
return key, float(raw)
|
||||
except Exception:
|
||||
pass
|
||||
return key, raw
|
||||
|
||||
def values_equal_for_grep(actual, expected):
|
||||
if actual == expected:
|
||||
return True
|
||||
if isinstance(actual, bool) or isinstance(expected, bool):
|
||||
return str(actual).lower() == str(expected).lower()
|
||||
if isinstance(actual, (int, float)) and isinstance(expected, (int, float)):
|
||||
return float(actual) == float(expected)
|
||||
return str(actual) == str(expected)
|
||||
|
||||
def grep_matches_item(item):
|
||||
return GREP is not None and grep_matches_text(json.dumps(item, ensure_ascii=False, sort_keys=True))
|
||||
if GREP is None:
|
||||
return False
|
||||
key, expected = parse_grep_key_value()
|
||||
if key is not None:
|
||||
if key == "name" and values_equal_for_grep(item.get("name"), expected):
|
||||
return True
|
||||
if key.startswith("resource.") and values_equal_for_grep(item.get(key.replace("resource.", "", 1)), expected):
|
||||
return True
|
||||
attrs = item.get("attributes", {}) if isinstance(item.get("attributes"), dict) else {}
|
||||
if key in attrs and values_equal_for_grep(attrs.get(key), expected):
|
||||
return True
|
||||
return grep_matches_text(json.dumps(item, ensure_ascii=False, sort_keys=True))
|
||||
|
||||
def span_matches_filters(item):
|
||||
attrs = item.get("attributes", {}) if isinstance(item.get("attributes"), dict) else {}
|
||||
@@ -641,7 +700,10 @@ def trace_summary(trace_id, meta, body, rc, stderr):
|
||||
for span in raw_spans:
|
||||
if not isinstance(span, dict):
|
||||
continue
|
||||
raw_attrs = attrs_to_dict(span.get("attributes"))
|
||||
item = compact_span(span, service, resource_attrs, scope_name)
|
||||
match_item = dict(item)
|
||||
match_item["attributes"] = raw_attrs
|
||||
attrs = item.get("attributes", {})
|
||||
if isinstance(attrs, dict) and isinstance(attrs.get("traceId"), str):
|
||||
business_trace_ids.add(attrs.get("traceId"))
|
||||
@@ -650,7 +712,7 @@ def trace_summary(trace_id, meta, body, rc, stderr):
|
||||
spans.append(item)
|
||||
if is_error_span(span, attrs if isinstance(attrs, dict) else {}):
|
||||
error_spans.append(item)
|
||||
if span_matches_filters(item) and (GREP is None or grep_matches_item(item)):
|
||||
if span_matches_filters(match_item) and (GREP is None or grep_matches_item(match_item)):
|
||||
matched_spans.append(item)
|
||||
return {
|
||||
"traceId": trace_id,
|
||||
@@ -728,6 +790,8 @@ payload = {
|
||||
"tempoQuery": QUERY,
|
||||
"pathFilter": PATH_FILTER,
|
||||
"statusFilter": STATUS_FILTER,
|
||||
"grepCoverage": None if GREP is None else "raw trace body, span name, status message, route and full span attributes inside scanned candidate traces",
|
||||
"grepQueryInference": "tempo-query-present" if QUERY is not None and GREP is not None else None,
|
||||
"businessTraceSearch": BUSINESS_TRACE_GREP,
|
||||
"limit": LIMIT,
|
||||
"candidateLimit": CANDIDATE_LIMIT,
|
||||
|
||||
Reference in New Issue
Block a user