@obsunified/collector 2.0.1 → 2.1.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.
Files changed (107) hide show
  1. package/dist/auth.d.ts +1 -1
  2. package/dist/auth.js +1 -1
  3. package/dist/{chunk-UGWDOGGH.js → chunk-KMEZKGSO.js} +22 -10
  4. package/dist/chunk-KMEZKGSO.js.map +1 -0
  5. package/dist/index.d.ts +322 -26
  6. package/dist/index.js +1478 -415
  7. package/dist/index.js.map +1 -1
  8. package/dist/{ingest-auth-HFVO1zIp.d.ts → ingest-auth-C5-8Mly5.d.ts} +9 -0
  9. package/package.json +5 -5
  10. package/src/analyses/derive.ts +26 -25
  11. package/src/analyses/investigations.ts +8 -8
  12. package/src/analyses/tier0.ts +22 -22
  13. package/src/auth/dashboard-auth.test.ts +791 -0
  14. package/src/auth/dashboard-auth.ts +187 -38
  15. package/src/auth/login-rate-limit.test.ts +129 -0
  16. package/src/auth/login-rate-limit.ts +143 -0
  17. package/src/framework/body-limit.test.ts +165 -0
  18. package/src/framework/collector.ts +355 -115
  19. package/src/framework/env.ts +9 -0
  20. package/src/framework/limits.ts +31 -0
  21. package/src/framework/scheduled-handlers.test.ts +342 -0
  22. package/src/index.ts +16 -1
  23. package/src/lib/action-retention.test.ts +472 -0
  24. package/src/lib/action-retention.ts +420 -0
  25. package/src/lib/actions-store.ts +6 -12
  26. package/src/lib/ai-store/calls.ts +2 -2
  27. package/src/lib/ai-store/sessions.ts +3 -3
  28. package/src/lib/ai-store/spans.ts +1 -1
  29. package/src/lib/ai-store/types.ts +2 -1
  30. package/src/lib/alerts-store.test.ts +153 -0
  31. package/src/lib/alerts-store.ts +122 -21
  32. package/src/lib/analyses-runner.test.ts +75 -0
  33. package/src/lib/analyses-runner.ts +60 -3
  34. package/src/lib/analyses-store-latest.test.ts +61 -0
  35. package/src/lib/analyses-store.ts +17 -10
  36. package/src/lib/blob-store.ts +1 -1
  37. package/src/lib/housekeeping.test.ts +73 -0
  38. package/src/lib/housekeeping.ts +93 -0
  39. package/src/lib/identity-index/action-lookups.ts +6 -6
  40. package/src/lib/identity-index/key-lookups.ts +2 -2
  41. package/src/lib/identity-index/user-lookup.ts +8 -5
  42. package/src/lib/identity-index.test.ts +2 -3
  43. package/src/lib/metrics-store-series.test.ts +99 -0
  44. package/src/lib/metrics-store.test.ts +23 -0
  45. package/src/lib/metrics-store.ts +27 -3
  46. package/src/lib/projects-store.test.ts +25 -0
  47. package/src/lib/projects-store.ts +4 -3
  48. package/src/lib/sql-db-dialect.test.ts +12 -3
  49. package/src/lib/sql-db-postgres.integration.test.ts +1225 -0
  50. package/src/lib/sql-db-postgres.test.ts +314 -0
  51. package/src/lib/sql-db-postgres.ts +392 -54
  52. package/src/lib/sql-db-sqlite-time-windows.test.ts +194 -0
  53. package/src/lib/sql-db.ts +40 -6
  54. package/src/lib/test-utils/action-graph-fixtures.ts +307 -0
  55. package/src/lib/test-utils/node-sqlite-db.ts +188 -0
  56. package/src/lib/webhook-url.test.ts +122 -0
  57. package/src/lib/webhook-url.ts +155 -0
  58. package/src/migrations/041_ingest_key_hash_text.sql +5 -0
  59. package/src/migrations/042_session_replay_project_composite_pk.sql +52 -0
  60. package/src/migrations/043_metric_exemplars_text_timestamps.sql +5 -0
  61. package/src/migrations/044_actions_started_at_index.sql +10 -0
  62. package/src/migrations/045_user_profiles_project_composite_pk.sql +40 -0
  63. package/src/migrations/046_action_graph_project_composite_pk.sql +255 -0
  64. package/src/migrations/047_ai_span_payloads_project_composite_pk.sql +61 -0
  65. package/src/migrations/048_telemetry_spans_project_composite_pk.sql +92 -0
  66. package/src/migrations/049_alert_state_notified.sql +17 -0
  67. package/src/migrations/alert-state-notified.test.ts +111 -0
  68. package/src/migrations/parity.test.ts +64 -0
  69. package/src/migrations/project-scoped-keys.test.ts +432 -0
  70. package/src/migrations-postgres/009_projects_and_keys.sql +4 -4
  71. package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +1 -1
  72. package/src/migrations-postgres/035_metric_exemplars.sql +5 -2
  73. package/src/migrations-postgres/041_ingest_key_hash_text.sql +32 -0
  74. package/src/migrations-postgres/042_session_replay_project_composite_pk.sql +16 -0
  75. package/src/migrations-postgres/043_metric_exemplars_text_timestamps.sql +47 -0
  76. package/src/migrations-postgres/044_actions_started_at_index.sql +8 -0
  77. package/src/migrations-postgres/045_user_profiles_project_composite_pk.sql +33 -0
  78. package/src/migrations-postgres/046_action_graph_project_composite_pk.sql +39 -0
  79. package/src/migrations-postgres/047_ai_span_payloads_project_composite_pk.sql +30 -0
  80. package/src/migrations-postgres/048_telemetry_spans_project_composite_pk.sql +37 -0
  81. package/src/migrations-postgres/049_alert_state_notified.sql +10 -0
  82. package/src/migrations-postgres/README.md +58 -5
  83. package/src/otlp/decode/body.test.ts +76 -0
  84. package/src/otlp/decode/body.ts +67 -6
  85. package/src/plugins/_json.ts +21 -0
  86. package/src/plugins/action-graph-processor.ts +6 -12
  87. package/src/plugins/ai-receiver.ts +5 -12
  88. package/src/plugins/ai-span-payloads-processor.ts +1 -2
  89. package/src/plugins/alerts-evaluator-retention.test.ts +170 -0
  90. package/src/plugins/alerts-evaluator.test.ts +464 -3
  91. package/src/plugins/alerts-evaluator.ts +165 -41
  92. package/src/plugins/alerts-routes.ts +10 -6
  93. package/src/plugins/ask-routes.ts +4 -5
  94. package/src/plugins/connected-routes.test.ts +106 -0
  95. package/src/plugins/connected-routes.ts +14 -5
  96. package/src/plugins/dashboard-routes.test.ts +120 -0
  97. package/src/plugins/dashboard-routes.ts +11 -2
  98. package/src/plugins/eval-cases-routes.ts +8 -17
  99. package/src/plugins/identity-receiver.ts +9 -11
  100. package/src/plugins/json-body.test.ts +91 -0
  101. package/src/plugins/onboarding-routes.test.ts +105 -0
  102. package/src/plugins/onboarding-routes.ts +26 -11
  103. package/src/plugins/projects-routes.ts +2 -1
  104. package/src/plugins/replay-isolation.test.ts +922 -0
  105. package/src/plugins/replay-receiver.ts +8 -8
  106. package/src/plugins/usage-receiver.ts +3 -6
  107. package/dist/chunk-UGWDOGGH.js.map +0 -1
