e11y 1.0.0 โ†’ 1.1.0

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +38 -6
  4. data/RELEASE.md +23 -9
  5. data/Rakefile +141 -28
  6. data/docs/QUICK-START.md +2 -2
  7. data/docs/RAILS_INTEGRATION.md +64 -14
  8. data/docs/architecture/ADR-001-architecture.md +1 -1
  9. data/docs/architecture/ADR-003-slo-observability.md +58 -1554
  10. data/docs/architecture/ADR-008-rails-integration.md +81 -512
  11. data/docs/architecture/ADR-011-testing-strategy.md +3 -3
  12. data/docs/architecture/ADR-012-event-evolution.md +11 -11
  13. data/docs/architecture/ADR-015-middleware-order.md +22 -20
  14. data/docs/architecture/ADR-016-self-monitoring-slo.md +6 -6
  15. data/docs/plans/2026-03-20-browser-overlay-svelte.md +281 -0
  16. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +33 -625
  17. data/docs/use_cases/UC-009-multi-service-tracing.md +26 -174
  18. data/docs/use_cases/UC-010-background-job-tracking.md +19 -86
  19. data/gems/e11y-devtools/README.md +28 -6
  20. data/gems/e11y-devtools/config/routes.rb +7 -0
  21. data/gems/e11y-devtools/e11y-devtools.gemspec +1 -1
  22. data/gems/e11y-devtools/frontend/.gitignore +24 -0
  23. data/gems/e11y-devtools/frontend/README.md +51 -0
  24. data/gems/e11y-devtools/frontend/index.html +14 -0
  25. data/gems/e11y-devtools/frontend/package-lock.json +3707 -0
  26. data/gems/e11y-devtools/frontend/package.json +28 -0
  27. data/gems/e11y-devtools/frontend/public/mocks/v1/events/recent.json +4205 -0
  28. data/gems/e11y-devtools/frontend/public/mocks/v1/interactions.json +194 -0
  29. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0a2e04027cfa22d014bc22e8b27cd913/events.json +86 -0
  30. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0e1543af6a630fb3af6b52283154b3e0/events.json +169 -0
  31. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/1838b691faa49564f97db8592ff3978d/events.json +78 -0
  32. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/29f198f6588dacffb687777eb5f8f118/events.json +197 -0
  33. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/34bc3c9c0097de28a7a6f99b90a8e7bc/events.json +194 -0
  34. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/3ba6c20d068ab9cee00e51b180e66444/events.json +184 -0
  35. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/435bfd8f17b9009146a79812d7c3726d/events.json +144 -0
  36. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/4c7676e3fe668e99edb2b94d7d5678a9/events.json +222 -0
  37. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/6daf0d47974bedfc55d5de7004a3ea9f/events.json +194 -0
  38. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8a81ada42834d15f287bb40010043605/events.json +194 -0
  39. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8c0a98900edaae105469df8daedccf02/events.json +198 -0
  40. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8e4f645180f8a7d1dce426b07380466b/events.json +222 -0
  41. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/93db346fa5d44a032605a13b627f4b80/events.json +128 -0
  42. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/98ff6146faf7bd9be8bd03a8275817ba/events.json +223 -0
  43. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/9997ddd0247bc7e25f2ca7a5c415c93d/events.json +197 -0
  44. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/99e35f8ef3baedd798cc4fd085980ad9/events.json +194 -0
  45. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b4f3095c1909924cbc98889a86c83d6d/events.json +131 -0
  46. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b54b7fc32b7575a7110de809d11ccda0/events.json +128 -0
  47. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c0b48033fa06746bcc5886745e053cff/events.json +169 -0
  48. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c44649ac76701b4558927cd2305ab535/events.json +169 -0
  49. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/d601ae3320057580a39dbdac2edfdf4a/events.json +248 -0
  50. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e67e724bab422d2b52eeb49635e512e1/events.json +194 -0
  51. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e6c72765a28f158a8485b35fa63f73da/events.json +194 -0
  52. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/f541b87405c9a54819b18ebe529f6419/events.json +194 -0
  53. data/gems/e11y-devtools/frontend/scripts/generate_mocks.rb +397 -0
  54. data/gems/e11y-devtools/frontend/src/App.svelte +827 -0
  55. data/gems/e11y-devtools/frontend/src/components/Fab.svelte +19 -0
  56. data/gems/e11y-devtools/frontend/src/components/FilterBar.svelte +38 -0
  57. data/gems/e11y-devtools/frontend/src/components/FullscreenPanel.svelte +82 -0
  58. data/gems/e11y-devtools/frontend/src/components/InteractionsTimeline.svelte +264 -0
  59. data/gems/e11y-devtools/frontend/src/components/RecentHistogram.svelte +354 -0
  60. data/gems/e11y-devtools/frontend/src/lib/api.ts +37 -0
  61. data/gems/e11y-devtools/frontend/src/lib/eventIdentity.ts +12 -0
  62. data/gems/e11y-devtools/frontend/src/lib/format.ts +37 -0
  63. data/gems/e11y-devtools/frontend/src/lib/listFilter.ts +43 -0
  64. data/gems/e11y-devtools/frontend/src/lib/recentVolume.ts +80 -0
  65. data/gems/e11y-devtools/frontend/src/lib/router.ts +12 -0
  66. data/gems/e11y-devtools/frontend/src/lib/transitions.ts +34 -0
  67. data/gems/e11y-devtools/frontend/src/lib/viewportOrigin.ts +25 -0
  68. data/gems/e11y-devtools/frontend/src/main.ts +8 -0
  69. data/gems/e11y-devtools/frontend/src/overlay-entry.ts +24 -0
  70. data/gems/e11y-devtools/frontend/src/overlay.css +1080 -0
  71. data/gems/e11y-devtools/frontend/svelte.config.js +2 -0
  72. data/gems/e11y-devtools/frontend/test_puppeteer.js +41 -0
  73. data/gems/e11y-devtools/frontend/test_scale.js +3 -0
  74. data/gems/e11y-devtools/frontend/tsconfig.app.json +21 -0
  75. data/gems/e11y-devtools/frontend/tsconfig.json +7 -0
  76. data/gems/e11y-devtools/frontend/tsconfig.node.json +26 -0
  77. data/gems/e11y-devtools/frontend/vite.config.ts +36 -0
  78. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +20 -115
  79. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +40 -0
  80. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +25 -0
  81. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +2 -2
  82. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +33 -0
  83. data/lib/e11y/adapters/file.rb +1 -1
  84. data/lib/e11y/instruments/active_job.rb +3 -5
  85. data/lib/e11y/instruments/sidekiq.rb +3 -5
  86. data/lib/e11y/pipeline/builder.rb +3 -3
  87. data/lib/e11y/railtie.rb +3 -2
  88. data/lib/e11y/tracing/propagator.rb +28 -0
  89. data/lib/e11y/version.rb +1 -1
  90. data/lib/e11y.rb +1 -20
  91. data/lib/generators/e11y/install/templates/e11y.rb +2 -2
  92. metadata +58 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d7e79dcbcbd63a42a108fff3b2c29f4cea6f07e8e2632145d4cff39f85514ac
