wide_events 0.1.3 → 0.2.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.
@@ -13,6 +13,17 @@ never needs child spans: filter `main = true` and aggregate.
13
13
 
14
14
  ## Where the rows live
15
15
 
16
+ - **Wide Events store (bundled DuckDB accessory):** the `wide_events` table.
17
+ `route`, `job_class`, `status`, `error`, `request_id`, `duration_ms`,
18
+ `kind`, `deployment`, and `source` are promoted columns; everything else
19
+ the app set is in the `attributes` VARIANT column, read with
20
+ `attributes['key.name']`. A VARIANT cannot be returned as-is, so cast it
21
+ to the type you want: `attributes['exception.type']::VARCHAR`,
22
+ `attributes['account.id']::BIGINT`,
23
+ `attributes['feature_flag.new_ui']::BOOLEAN`. `IS NULL` works without a
24
+ cast. Query it with `kamal telemetry` (an interactive prompt, or pipe SQL
25
+ to it non-interactively) - never SSH in and open the database file
26
+ directly.
16
27
  - **ClickHouse (HyperDX/ClickStack default schema):** the `otel_traces`
17
28
  table; wide-event attributes are in the `SpanAttributes` map, resource
18
29
  attributes (build, host, environment) in `ResourceAttributes`. Adapt table
@@ -20,12 +31,84 @@ never needs child spans: filter `main = true` and aggregate.
20
31
  - **Log sink deployments:** one JSON object per line; query with your log
21
32
  store's JSON operators.
22
33
 
23
- Filter by environment when staging and production share storage.
34
+ Filter by environment when staging and production share storage (the
35
+ DuckDB store is single-tenant per deployment, so this mainly applies to the
36
+ ClickHouse/log backends).
24
37
 
25
- ## Symptom to query
38
+ ## Operational health (DuckDB store)
39
+
40
+ `SELECT * FROM wide_events_store_status;` is the one-row operational view:
41
+ row counts for the last hour/day, oldest/newest received event, database and
42
+ free-disk bytes, write-readiness and its reason, last checkpoint/retention/
43
+ backup times and errors, and the store/protocol/DuckDB versions. Check this
44
+ first when `bin/rails wide_events:setup:check` reports `degraded`, or before
45
+ trusting a "no events" result - it tells you whether anything has arrived at
46
+ all.
47
+
48
+ ## Standing queries (DuckDB store)
49
+
50
+ "The reports route is slow":
51
+
52
+ ```sql
53
+ SELECT route,
54
+ count(*) AS requests,
55
+ quantile_cont(duration_ms, 0.5) AS p50_ms,
56
+ quantile_cont(duration_ms, 0.95) AS p95_ms
57
+ FROM wide_events
58
+ WHERE route = 'ReportsController#index'
59
+ AND occurred_at > current_timestamp - INTERVAL 1 DAY
60
+ GROUP BY route
61
+ ORDER BY p95_ms DESC
62
+ ```
63
+
64
+ "Which jobs are failing":
65
+
66
+ ```sql
67
+ SELECT job_class, count(*) AS failures
68
+ FROM wide_events
69
+ WHERE kind = 'job' AND error
70
+ AND occurred_at > current_timestamp - INTERVAL 1 DAY
71
+ GROUP BY job_class
72
+ ORDER BY failures DESC
73
+ ```
74
+
75
+ "Unhandled exceptions" - `error` true with no `exception.slug` set is the
76
+ standing "instrument this rescue" query (see instrumenting-wide-events):
77
+
78
+ ```sql
79
+ SELECT route, job_class, attributes['exception.type']::VARCHAR AS exception_type, count(*) AS occurrences
80
+ FROM wide_events
81
+ WHERE error
82
+ AND coalesce(attributes['exception.slug']::VARCHAR, '') = ''
83
+ AND occurred_at > current_timestamp - INTERVAL 1 DAY
84
+ GROUP BY route, job_class, exception_type
85
+ ORDER BY occurrences DESC
86
+ ```
87
+
88
+ "Did the deploy change anything" - group by `deployment` (the Kamal version
89
+ string) and compare:
90
+
91
+ ```sql
92
+ SELECT deployment,
93
+ count(*) AS requests,
94
+ avg(duration_ms) AS avg_ms,
95
+ quantile_cont(duration_ms, 0.95) AS p95_ms,
96
+ sum(error::INT) AS errors
97
+ FROM wide_events
98
+ WHERE route = 'ReportsController#index'
99
+ AND occurred_at > current_timestamp - INTERVAL 1 DAY
100
+ GROUP BY deployment
101
+ ORDER BY deployment DESC
102
+ ```
103
+
104
+ "Why did this specific request fail" - filter by `request_id` and read
105
+ every promoted column and attribute of the one row before reaching for logs.
106
+
107
+ ## Symptom to query (ClickHouse)
26
108
 