@@ -73,10 +73,19 @@ interface CollectorEnv {
73
73
  INGEST_KEY?: string;
74
74
  /** Password for dashboard login */
75
75
  DASHBOARD_PASSWORD?: string;
76
+ /** Programmatic read/admin bearer token for /internal/* endpoints (e.g. MCP server) */
77
+ DASHBOARD_TOKEN?: string;
76
78
  /** Comma-separated allowed origins for CORS (e.g. "https://my-app.com,https://staging.my-app.com") */
77
79
  ALLOWED_ORIGINS?: string;
78
80
  /** Set to "true" to allow unauthenticated ingest (local dev only) */
79
81
  ALLOW_UNAUTHENTICATED?: string;
82
+ /** Raw request body cap in bytes for /v1/* and /internal/* (default 10 MiB). */
83
+ MAX_BODY_BYTES?: string;
84
+ /**
85
+ * Set to "true" to let alert webhooks target loopback / private / link-local
86
+ * hosts (self-hosted receivers). Off by default to block SSRF.
87
+ */
88
+ ALERT_WEBHOOK_ALLOW_PRIVATE?: string;
80
89
  TELEMETRY_REDACT_FIELDS?: string;
81
90
  /**
82
91
  * Local/test escape hatch for payload capture. Production should prefer the
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@obsunified/collector",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Plugin-based observability collector runtime with pluggable SQL/blob storage.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/obs-unified/obs-unified.git",
8
+ "url": "https://github.com/dev-sawan-ruparel/obs-unified.git",
9
9
  "directory": "packages/obs-collector"
10
10
  },
11
11
  "keywords": [
@@ -38,8 +38,8 @@
38
38
  "sideEffects": false,
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.11.0",
41
- "@obsunified/pprof-decoder": "2.0.1",
42
- "@obsunified/types": "2.0.1"
41
+ "@obsunified/pprof-decoder": "2.0.2",
42
+ "@obsunified/types": "2.0.2"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "hono": "^4.7.0",
@@ -54,7 +54,7 @@
54
54
  "@bufbuild/buf": "^1.68.1",
55
55
  "@bufbuild/protoc-gen-es": "^2.11.0",
56
56
  "@types/pg": "^8.11.10",
57
- "hono": "^4.12.11",
57
+ "hono": "^4.13.13",
58
58
  "pg": "^8.13.1",
59
59
  "tsup": "^8.3.5",
60
60
  "typescript": "^6.0.2"
@@ -59,7 +59,7 @@ const fetchShape = async (
59
59
  FROM telemetry_spans
60
60
  WHERE project_id = ?
61
61
  AND service_name IS NOT NULL
62
- AND received_at >= datetime('now', '-1 hour')`,
62
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')`,
63
63
  )
64
64
  .bind(projectId)
65
65
  .all<{ service_name: string }>();
@@ -83,7 +83,7 @@ const fetchShape = async (
83
83
  AND parent.trace_id = child.trace_id
84
84
  AND parent.span_id = child.parent_span_id
85
85
  WHERE child.project_id = ?
86
- AND child.received_at >= datetime('now', '-1 hour')
86
+ AND child.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
87
87
  AND parent.service_name IS NOT NULL
88
88
  AND child.service_name IS NOT NULL
89
89
  AND parent.service_name <> child.service_name`,
@@ -101,7 +101,7 @@ const fetchShape = async (
101
101
  `SELECT DISTINCT json_extract(attributes_json, '$."messaging.destination"') AS topic
102
102
  FROM telemetry_spans
103
103
  WHERE project_id = ?
104
- AND received_at >= datetime('now', '-1 hour')
104
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
105
105
  AND json_extract(attributes_json, '$."messaging.destination"') IS NOT NULL`,
106
106
  )
107
107
  .bind(projectId)
@@ -121,7 +121,7 @@ const fetchShape = async (
121
121
  `SELECT 1
122
122
  FROM telemetry_spans
123
123
  WHERE project_id = ?
124
- AND received_at >= datetime('now', '-1 hour')
124
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
125
125
  AND (
126
126
  json_extract(attributes_json, '$."openinference.span.kind"') = 'LLM'
127
127
  OR json_extract(attributes_json, '$."gen_ai.system"') IS NOT NULL
@@ -194,7 +194,7 @@ const makeServiceErrorRate = (
194
194
  FROM telemetry_spans
195
195
  WHERE project_id = '${sqlEscape(projectId)}'
196
196
  AND service_name = '${sqlEscape(service)}'
197
- AND received_at >= datetime('now', '-5 minutes')
197
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
198
198
  ),
199
199
  base_window AS (
200
200
  SELECT
@@ -203,8 +203,8 @@ const makeServiceErrorRate = (
203
203
  FROM telemetry_spans
204
204
  WHERE project_id = '${sqlEscape(projectId)}'
205
205
  AND service_name = '${sqlEscape(service)}'
206
- AND received_at >= datetime('now', '-1 hour')
207
- AND received_at < datetime('now', '-5 minutes')
206
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
207
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
208
208
  ),
209
209
  spark AS (
210
210
  SELECT
@@ -214,7 +214,7 @@ const makeServiceErrorRate = (
214
214
  FROM telemetry_spans
215
215
  WHERE project_id = '${sqlEscape(projectId)}'
216
216
  AND service_name = '${sqlEscape(service)}'
217
- AND received_at >= datetime('now', '-30 minutes')
217
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
218
218
  GROUP BY minute
219
219
  ORDER BY minute ASC
220
220
  )
@@ -280,7 +280,7 @@ const makeServiceLatencyP95 = (
280
280
  FROM telemetry_spans
281
281
  WHERE project_id = '${sqlEscape(projectId)}'
282
282
  AND service_name = '${sqlEscape(service)}'
283
- AND received_at >= datetime('now', '-1 hour')
283
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
284
284
  ),
285
285
  base AS (
286
286
  SELECT duration_ms,
@@ -289,8 +289,8 @@ const makeServiceLatencyP95 = (
289
289
  FROM telemetry_spans
290
290
  WHERE project_id = '${sqlEscape(projectId)}'
291
291
  AND service_name = '${sqlEscape(service)}'
292
- AND received_at >= datetime('now', '-25 hours')
293
- AND received_at < datetime('now', '-24 hours')
292
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-25 hours')
293
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-24 hours')
294
294
  ),
295
295
  cur_p95 AS (
296
296
  SELECT duration_ms FROM cur
@@ -360,7 +360,7 @@ const makeDependencyHealth = (
360
360
  WHERE child.project_id = '${sqlEscape(projectId)}'
361
361
  AND parent.service_name = '${sqlEscape(source)}'
362
362
  AND child.service_name = '${sqlEscape(target)}'
363
- AND child.received_at >= datetime('now', '-5 minutes')
363
+ AND child.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
364
364
  ),
365
365
  edge_base AS (
366
366
  SELECT child.status_code AS sc
@@ -372,8 +372,8 @@ const makeDependencyHealth = (
372
372
  WHERE child.project_id = '${sqlEscape(projectId)}'
373
373
  AND parent.service_name = '${sqlEscape(source)}'
374
374
  AND child.service_name = '${sqlEscape(target)}'
375
- AND child.received_at >= datetime('now', '-1 hour')
376
- AND child.received_at < datetime('now', '-5 minutes')
375
+ AND child.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
376
+ AND child.received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
377
377
  ),
378
378
  now_agg AS (
379
379
  SELECT COUNT(*) AS total,
@@ -438,7 +438,7 @@ const makeMessagingLag = (
438
438
  WHERE project_id = '${sqlEscape(projectId)}'
439
439
  AND span_kind = 4
440
440
  AND json_extract(attributes_json, '$."messaging.destination"') = '${sqlEscape(topic)}'
441
- AND received_at >= datetime('now', '-15 minutes')
441
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-15 minutes')
442
442
  ),
443
443
  consumers AS (
444
444
  SELECT trace_id,
@@ -448,7 +448,7 @@ const makeMessagingLag = (
448
448
  WHERE project_id = '${sqlEscape(projectId)}'
449
449
  AND span_kind = 5
450
450
  AND json_extract(attributes_json, '$."messaging.destination"') = '${sqlEscape(topic)}'
451
- AND received_at >= datetime('now', '-15 minutes')
451
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-15 minutes')
452
452
  ),
453
453
  gaps AS (
454
454
  SELECT
@@ -514,15 +514,15 @@ const makeAiCostBurn = (projectId: string): AnalysisDefinition => ({
514
514
  COUNT(*) AS n
515
515
  FROM ai_calls
516
516
  WHERE project_id = '${sqlEscape(projectId)}'
517
- AND received_at >= datetime('now', '-1 hour')
517
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
518
518
  ),
519
519
  prev AS (
520
520
  SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
521
521
  COUNT(*) AS n
522
522
  FROM ai_calls
523
523
  WHERE project_id = '${sqlEscape(projectId)}'
524
- AND received_at >= datetime('now', '-2 hours')
525
- AND received_at < datetime('now', '-1 hour')
524
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-2 hours')
525
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
526
526
  ),
527
527
  delta AS (
528
528
  SELECT CASE
@@ -560,25 +560,26 @@ const makeAiErrorRate = (projectId: string): AnalysisDefinition => ({
560
560
  // AI error rate is per-call; 5 min cadence balances signal vs cost on
561
561
  // installs with hundreds of LLM spans.
562
562
  refreshSeconds: 300,
563
- // is_error = 1 fraction in the last 5 minutes vs trailing 1h baseline.
563
+ // is_error fraction in the last 5 minutes vs trailing 1h baseline. Tested
564
+ // as a bare boolean so it works for INTEGER 0/1 (D1) and BOOLEAN (Postgres).
564
565
  // Same thresholds as overall_error_rate.
565
566
  sql: `
566
567
  WITH now_window AS (
567
568
  SELECT
568
569
  COUNT(*) AS total,
569
- SUM(CASE WHEN is_error = 1 THEN 1 ELSE 0 END) AS errors
570
+ SUM(CASE WHEN is_error THEN 1 ELSE 0 END) AS errors
570
571
  FROM ai_calls
571
572
  WHERE project_id = '${sqlEscape(projectId)}'
572
- AND received_at >= datetime('now', '-5 minutes')
573
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
573
574
  ),
574
575
  base_window AS (
575
576
  SELECT
576
577
  COUNT(*) AS total,
577
- SUM(CASE WHEN is_error = 1 THEN 1 ELSE 0 END) AS errors
578
+ SUM(CASE WHEN is_error THEN 1 ELSE 0 END) AS errors
578
579
  FROM ai_calls
579
580
  WHERE project_id = '${sqlEscape(projectId)}'
580
- AND received_at >= datetime('now', '-1 hour')
581
- AND received_at < datetime('now', '-5 minutes')
581
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
582
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
582
583
  )
583
584
  SELECT
584
585
  CASE
@@ -48,7 +48,7 @@ const errorTopOffenders: AnalysisDefinition = {
48
48
  COUNT(*) AS errors
49
49
  FROM telemetry_spans
50
50
  WHERE project_id = '{{PROJECT_ID}}'
51
- AND received_at >= datetime('now', '-5 minutes')
51
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
52
52
  AND status_code = 2
53
53
  GROUP BY service_name
54
54
  ORDER BY errors DESC
@@ -59,8 +59,8 @@ const errorTopOffenders: AnalysisDefinition = {
59
59
  COUNT(*) AS errors
60
60
  FROM telemetry_spans
61
61
  WHERE project_id = '{{PROJECT_ID}}'
62
- AND received_at >= datetime('now', '-1 hour')
63
- AND received_at < datetime('now', '-5 minutes')
62
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
63
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
64
64
  AND status_code = 2
65
65
  GROUP BY service_name
66
66
  ORDER BY errors DESC
@@ -128,7 +128,7 @@ const latencyOutlierAttribution: AnalysisDefinition = {
128
128
  COUNT(*) OVER () AS n
129
129
  FROM telemetry_spans
130
130
  WHERE project_id = '{{PROJECT_ID}}'
131
- AND received_at >= datetime('now', '-15 minutes')
131
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-15 minutes')
132
132
  ),
133
133
  p95_cut AS (
134
134
  -- Conservatively call "tail" the slowest 5% of the window.
@@ -203,12 +203,12 @@ const logAnomalySummary: AnalysisDefinition = {
203
203
  (SELECT l2.message FROM logs l2
204
204
  WHERE l2.project_id = '{{PROJECT_ID}}'
205
205
  AND COALESCE(l2.logger_name, '<unknown>') = COALESCE(l1.logger_name, '<unknown>')
206
- AND l2.received_at >= datetime('now', '-5 minutes')
206
+ AND l2.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
207
207
  AND l2.severity_number >= 17
208
208
  ORDER BY l2.received_at DESC LIMIT 1) AS sample_message
209
209
  FROM logs l1
210
210
  WHERE l1.project_id = '{{PROJECT_ID}}'
211
- AND l1.received_at >= datetime('now', '-5 minutes')
211
+ AND l1.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
212
212
  AND l1.severity_number >= 17
213
213
  GROUP BY l1.logger_name
214
214
  ),
@@ -216,8 +216,8 @@ const logAnomalySummary: AnalysisDefinition = {
216
216
  SELECT DISTINCT COALESCE(logger_name, '<unknown>') AS logger
217
217
  FROM logs
218
218
  WHERE project_id = '{{PROJECT_ID}}'
219
- AND received_at >= datetime('now', '-1 hour')
220
- AND received_at < datetime('now', '-5 minutes')
219
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
220
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
221
221
  AND severity_number >= 17
222
222
  ),
223
223
  newcomers AS (
@@ -40,7 +40,7 @@ const overallErrorRate: AnalysisDefinition = {
40
40
  SUM(CASE WHEN status_code = 2 THEN 1 ELSE 0 END) AS errors
41
41
  FROM telemetry_spans
42
42
  WHERE project_id = '{{PROJECT_ID}}'
43
- AND received_at >= datetime('now', '-5 minutes')
43
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
44
44
  ),
45
45
  base_window AS (
46
46
  SELECT
@@ -48,8 +48,8 @@ const overallErrorRate: AnalysisDefinition = {
48
48
  SUM(CASE WHEN status_code = 2 THEN 1 ELSE 0 END) AS errors
49
49
  FROM telemetry_spans
50
50
  WHERE project_id = '{{PROJECT_ID}}'
51
- AND received_at >= datetime('now', '-1 hour')
52
- AND received_at < datetime('now', '-5 minutes')
51
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
52
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
53
53
  ),
54
54
  spark AS (
55
55
  -- per-minute error rate over the last 30 minutes for sparkline rendering
@@ -59,7 +59,7 @@ const overallErrorRate: AnalysisDefinition = {
59
59
  SUM(CASE WHEN status_code = 2 THEN 1 ELSE 0 END) AS errors
60
60
  FROM telemetry_spans
61
61
  WHERE project_id = '{{PROJECT_ID}}'
62
- AND received_at >= datetime('now', '-30 minutes')
62
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
63
63
  GROUP BY minute
64
64
  ORDER BY minute ASC
65
65
  )
@@ -123,7 +123,7 @@ const topErrorServices: AnalysisDefinition = {
123
123
  SUM(CASE WHEN status_code = 2 THEN 1 ELSE 0 END) AS errors
124
124
  FROM telemetry_spans
125
125
  WHERE project_id = '{{PROJECT_ID}}'
126
- AND received_at >= datetime('now', '-5 minutes')
126
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
127
127
  GROUP BY service
128
128
  ),
129
129
  top5 AS (
@@ -183,7 +183,7 @@ const latencyP95Overall: AnalysisDefinition = {
183
183
  COUNT(*) OVER () AS n
184
184
  FROM telemetry_spans
185
185
  WHERE project_id = '{{PROJECT_ID}}'
186
- AND received_at >= datetime('now', '-1 hour')
186
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
187
187
  ),
188
188
  base AS (
189
189
  SELECT duration_ms,
@@ -191,8 +191,8 @@ const latencyP95Overall: AnalysisDefinition = {
191
191
  COUNT(*) OVER () AS n
192
192
  FROM telemetry_spans
193
193
  WHERE project_id = '{{PROJECT_ID}}'
194
- AND received_at >= datetime('now', '-25 hours')
195
- AND received_at < datetime('now', '-24 hours')
194
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-25 hours')
195
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-24 hours')
196
196
  ),
197
197
  cur_p95 AS (
198
198
  SELECT duration_ms FROM cur
@@ -214,7 +214,7 @@ const latencyP95Overall: AnalysisDefinition = {
214
214
  COUNT(*) AS n
215
215
  FROM telemetry_spans
216
216
  WHERE project_id = '{{PROJECT_ID}}'
217
- AND received_at >= datetime('now', '-1 hour')
217
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
218
218
  GROUP BY bucket
219
219
  ORDER BY bucket ASC
220
220
  ),
@@ -271,7 +271,7 @@ const throughputSlope: AnalysisDefinition = {
271
271
  COUNT(*) OVER () AS n
272
272
  FROM telemetry_spans
273
273
  WHERE project_id = '{{PROJECT_ID}}'
274
- AND received_at >= datetime('now', '-30 minutes')
274
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
275
275
  ),
276
276
  prev AS (
277
277
  SELECT duration_ms,
@@ -279,8 +279,8 @@ const throughputSlope: AnalysisDefinition = {
279
279
  COUNT(*) OVER () AS n
280
280
  FROM telemetry_spans
281
281
  WHERE project_id = '{{PROJECT_ID}}'
282
- AND received_at >= datetime('now', '-60 minutes')
283
- AND received_at < datetime('now', '-30 minutes')
282
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-60 minutes')
283
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
284
284
  ),
285
285
  cur_n AS (SELECT COALESCE((SELECT n FROM cur LIMIT 1), 0) AS n),
286
286
  prev_n AS (SELECT COALESCE((SELECT n FROM prev LIMIT 1), 0) AS n),
@@ -347,7 +347,7 @@ const activeSessions: AnalysisDefinition = {
347
347
  SELECT COUNT(DISTINCT session_id) AS n
348
348
  FROM usage_events
349
349
  WHERE project_id = '{{PROJECT_ID}}'
350
- AND received_at >= datetime('now', '-15 minutes')
350
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-15 minutes')
351
351
  ),
352
352
  hourly AS (
353
353
  SELECT
@@ -355,7 +355,7 @@ const activeSessions: AnalysisDefinition = {
355
355
  COUNT(DISTINCT session_id) AS sessions
356
356
  FROM usage_events
357
357
  WHERE project_id = '{{PROJECT_ID}}'
358
- AND received_at >= datetime('now', '-24 hours')
358
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-24 hours')
359
359
  GROUP BY hour
360
360
  ORDER BY hour ASC
361
361
  )
@@ -400,7 +400,7 @@ const logErrorRate: AnalysisDefinition = {
400
400
  SUM(CASE WHEN severity_number >= 17 THEN 1 ELSE 0 END) AS errors
401
401
  FROM logs
402
402
  WHERE project_id = '{{PROJECT_ID}}'
403
- AND received_at >= datetime('now', '-5 minutes')
403
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
404
404
  ),
405
405
  base_window AS (
406
406
  SELECT
@@ -408,8 +408,8 @@ const logErrorRate: AnalysisDefinition = {
408
408
  SUM(CASE WHEN severity_number >= 17 THEN 1 ELSE 0 END) AS errors
409
409
  FROM logs
410
410
  WHERE project_id = '{{PROJECT_ID}}'
411
- AND received_at >= datetime('now', '-1 hour')
412
- AND received_at < datetime('now', '-5 minutes')
411
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-1 hour')
412
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
413
413
  ),
414
414
  spark AS (
415
415
  SELECT
@@ -418,7 +418,7 @@ const logErrorRate: AnalysisDefinition = {
418
418
  SUM(CASE WHEN severity_number >= 17 THEN 1 ELSE 0 END) AS errors
419
419
  FROM logs
420
420
  WHERE project_id = '{{PROJECT_ID}}'
421
- AND received_at >= datetime('now', '-30 minutes')
421
+ AND received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
422
422
  GROUP BY minute
423
423
  ORDER BY minute ASC
424
424
  )
@@ -490,19 +490,19 @@ const serviceCpuUtilization: AnalysisDefinition = {
490
490
  WHERE s.project_id = '{{PROJECT_ID}}'
491
491
  AND s.name = 'process.cpu.utilization'
492
492
  AND s.service_name IS NOT NULL
493
- AND p.received_at >= datetime('now', '-30 minutes')
493
+ AND p.received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
494
494
  ),
495
495
  now_window AS (
496
496
  SELECT service_name, AVG(value) AS avg_util
497
497
  FROM cpu_points
498
- WHERE received_at >= datetime('now', '-5 minutes')
498
+ WHERE received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
499
499
  GROUP BY service_name
500
500
  ),
501
501
  base_window AS (
502
502
  SELECT service_name, AVG(value) AS avg_util
503
503
  FROM cpu_points
504
- WHERE received_at >= datetime('now', '-30 minutes')
505
- AND received_at < datetime('now', '-5 minutes')
504
+ WHERE received_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 minutes')
505
+ AND received_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 minutes')
506
506
  GROUP BY service_name
507
507
  ),
508
508
  top_now AS (