4
- data.tar.gz: b53a0b131efaad3accb9ab20b75c5dcf973274422d384432a3b3562437750e4b
3
+ metadata.gz: 4cf2b7d2a17ee4e2474c6a286fe16e1c6dc3f3f6c62c687c21e6e19ff2326cff
4
+ data.tar.gz: cf70471e2647774b1ca4fa34c54cb731eed9adee5a6fb4e3b45203030a0c2843
5
5
  SHA512:
6
- metadata.gz: 4576c625410acc475dbd5782fa546b29adcc6a815c319d86eae9a9b8e0a700c49a099197db7fdccf3c3ea43f594c24bdd90ccdc689cad82e9a8da11d18d50999
7
- data.tar.gz: f569447a2c1b532cc39246d163fe245b61f63ec9ee618a2c9cf428936f3eead760549d6774de9b8a5afc09d12a0df7439ad53bf0f41993a7c518af566ebbdc1d
6
+ metadata.gz: f06a9a21e21cf3c287a0e5b271d15bbd1d8d453bf1168e9a1ef5a37eb3f1390f627b14801a02c668e6ae063ba12bcaf5854ba76ce4288ec4050b2a2e6eea18f1
7
+ data.tar.gz: c862108b20fc6c9da7ec1672baeb037dbbc4ac10fa06d713c8306c0ca1fd58c2e4bafec2b7a935fee2afc6bf7a1bd6741fb3a386b87edd024fa3c91e521c017d
data/CHANGELOG.md CHANGED
@@ -19,6 +19,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19
19
 
20
20
  ### Security
21
21
 
22
+ ## [1.1.0] - 2026-03-23
23
+
24
+ ### Added
25
+
26
+ - Sidekiq and Active Job: propagate **`user_id`** into **`e11y_baggage`** (and restore **`E11y::Current.user_id`** when the job runs). Key **`user_id`** is included in default baggage allowlist.
27
+
28
+ ### Changed
29
+
30
+ ### Fixed
31
+
32
+ - **Rails Railtie:** `config.enabled` is defaulted with `!Rails.env.test?` **only when still `nil`**, so an explicit `true`/`false` from `E11y.configure` in `config/application.rb` (or any code that runs before `before_initialize`) is no longer overwritten.
33
+
34
+ ### Deprecated
35
+
36
+ ### Removed
37
+
38
+ - **`E11y.track`** โ€” removed. Call **`YourEvent.track(...)`** on the event class only.
39
+
40
+ ### Security
41
+
22
42
  ## [1.0.0] - 2026-03-20