27
109
  Start from the unit of work, not from logs. One GROUP BY usually replaces a
28
- cross-tool investigation.
110
+ cross-tool investigation. (On the DuckDB store, use the equivalent queries
111
+ above instead - same symptoms, promoted columns instead of `SpanAttributes`.)
29
112
 
30
113
  "The reports route is slow":
31
114
 
@@ -52,7 +135,7 @@ LIMIT 20
52
135
  `SpanAttributes['http.request.id']` and read every attribute of the one row
53
136
  before reaching for logs or the trace waterfall.
54
137
 
55
- ## Standing queries
138
+ ## Standing queries (ClickHouse)
56
139
 
57
140
  - **Unnamed exception paths**: `SpanAttributes['error'] = 'true' AND
58
141
  SpanAttributes['exception.slug'] = '' AND SpanAttributes['exception.type'] != ''`:
@@ -69,6 +152,8 @@ before reaching for logs or the trace waterfall.
69
152
  ## Verifying your own fix
70
153
 
71
154
  After a deploy, don't ask a human to check a dashboard. Query the affected
72
- route/job filtered to the new `service.version` and compare against the
73
- prior build: error rate, p50, query count, and the specific attribute your
74
- change was supposed to move. State the numbers in your summary.
155
+ route/job filtered to the new build (`deployment` on the DuckDB store,
156
+ `ResourceAttributes['service.version']` on ClickHouse) and compare against
157
+ the prior build: error rate, p50/p95, query count, and the specific
158
+ attribute your change was supposed to move. State the numbers in your
159
+ summary.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: setting-up-wide-events-store
3
+ description: Use when a human asks to set up, deploy, or finish deploying the bundled Wide Events DuckDB store (the Kamal accessory that receives production telemetry). Covers the store generator, DNS, `kamal setup`, and post-deploy verification end to end.
4
+ ---
5
+
6
+ # Setting up the Wide Events store
7
+
8
+ This is the guided path from "wide_events is installed" to "production
9
+ requests are queryable." It wraps deterministic generator/checker code -
10
+ never hand-edit `config/deploy.yml` or `.kamal/secrets` yourself; every
11
+ mutation here is idempotent, so re-running any step (including this whole
12
+ skill) is always safe.
13
+
14
+ ## What to ask for
15
+
16
+ Exactly three things, nothing else:
17
+
18
+ 1. the SSH address (IP or DNS name) of the VM that will run the store;
19
+ 2. the public DNS hostname end users/agents will query through (no scheme,
20
+ no path); and
21
+ 3. retention in days (default 30 if the human has no preference).
22
+
23
+ ## Steps
24
+
25
+ 1. **Generate the accessory.**
26
+
27
+ ```bash
28
+ bin/rails generate wide_events:store --host=<ssh-address> --hostname=<telemetry-hostname> --retention-days=<n>
29
+ ```
30
+
31
+ This edits `config/deploy.yml` (accessory, app environment, `telemetry`
32
+ and `wide-events-backup` aliases) and writes durable, gitignored tokens to
33
+ `.kamal/wide-events-ingest-token` and `.kamal/wide-events-query-token`. If
34
+ it refuses instead of editing (ambiguous YAML, a conflicting existing
35
+ key), it prints a complete snippet - merge that by hand, then continue.
36
+
37
+ 2. **Show the exact DNS record.** Tell the human to point the telemetry
38
+ hostname at the store host now, before deploying, so propagation happens
39
+ in parallel with the next steps instead of after them.
40
+
41
+ 3. **Run the checker.**
42
+
43
+ ```bash
44
+ bin/rails wide_events:setup:check
45
+ ```
46
+
47
+ This is the same idempotent command at every stage - before the first
48
+ deploy, while waiting on DNS, and after `kamal setup` - so there is never
49
+ a different command to remember. It reports one of five states:
50
+
51
+ - `failed` - a Kamal version/config problem, or a host-directory
52
+ permission problem. Fix what the message says (it includes the exact
53
+ one-time administrator command if root is needed to create the data
54
+ directory) and re-run.
55
+ - `needs_dns` - DNS has not resolved yet. **This is expected and
56
+ resumable, not an error.** Nothing generated is reverted or lost;
57
+ re-running the same command later picks up exactly where it left off.
58
+ Wait for propagation and run it again.
59
+ - `ready_to_deploy` - DNS is good and the store isn't answering yet.
60
+ Proceed to step 4.
61
+ - `degraded` - the store is routable but not accepting writes (usually
62
+ low disk space). Do not proceed; investigate on the host.
63
+ - `healthy` - already fully deployed and working (useful if you're
64
+ re-running this skill against an existing setup).
65
+
66
+ 4. **Deploy, once the checker says `ready_to_deploy`.**
67
+
68
+ ```bash
69
+ kamal setup
70
+ ```
71
+
72
+ 5. **Re-run the checker.** `bin/rails wide_events:setup:check` again. On a
73
+ healthy deploy it sends one synthetic event, queries it back by ID, and
74
+ prints that row plus a route-count/p95-latency query - proof the whole
75
+ path (ingest token, query token, TLS, DuckDB) works before any real
76
+ traffic exists.
77
+
78
+ 6. **Ask the human to exercise one real route** of the app now that the
79
+ store is live (any request or job is fine).
80
+
81
+ 7. **After they confirm, query it back:**
82
+
83
+ ```bash
84
+ kamal telemetry
85
+ ```
86
+
87
+ opens an interactive SQL prompt against the store (or pipe SQL to it
88
+ non-interactively). Run a query for the most recent real request, e.g.:
89
+
90
+ ```sql
91
+ SELECT id, occurred_at, route, duration_ms, status
92
+ FROM wide_events
93
+ ORDER BY occurred_at DESC
94
+ LIMIT 1;
95
+ ```
96
+
97
+ Display that captured row, then the route count and p95 latency for the
98
+ exercised route, and state both numbers back to the human.
99
+
100
+ ## Things to tell the human, unprompted
101
+
102
+ - **DNS timeouts are resumable.** If `wide_events:setup:check` reports
103
+ `needs_dns`, that is normal while waiting on propagation, not a failure -
104
+ running the exact same command again later continues correctly.
105
+ - **Backups are local-only.** The store itself takes a verified local backup
106
+ once a day in-process (three most recent kept, in `backups/` next to the
107
+ database); `kamal wide-events-backup` is the on-demand version of the same
108
+ thing and prints the path it wrote. Either way this protects against
109
+ logical mistakes and bad upgrades, not loss of the VM or volume itself.
110
+ There is no off-host backup in v1.
111
+ - **Query access uses a separate token from ingestion.** `.kamal/wide-events-
112
+ query-token` is read-only and distinct from `.kamal/wide-events-ingest-
113
+ token`; sharing query access (e.g. with a teammate or another agent) never
114
+ requires sharing the ability to write events.
115
+
116
+ ## Where full querying guidance lives
117
+
118
+ Once the store is live, use the `debugging-with-wide-events` skill for the
119
+ standing queries (slow routes, job failures, unhandled exceptions, deploy
120
+ comparisons) and the `wide_events_store_status` operational view.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wide_events
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Miribyan
@@ -37,11 +37,26 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '2.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: csv
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
40
54
  description: 'Collects everything a Rails app knows about each request or job execution
