@obsunified/collector 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js +9 -0
- package/dist/auth.js.map +1 -0
- package/dist/chunk-UGWDOGGH.js +319 -0
- package/dist/chunk-UGWDOGGH.js.map +1 -0
- package/dist/chunk-XKQTVIRE.js +671 -0
- package/dist/chunk-XKQTVIRE.js.map +1 -0
- package/dist/index.d.ts +1197 -0
- package/dist/index.js +17707 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest-auth-HFVO1zIp.d.ts +165 -0
- package/dist/openai-FJYOQ7WO.js +384 -0
- package/dist/openai-FJYOQ7WO.js.map +1 -0
- package/package.json +73 -0
- package/src/ai-parity/ai-parity.acceptance.test.ts +449 -0
- package/src/analyses/derive.ts +642 -0
- package/src/analyses/index.ts +50 -0
- package/src/analyses/investigations.ts +277 -0
- package/src/analyses/tier0.ts +568 -0
- package/src/auth/dashboard-auth.ts +269 -0
- package/src/auth/ingest-auth.ts +147 -0
- package/src/durable-objects/tail-hub.test.ts +32 -0
- package/src/durable-objects/tail-hub.ts +158 -0
- package/src/framework/collector.ts +467 -0
- package/src/framework/env.ts +90 -0
- package/src/framework/logger.ts +81 -0
- package/src/index.ts +211 -0
- package/src/lib/action-aggregates.ts +1031 -0
- package/src/lib/action-identity.ts +106 -0
- package/src/lib/actions-store.ts +397 -0
- package/src/lib/ai-pricing.ts +80 -0
- package/src/lib/ai-store/calls.ts +148 -0
- package/src/lib/ai-store/evaluations.test.ts +47 -0
- package/src/lib/ai-store/evaluations.ts +125 -0
- package/src/lib/ai-store/helpers.ts +37 -0
- package/src/lib/ai-store/retention.ts +17 -0
- package/src/lib/ai-store/sessions.ts +277 -0
- package/src/lib/ai-store/spans.ts +109 -0
- package/src/lib/ai-store/types.ts +112 -0
- package/src/lib/ai-store.ts +76 -0
- package/src/lib/alerts-store.ts +458 -0
- package/src/lib/analyses-runner.ts +439 -0
- package/src/lib/analyses-store.test.ts +243 -0
- package/src/lib/analyses-store.ts +454 -0
- package/src/lib/ask.test.ts +302 -0
- package/src/lib/ask.ts +390 -0
- package/src/lib/blob-store-s3.ts +213 -0
- package/src/lib/blob-store.ts +366 -0
- package/src/lib/eval-cases-store.test.ts +171 -0
- package/src/lib/eval-cases-store.ts +1033 -0
- package/src/lib/evidence-references.test.ts +10 -0
- package/src/lib/evidence-references.ts +684 -0
- package/src/lib/hash.ts +26 -0
- package/src/lib/identity-index/action-lookups.ts +805 -0
- package/src/lib/identity-index/constants.ts +1 -0
- package/src/lib/identity-index/key-lookups.ts +199 -0
- package/src/lib/identity-index/mappers.ts +349 -0
- package/src/lib/identity-index/types.ts +196 -0
- package/src/lib/identity-index/user-lookup.ts +137 -0
- package/src/lib/identity-index.test.ts +624 -0
- package/src/lib/identity-index.ts +85 -0
- package/src/lib/json.ts +39 -0
- package/src/lib/llm.ts +263 -0
- package/src/lib/logs-store.ts +188 -0
- package/src/lib/metrics-store.test.ts +189 -0
- package/src/lib/metrics-store.ts +312 -0
- package/src/lib/narrate-gate.test.ts +260 -0
- package/src/lib/narrate-gate.ts +253 -0
- package/src/lib/openai.test.ts +300 -0
- package/src/lib/openai.ts +512 -0
- package/src/lib/otlp.ts +212 -0
- package/src/lib/parse-pprof.test.ts +64 -0
- package/src/lib/parse-pprof.ts +184 -0
- package/src/lib/payload-capture.ts +22 -0
- package/src/lib/projects-store.ts +207 -0
- package/src/lib/propagation-metric.test.ts +196 -0
- package/src/lib/propagation-metric.ts +175 -0
- package/src/lib/sql-db-dialect.test.ts +22 -0
- package/src/lib/sql-db-postgres.ts +229 -0
- package/src/lib/sql-db.ts +166 -0
- package/src/lib/stage6.test.ts +216 -0
- package/src/lib/store/export.ts +65 -0
- package/src/lib/store/helpers.ts +367 -0
- package/src/lib/store/ingest.ts +68 -0
- package/src/lib/store/issues.ts +158 -0
- package/src/lib/store/overview.ts +127 -0
- package/src/lib/store/retention.ts +10 -0
- package/src/lib/store/service-map.ts +384 -0
- package/src/lib/store/trace-detail.bench.ts +76 -0
- package/src/lib/store/trace-detail.test.ts +410 -0
- package/src/lib/store/trace-detail.ts +427 -0
- package/src/lib/store/trace-queries.test.ts +29 -0
- package/src/lib/store/trace-queries.ts +89 -0
- package/src/lib/store-overview.test.ts +77 -0
- package/src/lib/store.ts +96 -0
- package/src/lib/tail-publisher.ts +64 -0
- package/src/lib/test-utils/mem-sql-db.test.ts +93 -0
- package/src/lib/test-utils/mem-sql-db.ts +86 -0
- package/src/lib/ua-parser.ts +61 -0
- package/src/lib/usage-store.ts +567 -0
- package/src/migrations/001_telemetry_spans.sql +28 -0
- package/src/migrations/002_usage_events.sql +24 -0
- package/src/migrations/003_usage_analytics_columns.sql +13 -0
- package/src/migrations/004_logs.sql +19 -0
- package/src/migrations/005_ai_calls.sql +25 -0
- package/src/migrations/006_user_profiles.sql +11 -0
- package/src/migrations/007_session_replay_metadata.sql +10 -0
- package/src/migrations/008_session_replay_storage_bytes.sql +1 -0
- package/src/migrations/009_projects_and_keys.sql +22 -0
- package/src/migrations/010_signal_project_id.sql +15 -0
- package/src/migrations/011_default_project_seed.sql +3 -0
- package/src/migrations/012_alert_rules.sql +17 -0
- package/src/migrations/013_alert_evaluations.sql +13 -0
- package/src/migrations/014_alert_state.sql +9 -0
- package/src/migrations/015_metrics.sql +58 -0
- package/src/migrations/016_metrics_exp_histogram_summary.sql +5 -0
- package/src/migrations/017_span_dropped_counts.sql +11 -0
- package/src/migrations/018_log_flags.sql +8 -0
- package/src/migrations/019_ai_span_payloads.sql +30 -0
- package/src/migrations/020_ai_span_evaluations.sql +33 -0
- package/src/migrations/021_ai_span_session.sql +13 -0
- package/src/migrations/022_span_log_session_id.sql +13 -0
- package/src/migrations/023_service_map_indexes.sql +32 -0
- package/src/migrations/024_analysis_results.sql +49 -0
- package/src/migrations/025_stage6_autopinning_alerts.sql +38 -0
- package/src/migrations/026_obs_dashboard_project_seed.sql +18 -0
- package/src/migrations/027_identity_propagation.sql +50 -0
- package/src/migrations/028_profile_blobs.sql +57 -0
- package/src/migrations/029_telemetry_sdk_name.sql +20 -0
- package/src/migrations/030_analysis_claim.sql +18 -0
- package/src/migrations/031_agent_action_graph.sql +154 -0
- package/src/migrations/032_payload_capture_flags.sql +8 -0
- package/src/migrations/033_eval_cases.sql +52 -0
- package/src/migrations/034_eval_case_results.sql +23 -0
- package/src/migrations/035_metric_exemplars.sql +33 -0
- package/src/migrations/036_trace_instrumentation_gaps.sql +19 -0
- package/src/migrations/037_eval_runs.sql +37 -0
- package/src/migrations/038_tool_call_evidence.sql +11 -0
- package/src/migrations/039_drop_trace_instrumentation_gaps.sql +11 -0
- package/src/migrations/040_evidence_retrieval_refs.sql +50 -0
- package/src/migrations-postgres/001_telemetry_spans.sql +33 -0
- package/src/migrations-postgres/002_usage_events.sql +24 -0
- package/src/migrations-postgres/003_usage_analytics_columns.sql +13 -0
- package/src/migrations-postgres/004_logs.sql +19 -0
- package/src/migrations-postgres/005_ai_calls.sql +25 -0
- package/src/migrations-postgres/006_user_profiles.sql +11 -0
- package/src/migrations-postgres/007_session_replay_metadata.sql +10 -0
- package/src/migrations-postgres/008_session_replay_storage_bytes.sql +2 -0
- package/src/migrations-postgres/009_projects_and_keys.sql +25 -0
- package/src/migrations-postgres/010_signal_project_id.sql +18 -0
- package/src/migrations-postgres/011_default_project_seed.sql +5 -0
- package/src/migrations-postgres/012_alert_rules.sql +17 -0
- package/src/migrations-postgres/013_alert_evaluations.sql +13 -0
- package/src/migrations-postgres/014_alert_state.sql +9 -0
- package/src/migrations-postgres/015_metrics.sql +47 -0
- package/src/migrations-postgres/016_metrics_exp_histogram_summary.sql +1 -0
- package/src/migrations-postgres/017_span_dropped_counts.sql +4 -0
- package/src/migrations-postgres/018_log_flags.sql +2 -0
- package/src/migrations-postgres/019_ai_span_payloads.sql +18 -0
- package/src/migrations-postgres/020_ai_span_evaluations.sql +21 -0
- package/src/migrations-postgres/021_ai_span_session.sql +7 -0
- package/src/migrations-postgres/022_span_log_session_id.sql +7 -0
- package/src/migrations-postgres/023_service_map_indexes.sql +9 -0
- package/src/migrations-postgres/024_analysis_results.sql +41 -0
- package/src/migrations-postgres/025_stage6_autopinning_alerts.sql +17 -0
- package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +14 -0
- package/src/migrations-postgres/027_identity_propagation.sql +27 -0
- package/src/migrations-postgres/028_profile_blobs.sql +48 -0
- package/src/migrations-postgres/029_telemetry_sdk_name.sql +5 -0
- package/src/migrations-postgres/030_analysis_claim.sql +5 -0
- package/src/migrations-postgres/031_agent_action_graph.sql +190 -0
- package/src/migrations-postgres/032_payload_capture_flags.sql +8 -0
- package/src/migrations-postgres/033_eval_cases.sql +55 -0
- package/src/migrations-postgres/034_eval_case_results.sql +26 -0
- package/src/migrations-postgres/035_metric_exemplars.sql +31 -0
- package/src/migrations-postgres/036_trace_instrumentation_gaps.sql +19 -0
- package/src/migrations-postgres/037_eval_runs.sql +40 -0
- package/src/migrations-postgres/038_tool_call_evidence.sql +11 -0
- package/src/migrations-postgres/039_drop_trace_instrumentation_gaps.sql +11 -0
- package/src/migrations-postgres/040_evidence_retrieval_refs.sql +47 -0
- package/src/migrations-postgres/README.md +40 -0
- package/src/otlp/acceptance.test.ts +373 -0
- package/src/otlp/decode/body.ts +127 -0
- package/src/otlp/decode/logs.ts +96 -0
- package/src/otlp/decode/metrics.ts +402 -0
- package/src/otlp/decode/traces.ts +92 -0
- package/src/otlp/decode/values.ts +150 -0
- package/src/otlp/decode.test.ts +800 -0
- package/src/otlp/decode.ts +11 -0
- package/src/otlp/gen/opentelemetry/proto/collector/logs/v1/logs_service_pb.ts +132 -0
- package/src/otlp/gen/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.ts +158 -0
- package/src/otlp/gen/opentelemetry/proto/collector/trace/v1/trace_service_pb.ts +158 -0
- package/src/otlp/gen/opentelemetry/proto/common/v1/common_pb.ts +289 -0
- package/src/otlp/gen/opentelemetry/proto/logs/v1/logs_pb.ts +397 -0
- package/src/otlp/gen/opentelemetry/proto/metrics/v1/metrics_pb.ts +1329 -0
- package/src/otlp/gen/opentelemetry/proto/resource/v1/resource_pb.ts +86 -0
- package/src/otlp/gen/opentelemetry/proto/trace/v1/trace_pb.ts +710 -0
- package/src/otlp/response.test.ts +158 -0
- package/src/otlp/response.ts +129 -0
- package/src/plugins/_context.ts +15 -0
- package/src/plugins/action-graph-processor/enrichers.ts +45 -0
- package/src/plugins/action-graph-processor/redaction.ts +84 -0
- package/src/plugins/action-graph-processor.test.ts +1023 -0
- package/src/plugins/action-graph-processor.ts +1172 -0
- package/src/plugins/action-routes.test.ts +716 -0
- package/src/plugins/action-routes.ts +343 -0
- package/src/plugins/ai-receiver.ts +254 -0
- package/src/plugins/ai-span-payloads-processor.test.ts +90 -0
- package/src/plugins/ai-span-payloads-processor.ts +180 -0
- package/src/plugins/alerts-evaluator.test.ts +91 -0
- package/src/plugins/alerts-evaluator.ts +328 -0
- package/src/plugins/alerts-routes.ts +101 -0
- package/src/plugins/analyses-routes.ts +138 -0
- package/src/plugins/ask-routes.ts +182 -0
- package/src/plugins/bot-filter.ts +19 -0
- package/src/plugins/connected-routes/manifest.ts +531 -0
- package/src/plugins/connected-routes.test.ts +1791 -0
- package/src/plugins/connected-routes.ts +1103 -0
- package/src/plugins/dashboard-routes.ts +41 -0
- package/src/plugins/default-span-enrichment.ts +92 -0
- package/src/plugins/eval-cases-routes.test.ts +391 -0
- package/src/plugins/eval-cases-routes.ts +529 -0
- package/src/plugins/evidence-retrieval-routes.benchmark.test.ts +245 -0
- package/src/plugins/evidence-retrieval-routes.test.ts +932 -0
- package/src/plugins/evidence-retrieval-routes.ts +2179 -0
- package/src/plugins/gen-ai-normalizer.test.ts +680 -0
- package/src/plugins/gen-ai-normalizer.ts +302 -0
- package/src/plugins/identity-receiver.ts +96 -0
- package/src/plugins/issue-enrichment.ts +132 -0
- package/src/plugins/issue-insights.ts +81 -0
- package/src/plugins/logs-receiver.ts +160 -0
- package/src/plugins/metrics-receiver.ts +91 -0
- package/src/plugins/onboarding-routes.ts +54 -0
- package/src/plugins/otlp-receiver.ts +124 -0
- package/src/plugins/platform-routes.ts +165 -0
- package/src/plugins/profile-routes.ts +468 -0
- package/src/plugins/projects-routes.ts +92 -0
- package/src/plugins/query-routes.ts +220 -0
- package/src/plugins/redaction-processor.ts +132 -0
- package/src/plugins/replay-query-routes.test.ts +38 -0
- package/src/plugins/replay-query-routes.ts +199 -0
- package/src/plugins/replay-receiver.ts +81 -0
- package/src/plugins/tail-routes.test.ts +47 -0
- package/src/plugins/tail-routes.ts +42 -0
- package/src/plugins/timeline-routes.ts +244 -0
- package/src/plugins/ua-enrichment.ts +27 -0
- package/src/plugins/usage-normalization.ts +46 -0
- package/src/plugins/usage-privacy.ts +55 -0
- package/src/plugins/usage-query-routes.ts +102 -0
- package/src/plugins/usage-receiver.ts +129 -0
- package/src/plugins/users-query-routes.ts +78 -0
- package/src/plugins/utm-enrichment.ts +36 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 0002 Stage 4 — investigation templates.
|
|
3
|
+
*
|
|
4
|
+
* The RFC originally targeted a Python sidecar for these (Stage 2). We
|
|
5
|
+
* skipped Stage 2; investigations here are pure SQL (multiple result-sets
|
|
6
|
+
* stitched into one structured `payload.evidence` blob) executed by the
|
|
7
|
+
* existing runner. SQLite's window functions + json builders cover what
|
|
8
|
+
* Polars would do for the cohort/funnel/anomaly shapes we ship.
|
|
9
|
+
*
|
|
10
|
+
* Convention used by `InvestigationPage`:
|
|
11
|
+
*
|
|
12
|
+
* payload.evidence = {
|
|
13
|
+
* [section_name]: {
|
|
14
|
+
* title?: string,
|
|
15
|
+
* headers: string[],
|
|
16
|
+
* rows: Array<Array<string | number | null>>,
|
|
17
|
+
* note?: string,
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* The runner shapes status / primary / baseline / delta the same way as
|
|
22
|
+
* tile analyses; the page UI renders the headline numbers above the
|
|
23
|
+
* narrative and tables below.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { AnalysisDefinition } from "@obsunified/types";
|
|
27
|
+
|
|
28
|
+
// ── error_top_offenders ────────────────────────────────────────────────────
|
|
29
|
+
// Cohort-comparison investigation. "Which services contributed the most
|
|
30
|
+
// errors in the last 5 minutes vs the trailing hour, and is the mix
|
|
31
|
+
// shifting?" Status: critical if current top-3 service error count > 50;
|
|
32
|
+
// warn if > 10; ok otherwise. Primary value is current top-1 error count.
|
|
33
|
+
const errorTopOffenders: AnalysisDefinition = {
|
|
34
|
+
id: "investigate.error_top_offenders",
|
|
35
|
+
title: "Error top offenders",
|
|
36
|
+
group: "Health",
|
|
37
|
+
source: "tier0",
|
|
38
|
+
view: "page",
|
|
39
|
+
refreshSeconds: 300,
|
|
40
|
+
narrate: {
|
|
41
|
+
prompt:
|
|
42
|
+
"Summarize which services dominate errors right now and how the mix shifted vs the last hour. If one service moved sharply, name it. Time anchor.",
|
|
43
|
+
only_when: "always",
|
|
44
|
+
},
|
|
45
|
+
sql: `
|
|
46
|
+
WITH cur AS (
|
|
47
|
+
SELECT COALESCE(service_name, '<unknown>') AS service,
|
|
48
|
+
COUNT(*) AS errors
|
|
49
|
+
FROM telemetry_spans
|
|
50
|
+
WHERE project_id = '{{PROJECT_ID}}'
|
|
51
|
+
AND received_at >= datetime('now', '-5 minutes')
|
|
52
|
+
AND status_code = 2
|
|
53
|
+
GROUP BY service_name
|
|
54
|
+
ORDER BY errors DESC
|
|
55
|
+
LIMIT 5
|
|
56
|
+
),
|
|
57
|
+
prev AS (
|
|
58
|
+
SELECT COALESCE(service_name, '<unknown>') AS service,
|
|
59
|
+
COUNT(*) AS errors
|
|
60
|
+
FROM telemetry_spans
|
|
61
|
+
WHERE project_id = '{{PROJECT_ID}}'
|
|
62
|
+
AND received_at >= datetime('now', '-1 hour')
|
|
63
|
+
AND received_at < datetime('now', '-5 minutes')
|
|
64
|
+
AND status_code = 2
|
|
65
|
+
GROUP BY service_name
|
|
66
|
+
ORDER BY errors DESC
|
|
67
|
+
LIMIT 5
|
|
68
|
+
),
|
|
69
|
+
top1 AS (
|
|
70
|
+
SELECT errors AS top_errors FROM cur LIMIT 1
|
|
71
|
+
)
|
|
72
|
+
SELECT
|
|
73
|
+
CASE
|
|
74
|
+
WHEN COALESCE((SELECT top_errors FROM top1), 0) > 50 THEN 'critical'
|
|
75
|
+
WHEN COALESCE((SELECT top_errors FROM top1), 0) > 10 THEN 'warn'
|
|
76
|
+
ELSE 'ok'
|
|
77
|
+
END AS status,
|
|
78
|
+
COALESCE((SELECT top_errors FROM top1), 0) AS primary_value,
|
|
79
|
+
(SELECT errors FROM prev LIMIT 1) AS baseline_value,
|
|
80
|
+
(SELECT json_object(
|
|
81
|
+
'window', '5m',
|
|
82
|
+
'baselineWindow', '5m–60m ago',
|
|
83
|
+
'evidence', json_object(
|
|
84
|
+
'current_top_services', json_object(
|
|
85
|
+
'title', 'Top error services — last 5 minutes',
|
|
86
|
+
'headers', json_array('service', 'errors'),
|
|
87
|
+
'rows', (
|
|
88
|
+
SELECT json_group_array(json_array(service, errors))
|
|
89
|
+
FROM cur
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
'baseline_top_services', json_object(
|
|
93
|
+
'title', 'Top error services — 5–60 minutes ago',
|
|
94
|
+
'headers', json_array('service', 'errors'),
|
|
95
|
+
'rows', (
|
|
96
|
+
SELECT json_group_array(json_array(service, errors))
|
|
97
|
+
FROM prev
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
)) AS payload
|
|
102
|
+
`,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// ── latency_outlier_attribution ───────────────────────────────────────────
|
|
106
|
+
// Funnel-style investigation: of the slow tail (last 15min), which
|
|
107
|
+
// service+span combinations dominate? Helps users go from "p95 is up" to
|
|
108
|
+
// "checkout.submit is the offender" in one click.
|
|
109
|
+
const latencyOutlierAttribution: AnalysisDefinition = {
|
|
110
|
+
id: "investigate.latency_outlier_attribution",
|
|
111
|
+
title: "Latency outlier attribution",
|
|
112
|
+
group: "Health",
|
|
113
|
+
source: "tier0",
|
|
114
|
+
view: "page",
|
|
115
|
+
refreshSeconds: 300,
|
|
116
|
+
narrate: {
|
|
117
|
+
prompt:
|
|
118
|
+
"Identify which span(s) dominate the slow tail in the last 15 minutes. Quote concrete duration numbers and span names from the evidence. Time anchor.",
|
|
119
|
+
only_when: "always",
|
|
120
|
+
},
|
|
121
|
+
sql: `
|
|
122
|
+
WITH ranked AS (
|
|
123
|
+
SELECT
|
|
124
|
+
duration_ms,
|
|
125
|
+
COALESCE(service_name, '<unknown>') AS service,
|
|
126
|
+
COALESCE(span_name, '<unknown>') AS span,
|
|
127
|
+
ROW_NUMBER() OVER (ORDER BY duration_ms DESC) AS rn,
|
|
128
|
+
COUNT(*) OVER () AS n
|
|
129
|
+
FROM telemetry_spans
|
|
130
|
+
WHERE project_id = '{{PROJECT_ID}}'
|
|
131
|
+
AND received_at >= datetime('now', '-15 minutes')
|
|
132
|
+
),
|
|
133
|
+
p95_cut AS (
|
|
134
|
+
-- Conservatively call "tail" the slowest 5% of the window.
|
|
135
|
+
SELECT MIN(duration_ms) AS p95
|
|
136
|
+
FROM ranked
|
|
137
|
+
WHERE rn <= MAX(1, (n + 19) / 20)
|
|
138
|
+
),
|
|
139
|
+
offenders AS (
|
|
140
|
+
SELECT service, span,
|
|
141
|
+
COUNT(*) AS tail_count,
|
|
142
|
+
MAX(duration_ms) AS max_ms,
|
|
143
|
+
ROUND(AVG(duration_ms), 1) AS avg_ms
|
|
144
|
+
FROM ranked, p95_cut
|
|
145
|
+
WHERE duration_ms >= COALESCE(p95_cut.p95, 0)
|
|
146
|
+
GROUP BY service, span
|
|
147
|
+
ORDER BY tail_count DESC
|
|
148
|
+
LIMIT 8
|
|
149
|
+
),
|
|
150
|
+
top_max AS (
|
|
151
|
+
SELECT max_ms FROM offenders ORDER BY max_ms DESC LIMIT 1
|
|
152
|
+
)
|
|
153
|
+
SELECT
|
|
154
|
+
CASE
|
|
155
|
+
WHEN COALESCE((SELECT max_ms FROM top_max), 0) > 5000 THEN 'critical'
|
|
156
|
+
WHEN COALESCE((SELECT max_ms FROM top_max), 0) > 1000 THEN 'warn'
|
|
157
|
+
ELSE 'ok'
|
|
158
|
+
END AS status,
|
|
159
|
+
COALESCE((SELECT max_ms FROM top_max), 0) AS primary_value,
|
|
160
|
+
(SELECT p95 FROM p95_cut) AS baseline_value,
|
|
161
|
+
(SELECT json_object(
|
|
162
|
+
'window', '15m',
|
|
163
|
+
'unit', 'ms',
|
|
164
|
+
'evidence', json_object(
|
|
165
|
+
'tail_offenders', json_object(
|
|
166
|
+
'title', 'Top tail offenders — slowest 5% of last 15 minutes',
|
|
167
|
+
'headers', json_array('service', 'span', 'count_in_tail', 'max_ms', 'avg_ms'),
|
|
168
|
+
'rows', (
|
|
169
|
+
SELECT json_group_array(json_array(service, span, tail_count, max_ms, avg_ms))
|
|
170
|
+
FROM offenders
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
)) AS payload
|
|
175
|
+
`,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// ── log_anomaly_summary ────────────────────────────────────────────────────
|
|
179
|
+
// Anomaly investigation: which ERROR-severity logger names showed up in
|
|
180
|
+
// the last 5 minutes that weren't present in the prior hour? These are
|
|
181
|
+
// the "first time we've ever seen this fail" cases — usually the most
|
|
182
|
+
// interesting line in an incident.
|
|
183
|
+
const logAnomalySummary: AnalysisDefinition = {
|
|
184
|
+
id: "investigate.log_anomaly_summary",
|
|
185
|
+
title: "Log anomaly summary",
|
|
186
|
+
group: "Health",
|
|
187
|
+
source: "tier0",
|
|
188
|
+
view: "page",
|
|
189
|
+
refreshSeconds: 300,
|
|
190
|
+
narrate: {
|
|
191
|
+
prompt:
|
|
192
|
+
"List the new ERROR loggers (those absent from the prior hour). If any look like 'never seen before', call it out. Quote one representative message per logger. Time anchor.",
|
|
193
|
+
only_when: "always",
|
|
194
|
+
},
|
|
195
|
+
sql: `
|
|
196
|
+
WITH recent AS (
|
|
197
|
+
-- Correlated subquery for sample_message uses the table alias
|
|
198
|
+
-- l1 so SQLite resolves the outer column reference; the AS alias
|
|
199
|
+
-- 'logger' defined in the SELECT list isn't visible inside a
|
|
200
|
+
-- correlated subquery in SQLite.
|
|
201
|
+
SELECT COALESCE(l1.logger_name, '<unknown>') AS logger,
|
|
202
|
+
COUNT(*) AS occurrences,
|
|
203
|
+
(SELECT l2.message FROM logs l2
|
|
204
|
+
WHERE l2.project_id = '{{PROJECT_ID}}'
|
|
205
|
+
AND COALESCE(l2.logger_name, '<unknown>') = COALESCE(l1.logger_name, '<unknown>')
|
|
206
|
+
AND l2.received_at >= datetime('now', '-5 minutes')
|
|
207
|
+
AND l2.severity_number >= 17
|
|
208
|
+
ORDER BY l2.received_at DESC LIMIT 1) AS sample_message
|
|
209
|
+
FROM logs l1
|
|
210
|
+
WHERE l1.project_id = '{{PROJECT_ID}}'
|
|
211
|
+
AND l1.received_at >= datetime('now', '-5 minutes')
|
|
212
|
+
AND l1.severity_number >= 17
|
|
213
|
+
GROUP BY l1.logger_name
|
|
214
|
+
),
|
|
215
|
+
baseline AS (
|
|
216
|
+
SELECT DISTINCT COALESCE(logger_name, '<unknown>') AS logger
|
|
217
|
+
FROM logs
|
|
218
|
+
WHERE project_id = '{{PROJECT_ID}}'
|
|
219
|
+
AND received_at >= datetime('now', '-1 hour')
|
|
220
|
+
AND received_at < datetime('now', '-5 minutes')
|
|
221
|
+
AND severity_number >= 17
|
|
222
|
+
),
|
|
223
|
+
newcomers AS (
|
|
224
|
+
SELECT recent.*
|
|
225
|
+
FROM recent
|
|
226
|
+
LEFT JOIN baseline ON baseline.logger = recent.logger
|
|
227
|
+
WHERE baseline.logger IS NULL
|
|
228
|
+
ORDER BY recent.occurrences DESC
|
|
229
|
+
LIMIT 8
|
|
230
|
+
),
|
|
231
|
+
all_recent AS (
|
|
232
|
+
SELECT recent.*,
|
|
233
|
+
CASE WHEN baseline.logger IS NULL THEN 1 ELSE 0 END AS is_new
|
|
234
|
+
FROM recent
|
|
235
|
+
LEFT JOIN baseline ON baseline.logger = recent.logger
|
|
236
|
+
ORDER BY is_new DESC, occurrences DESC
|
|
237
|
+
LIMIT 12
|
|
238
|
+
)
|
|
239
|
+
SELECT
|
|
240
|
+
CASE
|
|
241
|
+
WHEN (SELECT COUNT(*) FROM newcomers) >= 3 THEN 'critical'
|
|
242
|
+
WHEN (SELECT COUNT(*) FROM newcomers) >= 1 THEN 'warn'
|
|
243
|
+
ELSE 'ok'
|
|
244
|
+
END AS status,
|
|
245
|
+
(SELECT COUNT(*) FROM newcomers) AS primary_value,
|
|
246
|
+
(SELECT COUNT(*) FROM baseline) AS baseline_value,
|
|
247
|
+
(SELECT json_object(
|
|
248
|
+
'window', '5m',
|
|
249
|
+
'baselineWindow', '5–60m ago',
|
|
250
|
+
'evidence', json_object(
|
|
251
|
+
'new_error_loggers', json_object(
|
|
252
|
+
'title', 'New ERROR loggers — first seen in last 5 minutes',
|
|
253
|
+
'headers', json_array('logger', 'occurrences', 'sample_message'),
|
|
254
|
+
'note', 'Loggers absent from the prior 5–60 minute window.',
|
|
255
|
+
'rows', (
|
|
256
|
+
SELECT json_group_array(json_array(logger, occurrences, sample_message))
|
|
257
|
+
FROM newcomers
|
|
258
|
+
)
|
|
259
|
+
),
|
|
260
|
+
'all_recent_loggers', json_object(
|
|
261
|
+
'title', 'All ERROR loggers — last 5 minutes',
|
|
262
|
+
'headers', json_array('logger', 'occurrences', 'is_new', 'sample_message'),
|
|
263
|
+
'rows', (
|
|
264
|
+
SELECT json_group_array(json_array(logger, occurrences, is_new, sample_message))
|
|
265
|
+
FROM all_recent
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
)) AS payload
|
|
270
|
+
`,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export const INVESTIGATION_ANALYSES: AnalysisDefinition[] = [
|
|
274
|
+
errorTopOffenders,
|
|
275
|
+
latencyOutlierAttribution,
|
|
276
|
+
logAnomalySummary,
|
|
277
|
+
];
|