23
43
 
24
44
  ### BREAKING: Configuration โ€” Flat config API
@@ -64,8 +84,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
64
84
 
65
85
  ### Added
66
86
 
87
+ - Monorepo release tooling: `rake release:build_gems` and `rake release:gem_push` build/publish **e11y** and **e11y-devtools**; optional `release:rubygems:push_core` / `push_devtools`; GitHub Release workflow attaches both `.gem` files.
88
+
67
89
  ### Changed
68
90
 
91
+ - **e11y-devtools** 0.1.1 โ€” depends on **e11y** `~> 1.0` (`CORE_VERSION`); README Gemfile example updated.
92
+
69
93
  ### Fixed
70
94
 
71
95
  ### Deprecated
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  [Quick Start](#quick-start) โ€ข [How it works](#the-e11y-solution) โ€ข [Docs](#documentation)
12
12
 
13
- > v0.2.0 ยท Actively developed ยท Production feedback welcome โ†’ [open an issue](https://github.com/arturseletskiy/e11y/issues)
13
+ > v1.0.0 ยท Actively developed ยท Production feedback welcome โ†’ [open an issue](https://github.com/arturseletskiy/e11y/issues)
14
14
 
15
15
  </div>
16
16
 
@@ -93,8 +93,11 @@ end
93
93
 
94
94
  # 3. Track it
95
95
  OrderPaidEvent.track(order_id: "123", amount: 99.99, currency: "USD")
96
+
96
97
  ```
97
98
 
99
+ **Public API:** define events as subclasses of `E11y::Event::Base` and call **`.track(...)`** on the event class (for example `OrderPaidEvent.track(...)`). That is the only supported tracking entry point in application code.
100
+
98
101
  โ†’ [Full Quick Start guide (5 min)](#quick-start)
99
102
 
100
103
  ---
@@ -221,11 +224,19 @@ gem "e11y"
221
224
  E11y.configure do |config|
222
225
  config.adapters[:logs] = E11y::Adapters::Loki.new(url: ENV["LOKI_URL"])
223
226
  config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: ENV["SENTRY_DSN"])
224
- end
225
227
 
226
- # Auto-instruments Rails (optional):
227
- config.rails_instrumentation_enabled = true
228
- # โ†’ HTTP requests, ActiveRecord, ActiveJob, Cache events
228
+ # ActiveSupport::Notifications โ†’ E11y (HTTP, DB, cache, job lifecycle, etc.)
229
+ config.rails_instrumentation_enabled = true
230
+
231
+ # Optional: Sidekiq client/server middleware (buffer + job events) โ€” enable if you use Sidekiq
232
+ config.sidekiq_enabled = true
233
+
234
+ # Optional: ActiveJob callbacks (buffer + context) โ€” enable if you use Active Job
235
+ config.active_job_enabled = true
236
+
237
+ # Optional: wrap Rails.logger and emit E11y::Events::Rails::Log::* โ€” opt-in
238
+ # config.logger_bridge_enabled = true
239
+ end
229
240
  ```
230
241
 
231
242
  **vs. Traditional Observability:**
@@ -322,8 +333,17 @@ E11y.configure do |config|
322
333
  dsn: ENV["SENTRY_DSN"]
323
334
  )
324
335
 
325
- # Optional: Auto-instrument Rails
336
+ # ActiveSupport::Notifications โ†’ E11y (see docs/RAILS_INTEGRATION.md)
326
337
  config.rails_instrumentation_enabled = true
338
+
339
+ # Optional: Sidekiq / ActiveJob (ephemeral buffer + instrumentation at job boundaries)
340
+ config.sidekiq_enabled = true # set if you use Sidekiq
341
+ config.active_job_enabled = true # set if you use Active Job (in addition or instead)
342
+
343
+ # Optional: send Rails.logger lines into E11y as structured events
344
+ # config.logger_bridge_enabled = true
345
+ # config.logger_bridge_track_severities = [:warn, :error, :fatal] # nil = all severities
346
+ # config.logger_bridge_ignore_patterns = [%r{\A\[ActiveJob\]}]
327
347
  end
328
348
  ```
329
349
 
@@ -532,6 +552,18 @@ p99 latency <70ยตs (`:always`), <10ยตs (`:sampled`), <50ยตs (`:never`). Full ben
532
552
 
533
553
  ---
534
554
 
555
+ ## Upgrading
556
+
557
+ Breaking renames and migration steps are listed in [CHANGELOG.md](CHANGELOG.md). Common ones:
558
+
559
+ - **`RequestScopedBuffer` โ†’ `E11y::Buffers::EphemeralBuffer`** โ€” use `initialize!`, `flush_on_error`, and `discard` at request/job boundaries (middleware and instruments already do this).
560
+ - **Sidekiq / Active Job** โ€” enable with `config.sidekiq_enabled` and `config.active_job_enabled`; see [docs/RAILS_INTEGRATION.md](docs/RAILS_INTEGRATION.md).
561
+ - **Logger bridge** โ€” `config.logger_bridge_enabled` plus optional `logger_bridge_track_severities` and `logger_bridge_ignore_patterns` (same file).
562
+
563
+ Known tradeoffs and unfinished pieces: [docs/LIMITATIONS.md](docs/LIMITATIONS.md).
564
+
565
+ ---
566
+
535
567
  ## Documentation
536
568
 
537
569
  | Topic | Doc |
data/RELEASE.md CHANGED
@@ -68,7 +68,7 @@ git commit -m "Bump version to 0.2.0"
68
68
 
69
69
  ### Step 1: Prepare Release
70
70
 
71
- Run tests, build gem, create tag:
71
+ Run tests, build both gems, create tag:
72
72
 
73
73
  ```bash
74
74
  rake release:prep
@@ -77,8 +77,14 @@ rake release:prep
77
77
  This will:
78
78
  - โœ… Check git status (fails if uncommitted changes)
79
79
  - โœ… Run full test suite
80
- - โœ… Build gem file
81
- - โœ… Create annotated git tag
80
+ - โœ… Build **e11y** and **e11y-devtools** `.gem` files (devtools is built under `gems/e11y-devtools/`)
81
+ - โœ… Create annotated git tag `v<e11y-version>` (tag follows the core gem only)
82
+
83
+ Build gems without tests (e.g. after a failed spec run you already trust):
84
+
85
+ ```bash
86
+ rake release:build_gems
87
+ ```
82
88
 
83
89
  Or manually:
84
90
 
@@ -86,8 +92,9 @@ Or manually:
86
92
  # Run all tests
87
93
  bundle exec rspec
88
94
 
89
- # Build gem
95
+ # Build gems
90
96
  gem build e11y.gemspec
97
+ (cd gems/e11y-devtools && gem build e11y-devtools.gemspec)
91
98
 
92
99
  # Create and push tag
93
100
  git tag -a v0.2.0 -m "Release v0.2.0"
@@ -114,10 +121,16 @@ rake release:gem_push
114
121
  ```
115
122
 
116
123
  This will:
117
- - โœ… Verify gem file exists
118
- - โœ… Prompt for confirmation
119
- - โœ… Push gem to RubyGems (requires MFA)
120
- - โœ… Show verification URL
124
+ - โœ… Verify both `.gem` files exist (e11y in repo root, e11y-devtools under `gems/e11y-devtools/`)
125
+ - โœ… Prompt once for confirmation
126
+ - โœ… Push **e11y** first, then **e11y-devtools** (each `gem push` may ask for MFA)
127
+
128
+ Push only one gem if needed:
129
+
130
+ ```bash
131
+ rake release:rubygems:push_core # e11y only
132
+ rake release:rubygems:push_devtools # e11y-devtools only
133
+ ```
121
134
 
122
135
  Or manually:
123
136
 
@@ -133,8 +146,9 @@ Or manually:
133
146
  # Sign in to RubyGems (one-time setup)
134
147
  gem signin
135
148
 
136
- # Push the gem (requires MFA)
149
+ # Push the gems (requires MFA; e11y first โ€” devtools depends on it)
137
150
  gem push e11y-0.1.0.gem
151
+ gem push gems/e11y-devtools/e11y-devtools-0.1.0.gem
138
152
  ```
139
153
 
140
154
  Expected output:
data/Rakefile CHANGED
@@ -9,7 +9,8 @@
9
9
  # rake release:full # Complete release workflow (prep + git_push + gem_push)
10
10
  # rake release:prep # Run tests, build gem, create tag
11
11
  # rake release:git_push # Push to GitHub
12
- # rake release:gem_push # Publish to RubyGems
12
+ # rake release:gem_push # Publish e11y + e11y-devtools to RubyGems
13
+ # rake release:build_gems # Build both .gem packages (no tests)
13
14
  # rake spec:all # Run all test suites
14
15
  # rake spec:unit # Run unit tests only (fast)
15
16
  # rake spec:integration # Run integration tests
@@ -26,6 +27,9 @@ def e11y_devtools_specs_available?
26
27
  File.directory?(File.join(__dir__, "gems/e11y-devtools/spec"))
27
28
  end
28
29
 
30
+ # Built with: (cd gems/e11y-devtools && gem build โ€ฆ) โ€” .gem stays in this directory
31
+ E11Y_DEVTOOLS_GEM_DIR = File.expand_path("gems/e11y-devtools", __dir__).freeze
32
+
29
33
  RSpec::Core::RakeTask.new(:spec)
30
34
 
31
35
  RuboCop::RakeTask.new
@@ -277,6 +281,31 @@ namespace :release do
277
281
  puts "#{'=' * 80}\n"
278
282
  end
279
283
 
284
+ desc "Build e11y and e11y-devtools .gem files (no tests; devtools built in its directory)"
285
+ task :build_gems do
286
+ require_relative "lib/e11y/version"
287
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
288
+
289
+ puts "\n[build] e11y v#{E11y::VERSION}..."
290
+ unless system("gem build e11y.gemspec")
291
+ puts "โŒ Error: Failed to build e11y gem"
292
+ exit 1
293
+ end
294
+
295
+ puts "\n[build] e11y-devtools v#{E11y::Devtools::VERSION}..."
296
+ Dir.chdir(E11Y_DEVTOOLS_GEM_DIR) do
297
+ unless system("gem build e11y-devtools.gemspec")
298
+ puts "โŒ Error: Failed to build e11y-devtools gem"
299
+ exit 1
300
+ end
301
+ end
302
+
303
+ devtools_artifact = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{E11y::Devtools::VERSION}.gem")
304
+ puts "\nโœ… Built:"
305
+ puts " - e11y-#{E11y::VERSION}.gem"
306
+ puts " - #{devtools_artifact}"
307
+ end
308
+
280
309
  desc "Prepare release: run tests, build gem, create git tag (safe)"
281
310
  task :prep do
282
311
  require_relative "lib/e11y/version"
@@ -302,13 +331,11 @@ namespace :release do
302
331
  end
303
332
  puts "โœ… All tests passed"
304
333
 
305
- # Step 3: Build gem
306
- puts "\n[3/5] Building gem..."
307
- unless system("gem build e11y.gemspec")
308
- puts "โŒ Error: Failed to build gem"
309
- exit 1
310
- end
311
- puts "โœ… Gem built: e11y-#{version}.gem"
334
+ # Step 3: Build gems (e11y + e11y-devtools)
335
+ puts "\n[3/5] Building gems..."
336
+ Rake::Task["release:build_gems"].invoke
337
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
338
+ puts "โœ… Gems built: e11y-#{version}.gem + e11y-devtools-#{E11y::Devtools::VERSION}.gem"
312
339
 
313
340
  # Step 4: Create git tag
314
341
  puts "\n[4/5] Creating git tag..."
@@ -336,28 +363,108 @@ namespace :release do
336
363
  puts " git push origin main"
337
364
  puts " git push origin #{tag_name}"
338
365
  puts " 3. Publish to RubyGems:"
339
- puts " rake release:publish"
366
+ puts " rake release:gem_push"
340
367
  puts "\n"
341
368
  end
342
369
 
343
- desc "Publish gem to RubyGems.org (requires authentication, safe)"
370
+ namespace :rubygems do
371
+ desc "Publish e11y gem only"
372
+ task :push_core do
373
+ require_relative "lib/e11y/version"
374
+ gem_file = "e11y-#{E11y::VERSION}.gem"
375
+
376
+ puts "\n#{'=' * 80}"
377
+ puts "๐Ÿ“ค Publishing e11y v#{E11y::VERSION} to RubyGems.org"
378
+ puts "#{'=' * 80}\n"
379
+
380
+ unless File.exist?(gem_file)
381
+ puts "โŒ Error: Gem file not found: #{gem_file}"
382
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
383
+ exit 1
384
+ end
385
+
386
+ puts "This will publish #{gem_file}"
387
+ puts "You may be prompted for RubyGems credentials and MFA."
388
+ puts "\nContinue? (y/N)"
389
+
390
+ response = $stdin.gets.chomp.downcase
391
+ unless %w[y yes].include?(response)
392
+ puts "โŒ Publication cancelled"
393
+ exit 0
394
+ end
395
+
396
+ unless system("gem push #{gem_file}")
397
+ puts "\nโŒ Error: Failed to publish e11y"
398
+ exit 1
399
+ end
400
+
401
+ puts "\nโœ… Published e11y v#{E11y::VERSION}"
402
+ puts "Verify: https://rubygems.org/gems/e11y/versions/#{E11y::VERSION}"
403
+ end
404
+
405
+ desc "Publish e11y-devtools gem only"
406
+ task :push_devtools do
407
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
408
+ version = E11y::Devtools::VERSION
409
+ gem_file = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{version}.gem")
410
+
411
+ puts "\n#{'=' * 80}"
412
+ puts "๐Ÿ“ค Publishing e11y-devtools v#{version} to RubyGems.org"
413
+ puts "#{'=' * 80}\n"
414
+
415
+ unless File.exist?(gem_file)
416
+ puts "โŒ Error: Gem file not found: #{gem_file}"
417
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
418
+ exit 1
419
+ end
420
+
421
+ puts "This will publish #{gem_file}"
422
+ puts "You may be prompted for RubyGems credentials and MFA."
423
+ puts "\nContinue? (y/N)"
424
+
425
+ response = $stdin.gets.chomp.downcase
426
+ unless %w[y yes].include?(response)
427
+ puts "โŒ Publication cancelled"
428
+ exit 0
429
+ end
430
+
431
+ unless system("gem push #{gem_file}")
432
+ puts "\nโŒ Error: Failed to publish e11y-devtools"
433
+ exit 1
434
+ end
435
+
436
+ puts "\nโœ… Published e11y-devtools v#{version}"
437
+ puts "Verify: https://rubygems.org/gems/e11y-devtools/versions/#{version}"
438
+ end
439
+ end
440
+
441
+ desc "Publish e11y then e11y-devtools to RubyGems.org (requires authentication, MFA)"
344
442
  task :gem_push do
345
443
  require_relative "lib/e11y/version"
346
- version = E11y::VERSION
347
- gem_file = "e11y-#{version}.gem"
444
+ require_relative "gems/e11y-devtools/lib/e11y/devtools/version"
445
+
446
+ core_gem = "e11y-#{E11y::VERSION}.gem"
447
+ devtools_gem = File.join(E11Y_DEVTOOLS_GEM_DIR, "e11y-devtools-#{E11y::Devtools::VERSION}.gem")
348
448
 
349
449
  puts "\n#{'=' * 80}"
350
- puts "๐Ÿ“ค Publishing e11y v#{version} to RubyGems.org"
450
+ puts "๐Ÿ“ค Publishing to RubyGems.org"
351
451
  puts "#{'=' * 80}\n"
352
452
 
353
- unless File.exist?(gem_file)
354
- puts "โŒ Error: Gem file not found: #{gem_file}"
355
- puts "Run 'rake release:prep' first"
453
+ unless File.exist?(core_gem)
454
+ puts "โŒ Error: Gem file not found: #{core_gem}"
455
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
456
+ exit 1
457
+ end
458
+ unless File.exist?(devtools_gem)
459
+ puts "โŒ Error: Gem file not found: #{devtools_gem}"
460
+ puts "Run 'rake release:build_gems' or 'rake release:prep' first"
356
461
  exit 1
357
462
  end
358
463
 
359
- puts "This will publish #{gem_file} to RubyGems.org"
360
- puts "You will be prompted for your RubyGems credentials and MFA code."
464
+ puts "This will publish (e11y first, then e11y-devtools):"
465
+ puts " 1. #{core_gem}"
466
+ puts " 2. #{devtools_gem}"
467
+ puts "\nYou may be prompted for RubyGems credentials and MFA for each push."
361
468
  puts "\nContinue? (y/N)"
362
469
 
363
470
  response = $stdin.gets.chomp.downcase
@@ -366,17 +473,19 @@ namespace :release do
366
473
  exit 0
367
474
  end
368
475
 
369
- unless system("gem push #{gem_file}")
370
- puts "\nโŒ Error: Failed to publish gem"
371
- puts "Make sure you have:"
372
- puts " 1. RubyGems account (https://rubygems.org/sign_up)"
373
- puts " 2. Signed in: gem signin"
374
- puts " 3. MFA enabled on your account"
476
+ unless system("gem push #{core_gem}")
477
+ puts "\nโŒ Error: Failed to publish e11y"
478
+ exit 1
479
+ end
480
+
481
+ unless system("gem push #{devtools_gem}")
482
+ puts "\nโŒ Error: Failed to publish e11y-devtools (e11y may already be on RubyGems)"
375
483
  exit 1
376
484
  end
377
485
 
378
- puts "\nโœ… Successfully published e11y v#{version} to RubyGems.org!"
379
- puts "\nVerify: https://rubygems.org/gems/e11y/versions/#{version}"
486
+ puts "\nโœ… Successfully published both gems!"
487
+ puts " e11y: https://rubygems.org/gems/e11y/versions/#{E11y::VERSION}"
488
+ puts " e11y-devtools: https://rubygems.org/gems/e11y-devtools/versions/#{E11y::Devtools::VERSION}"
380
489
  end
381
490
 
382
491
  desc "Push git changes and tag to GitHub (safe)"
@@ -442,19 +551,23 @@ namespace :release do
442
551
  puts "=" * 80
443
552
  puts "\nPost-release tasks:"
444
553
  puts " 1. Create GitHub release: https://github.com/arturseletskiy/e11y/releases/new"
445
- puts " 2. Verify on RubyGems: https://rubygems.org/gems/e11y"
554
+ puts " 2. Verify on RubyGems: https://rubygems.org/gems/e11y and /gems/e11y-devtools"
446
555
  puts " 3. Update README badges"
447
556
  puts " 4. Announce on social media"
448
557
  puts "\n"
449
558
  end
450
559
 
451
- desc "Clean up built gems"
560
+ desc "Clean up built gem files (repo root + gems/e11y-devtools)"
452
561
  task :clean do
453
562
  puts "๐Ÿงน Cleaning up gem files..."
454
563
  FileList["*.gem"].each do |gem_file|
455
564
  File.delete(gem_file)
456
565
  puts " Deleted: #{gem_file}"
457
566
  end
567
+ FileList[File.join(E11Y_DEVTOOLS_GEM_DIR, "*.gem")].each do |gem_file|
568
+ File.delete(gem_file)
569
+ puts " Deleted: #{gem_file}"
570
+ end
458
571
  puts "โœ… Clean complete"
459
572
  end
460
573
  end
data/docs/QUICK-START.md CHANGED
@@ -860,7 +860,7 @@ E11y.config.adapters[:logs].healthy?
860
860
  | OTelLogs payload attributes | All payload attributes now included in OTel log records |
861
861
  | `config.slo_tracking = true` | Boolean coercion now accepted |
862
862
  | `retention` / `retention_period` | Both work as aliases on event class |
863
- | `config.slo do` | SLO configuration block DSL |
863
+ | `add_slo_controller` / `add_slo_job` | Helpers on `E11y::Configuration` (stored config; see UC-004) |
864
864
  | `config.rate_limiting do` | Rate limiting block DSL |
865
865
  | `config.cardinality_protection do` | Cardinality DSL block |
866
866
  | `config.register_adapter` | Alias for `config.adapters[name] =` |
@@ -878,7 +878,7 @@ The following features are **documented in ADRs** but not yet implemented:
878
878
  |---------|--------|
879
879
  | `rails g e11y:grafana_dashboard` | ADR-003 |
880
880
  | `rails g e11y:prometheus_alerts` | ADR-003 |
881
- | `config.slo do controller ... job ... end` โ€” per-controller SLO config | ADR-003 |
881
+ | Wire `add_slo_controller` / `add_slo_job` into HTTP/job `Tracker` dimensions | UC-004, ADR-003 |
882
882
  | Per-event rate limiting (`rate_limit` DSL on event class) | UC-011 |
883
883
  | Tiered storage (archival) | UC-019 โ€” filter by `retention_until` |
884
884
  | Cost Tracking / Budget Enforcement | ADR-009, UC-015 |
@@ -2,28 +2,78 @@
2
2
 
3
3
  > Back to [README](../README.md#documentation)
4
4
 
5
- E11y integrates with Rails via Railtie.
5
+ E11y integrates with Rails via `E11y::Railtie` (`lib/e11y/railtie.rb`). After `bundle install`, requiring the gem in a Rails app loads the Railtie automatically.
6
6
 
7
- ## Auto-Instrumented Components
7
+ ## Request middleware
8
8
 
9
- E11y includes event definitions for common Rails components:
9
+ `E11y::Middleware::Request` is inserted into the Rack stack (before `Rails::Rack::Logger` when present). It sets trace and request context on `E11y::Current`, optionally starts the **ephemeral (request-scoped) buffer** for debug events, and adds `X-E11y-Trace-Id` / `X-E11y-Span-Id` response headers.
10
10
 
11
- | Component | Event Classes | Location |
12
- |-----------|--------------|----------|
13
- | **HTTP Requests** | Request, StartProcessing, Redirect, SendFile | `lib/e11y/events/rails/http/` |
14
- | **ActiveRecord** | Query | `lib/e11y/events/rails/database/` |
15
- | **ActiveJob** | Enqueued, Started, Completed, Failed, Scheduled | `lib/e11y/events/rails/job/` |
16
- | **Cache** | Read, Write, Delete | `lib/e11y/events/rails/cache/` |
17
- | **View** | Render | `lib/e11y/events/rails/view/` |
11
+ ## Rails instrumentation (`ActiveSupport::Notifications`)
18
12
 
19
- Enable instrumentation in your configuration as needed.
13
+ When **`config.rails_instrumentation_enabled = true`**, E11y subscribes to Rails instrumentation and maps notifications to typed events (see `lib/e11y/instruments/rails_instrumentation.rb`).
20
14
 
21
- ## Sidekiq Integration
15
+ | Area | Event classes (under `E11y::Events::Rails::`) |
16
+ |------|-----------------------------------------------|
17
+ | HTTP | `Http::Request`, `Http::StartProcessing`, `Http::Redirect`, `Http::SendFile` |
18
+ | Database | `Database::Query` |
19
+ | Active Job (notification names) | `Job::Enqueued`, `Job::Scheduled`, `Job::Started`, `Job::Completed`, `Job::Failed` |
20
+ | Cache | `Cache::Read`, `Cache::Write`, `Cache::Delete` |
21
+ | Views | `View::Render` |
22
22
 
23
- E11y includes Sidekiq instrumentation support. Configure in your initializer:
23
+ This is **independent** of the Sidekiq and Active Job toggles below: instrumentation listens to Rails; the job toggles add **extra** process integration (buffer lifecycle, middleware, callbacks).
24
+
25
+ ## Sidekiq
26
+
27
+ Enable **only if** you use Sidekiq:
28
+
29
+ ```ruby
30
+ E11y.configure do |config|
31
+ config.sidekiq_enabled = true
32
+ end
33
+ ```
34
+
35
+ The Railtie registers client and server middleware (`E11y::Instruments::Sidekiq`) so jobs participate in the same **ephemeral buffer** semantics as HTTP requests when `config.ephemeral_buffer_enabled` is true.
36
+
37
+ On enqueue, **`E11y::Current.user_id`** (when set, e.g. from request middleware) is merged into **`e11y_baggage`** together with any allowed `Current.baggage` keys. The worker restores **`E11y::Current.baggage`** and **`E11y::Current.user_id`** from that hash. Key **`user_id`** is in the default baggage allowlist (`E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS`).
38
+
39
+ ## Active Job
40
+
41
+ Enable when you want callbacks and buffer handling on **`ActiveJob::Base`** (and **`ApplicationJob`** when that constant is already defined at hook time):
42
+
43
+ ```ruby
44
+ E11y.configure do |config|
45
+ config.active_job_enabled = true
46
+ end
47
+ ```
48
+
49
+ You can use **both** `rails_instrumentation_enabled` and `active_job_enabled`; they complement each other. If you only enqueue via Sidekiq without Active Job, you may rely on `sidekiq_enabled` alone.
50
+
51
+ The **`before_enqueue`** callback applies the same **`e11y_baggage`** merge as Sidekiq (including **`user_id`** from `E11y::Current`).
52
+
53
+ ## Rails.logger bridge
54
+
55
+ Optional wrapper that still delegates to the original logger but also emits **`E11y::Events::Rails::Log::*`** events (`lib/e11y/events/rails/log.rb`):
24
56
 
25
57
  ```ruby
26
58
  E11y.configure do |config|
27
- config.rails_instrumentation_enabled = true
59
+ config.logger_bridge_enabled = true
60
+ # Optional: only these severities (Symbol or String); nil / empty = all
61
+ config.logger_bridge_track_severities = %i[warn error fatal]
62
+ # Optional: skip noisy lines (String substrings or Regexp)
63
+ config.logger_bridge_ignore_patterns = [%r{health}]
28
64
  end
29
65
  ```
66
+
67
+ Filtering uses **`logger_bridge_track_severities`** and **`logger_bridge_ignore_patterns`** only.
68
+
69
+ ## Configuration reference
70
+
71
+ | Flag | Purpose |
72
+ |------|---------|
73
+ | `rails_instrumentation_enabled` | Map `ActiveSupport::Notifications` to E11y events |
74
+ | `sidekiq_enabled` | Sidekiq client/server middleware |
75
+ | `active_job_enabled` | `ActiveJob::Base` / `ApplicationJob` callbacks |
76
+ | `logger_bridge_enabled` | Wrap `Rails.logger` with `E11y::Logger::Bridge` |
77
+ | `ephemeral_buffer_enabled` | Request/job-scoped debug buffer (see README) |
78
+
79
+ Further detail: [ADR-008: Rails integration](architecture/ADR-008-rails-integration.md), [Quick Start](QUICK-START.md).
@@ -2351,7 +2351,7 @@ end
2351
2351
  **Middleware checks opt-out flag before processing:**
2352
2352
 
2353
2353
  ```ruby
2354
- # E11y::Middleware::PIIFiltering
2354
+ # E11y::Middleware::PIIFilter
2355
2355
  def call(event_data)
2356
2356
  event_class = event_data[:event_class]
2357
2357