41
55
  into one flat, high-cardinality event: route, user, build, query counts, cache hits,
42
- feature flags, phase timings, errors. Flushes onto the OpenTelemetry root span or
43
- a JSON log line. Ships a checkable attribute registry, test helpers, and agent skills
44
- for writing and querying instrumentation.'
56
+ feature flags, phase timings, errors. Ships a bundled DuckDB store you deploy as
57
+ a Kamal accessory (one generator, then SQL against production), or flushes onto
58
+ the OpenTelemetry root span or a JSON log line instead. Includes a checkable attribute
59
+ registry, test helpers, and agent skills for instrumenting, querying, and deploying.'
45
60
  email:
46
61
  - adam@adam.am
47
62
  executables: []
@@ -55,25 +70,41 @@ files:
55
70
  - lib/generators/wide_events/install/templates/agents_md_section.md
56
71
  - lib/generators/wide_events/install/templates/initializer.rb
57
72
  - lib/generators/wide_events/install/templates/registry.yml
73
+ - lib/generators/wide_events/skill_installer.rb
58
74
  - lib/generators/wide_events/skills/skills_generator.rb
75
+ - lib/generators/wide_events/store/store_generator.rb
76
+ - lib/generators/wide_events/store/templates/accessory.yml.erb
59
77
  - lib/wide_event.rb
60
78
  - lib/wide_event/configuration.rb
61
79
  - lib/wide_event/job_instrumentation.rb
80
+ - lib/wide_event/kamal/deploy_editor.rb
81
+ - lib/wide_event/kamal/secrets_editor.rb
62
82
  - lib/wide_event/middleware.rb
63
83
  - lib/wide_event/railtie.rb
64
84
  - lib/wide_event/registry.rb
65
85
  - lib/wide_event/registry/defaults.yml
86
+ - lib/wide_event/setup/checker.rb
87
+ - lib/wide_event/setup/command_runner.rb
66
88
  - lib/wide_event/sinks/log_line.rb
67
89
  - lib/wide_event/sinks/memory.rb
68
90
  - lib/wide_event/sinks/otel_span.rb
91
+ - lib/wide_event/sinks/store.rb
69
92
  - lib/wide_event/span_counter_processor.rb
93
+ - lib/wide_event/store/client.rb
94
+ - lib/wide_event/store/envelope.rb
95
+ - lib/wide_event/store/formatter.rb
96
+ - lib/wide_event/store/query_result.rb
97
+ - lib/wide_event/store/sender.rb
70
98
  - lib/wide_event/subscribers.rb
71
99
  - lib/wide_event/tasks/registry.rake
100
+ - lib/wide_event/tasks/setup.rake
101
+ - lib/wide_event/tasks/store.rake
72
102
  - lib/wide_event/test_helper.rb
73
103
  - lib/wide_event/version.rb
74
104
  - lib/wide_events.rb
75
105
  - skills/debugging-with-wide-events/SKILL.md
76
106
  - skills/instrumenting-wide-events/SKILL.md
107
+ - skills/setting-up-wide-events-store/SKILL.md
77
108
  homepage: https://github.com/adammiribyan/wide_events
78
109
  licenses:
79
110
  - MIT
@@ -100,6 +131,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
131
  requirements: []
101
132
  rubygems_version: 3.6.9
102
133
  specification_version: 4
103
- summary: One wide telemetry event per request or job, on the OTel span you already
104
- export.
134
+ summary: One wide telemetry event per Rails request or job, in a database you own.
105
135
  test_files: []