e11y 0.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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +69 -0
  4. data/CHANGELOG.md +26 -0
  5. data/CODE_OF_CONDUCT.md +64 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +179 -0
  8. data/Rakefile +37 -0
  9. data/benchmarks/run_all.rb +33 -0
  10. data/config/README.md +83 -0
  11. data/config/loki-local-config.yaml +35 -0
  12. data/config/prometheus.yml +15 -0
  13. data/docker-compose.yml +78 -0
  14. data/docs/00-ICP-AND-TIMELINE.md +483 -0
  15. data/docs/01-SCALE-REQUIREMENTS.md +858 -0
  16. data/docs/ADR-001-architecture.md +2617 -0
  17. data/docs/ADR-002-metrics-yabeda.md +1395 -0
  18. data/docs/ADR-003-slo-observability.md +3337 -0
  19. data/docs/ADR-004-adapter-architecture.md +2385 -0
  20. data/docs/ADR-005-tracing-context.md +1372 -0
  21. data/docs/ADR-006-security-compliance.md +4143 -0
  22. data/docs/ADR-007-opentelemetry-integration.md +1385 -0
  23. data/docs/ADR-008-rails-integration.md +1911 -0
  24. data/docs/ADR-009-cost-optimization.md +2993 -0
  25. data/docs/ADR-010-developer-experience.md +2166 -0
  26. data/docs/ADR-011-testing-strategy.md +1836 -0
  27. data/docs/ADR-012-event-evolution.md +958 -0
  28. data/docs/ADR-013-reliability-error-handling.md +2750 -0
  29. data/docs/ADR-014-event-driven-slo.md +1533 -0
  30. data/docs/ADR-015-middleware-order.md +1061 -0
  31. data/docs/ADR-016-self-monitoring-slo.md +1234 -0
  32. data/docs/API-REFERENCE-L28.md +914 -0
  33. data/docs/COMPREHENSIVE-CONFIGURATION.md +2366 -0
  34. data/docs/IMPLEMENTATION_NOTES.md +2804 -0
  35. data/docs/IMPLEMENTATION_PLAN.md +1971 -0
  36. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +586 -0
  37. data/docs/PLAN.md +148 -0
  38. data/docs/QUICK-START.md +934 -0
  39. data/docs/README.md +296 -0
  40. data/docs/design/00-memory-optimization.md +593 -0
  41. data/docs/guides/MIGRATION-L27-L28.md +692 -0
  42. data/docs/guides/PERFORMANCE-BENCHMARKS.md +434 -0
  43. data/docs/guides/README.md +44 -0
  44. data/docs/prd/01-overview-vision.md +440 -0
  45. data/docs/use_cases/README.md +119 -0
  46. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +813 -0
  47. data/docs/use_cases/UC-002-business-event-tracking.md +1953 -0
  48. data/docs/use_cases/UC-003-pattern-based-metrics.md +1627 -0
  49. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +728 -0
  50. data/docs/use_cases/UC-005-sentry-integration.md +759 -0
  51. data/docs/use_cases/UC-006-trace-context-management.md +905 -0
  52. data/docs/use_cases/UC-007-pii-filtering.md +2648 -0
  53. data/docs/use_cases/UC-008-opentelemetry-integration.md +1153 -0
  54. data/docs/use_cases/UC-009-multi-service-tracing.md +1043 -0
  55. data/docs/use_cases/UC-010-background-job-tracking.md +1018 -0
  56. data/docs/use_cases/UC-011-rate-limiting.md +1906 -0
  57. data/docs/use_cases/UC-012-audit-trail.md +2301 -0
  58. data/docs/use_cases/UC-013-high-cardinality-protection.md +2127 -0
  59. data/docs/use_cases/UC-014-adaptive-sampling.md +1940 -0
  60. data/docs/use_cases/UC-015-cost-optimization.md +735 -0
  61. data/docs/use_cases/UC-016-rails-logger-migration.md +785 -0
  62. data/docs/use_cases/UC-017-local-development.md +867 -0
  63. data/docs/use_cases/UC-018-testing-events.md +1081 -0
  64. data/docs/use_cases/UC-019-tiered-storage-migration.md +562 -0
  65. data/docs/use_cases/UC-020-event-versioning.md +708 -0
  66. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +956 -0
  67. data/docs/use_cases/UC-022-event-registry.md +648 -0
  68. data/docs/use_cases/backlog.md +226 -0
  69. data/e11y.gemspec +76 -0
  70. data/lib/e11y/adapters/adaptive_batcher.rb +207 -0
  71. data/lib/e11y/adapters/audit_encrypted.rb +239 -0
  72. data/lib/e11y/adapters/base.rb +580 -0
  73. data/lib/e11y/adapters/file.rb +224 -0
  74. data/lib/e11y/adapters/in_memory.rb +216 -0
  75. data/lib/e11y/adapters/loki.rb +333 -0
  76. data/lib/e11y/adapters/otel_logs.rb +203 -0
  77. data/lib/e11y/adapters/registry.rb +141 -0
  78. data/lib/e11y/adapters/sentry.rb +230 -0
  79. data/lib/e11y/adapters/stdout.rb +108 -0
  80. data/lib/e11y/adapters/yabeda.rb +370 -0
  81. data/lib/e11y/buffers/adaptive_buffer.rb +339 -0
  82. data/lib/e11y/buffers/base_buffer.rb +40 -0
  83. data/lib/e11y/buffers/request_scoped_buffer.rb +246 -0
  84. data/lib/e11y/buffers/ring_buffer.rb +267 -0
  85. data/lib/e11y/buffers.rb +14 -0
  86. data/lib/e11y/console.rb +122 -0
  87. data/lib/e11y/current.rb +48 -0
  88. data/lib/e11y/event/base.rb +894 -0
  89. data/lib/e11y/event/value_sampling_config.rb +84 -0
  90. data/lib/e11y/events/base_audit_event.rb +43 -0
  91. data/lib/e11y/events/base_payment_event.rb +33 -0
  92. data/lib/e11y/events/rails/cache/delete.rb +21 -0
  93. data/lib/e11y/events/rails/cache/read.rb +23 -0
  94. data/lib/e11y/events/rails/cache/write.rb +22 -0
  95. data/lib/e11y/events/rails/database/query.rb +45 -0
  96. data/lib/e11y/events/rails/http/redirect.rb +21 -0
  97. data/lib/e11y/events/rails/http/request.rb +26 -0
  98. data/lib/e11y/events/rails/http/send_file.rb +21 -0
  99. data/lib/e11y/events/rails/http/start_processing.rb +26 -0
  100. data/lib/e11y/events/rails/job/completed.rb +22 -0
  101. data/lib/e11y/events/rails/job/enqueued.rb +22 -0
  102. data/lib/e11y/events/rails/job/failed.rb +22 -0
  103. data/lib/e11y/events/rails/job/scheduled.rb +23 -0
  104. data/lib/e11y/events/rails/job/started.rb +22 -0
  105. data/lib/e11y/events/rails/log.rb +56 -0
  106. data/lib/e11y/events/rails/view/render.rb +23 -0
  107. data/lib/e11y/events.rb +18 -0
  108. data/lib/e11y/instruments/active_job.rb +201 -0
  109. data/lib/e11y/instruments/rails_instrumentation.rb +141 -0
  110. data/lib/e11y/instruments/sidekiq.rb +175 -0
  111. data/lib/e11y/logger/bridge.rb +205 -0
  112. data/lib/e11y/metrics/cardinality_protection.rb +172 -0
  113. data/lib/e11y/metrics/cardinality_tracker.rb +134 -0
  114. data/lib/e11y/metrics/registry.rb +234 -0
  115. data/lib/e11y/metrics/relabeling.rb +226 -0
  116. data/lib/e11y/metrics.rb +102 -0
  117. data/lib/e11y/middleware/audit_signing.rb +174 -0
  118. data/lib/e11y/middleware/base.rb +140 -0
  119. data/lib/e11y/middleware/event_slo.rb +167 -0
  120. data/lib/e11y/middleware/pii_filter.rb +266 -0
  121. data/lib/e11y/middleware/pii_filtering.rb +280 -0
  122. data/lib/e11y/middleware/rate_limiting.rb +214 -0
  123. data/lib/e11y/middleware/request.rb +163 -0
  124. data/lib/e11y/middleware/routing.rb +157 -0
  125. data/lib/e11y/middleware/sampling.rb +254 -0
  126. data/lib/e11y/middleware/slo.rb +168 -0
  127. data/lib/e11y/middleware/trace_context.rb +131 -0
  128. data/lib/e11y/middleware/validation.rb +118 -0
  129. data/lib/e11y/middleware/versioning.rb +132 -0
  130. data/lib/e11y/middleware.rb +12 -0
  131. data/lib/e11y/pii/patterns.rb +90 -0
  132. data/lib/e11y/pii.rb +13 -0
  133. data/lib/e11y/pipeline/builder.rb +155 -0
  134. data/lib/e11y/pipeline/zone_validator.rb +110 -0
  135. data/lib/e11y/pipeline.rb +12 -0
  136. data/lib/e11y/presets/audit_event.rb +65 -0
  137. data/lib/e11y/presets/debug_event.rb +34 -0
  138. data/lib/e11y/presets/high_value_event.rb +51 -0
  139. data/lib/e11y/presets.rb +19 -0
  140. data/lib/e11y/railtie.rb +138 -0
  141. data/lib/e11y/reliability/circuit_breaker.rb +216 -0
  142. data/lib/e11y/reliability/dlq/file_storage.rb +277 -0
  143. data/lib/e11y/reliability/dlq/filter.rb +117 -0
  144. data/lib/e11y/reliability/retry_handler.rb +207 -0
  145. data/lib/e11y/reliability/retry_rate_limiter.rb +117 -0
  146. data/lib/e11y/sampling/error_spike_detector.rb +225 -0
  147. data/lib/e11y/sampling/load_monitor.rb +161 -0
  148. data/lib/e11y/sampling/stratified_tracker.rb +92 -0
  149. data/lib/e11y/sampling/value_extractor.rb +82 -0
  150. data/lib/e11y/self_monitoring/buffer_monitor.rb +79 -0
  151. data/lib/e11y/self_monitoring/performance_monitor.rb +97 -0
  152. data/lib/e11y/self_monitoring/reliability_monitor.rb +146 -0
  153. data/lib/e11y/slo/event_driven.rb +150 -0
  154. data/lib/e11y/slo/tracker.rb +119 -0
  155. data/lib/e11y/version.rb +9 -0
  156. data/lib/e11y.rb +283 -0
  157. metadata +452 -0
@@ -0,0 +1,914 @@
1
+ # API Reference: Advanced Sampling (Phase 2.8)
2
+
3
+ **Version:** 1.0
4
+ **Date:** January 20, 2026
5
+ **Applies to:** E11y gem v0.8.0+
6
+
7
+ ---
8
+
9
+ ## ๐Ÿ“‹ Overview
10
+
11
+ This document provides complete API reference for all classes, modules, and methods introduced in Phase 2.8 (Advanced Sampling Strategies):
12
+
13
+ - **FEAT-4838**: Error-Based Adaptive Sampling
14
+ - **FEAT-4842**: Load-Based Adaptive Sampling
15
+ - **FEAT-4846**: Value-Based Sampling
16
+ - **FEAT-4850**: Stratified Sampling for SLO Accuracy
17
+
18
+ ---
19
+
20
+ ## ๐Ÿ” Table of Contents
21
+
22
+ 1. [E11y::Sampling::ErrorSpikeDetector](#e11ysamplingerrorspikedetector)
23
+ 2. [E11y::Sampling::LoadMonitor](#e11ysamplingloadmonitor)
24
+ 3. [E11y::Sampling::ValueExtractor](#e11ysamplingvalueextractor)
25
+ 4. [E11y::Event::ValueSamplingConfig](#e11yeventvaluesamplingconfig)
26
+ 5. [E11y::Sampling::StratifiedTracker](#e11ysamplingrifiedtracker)
27
+ 6. [E11y::Middleware::Sampling](#e11ymiddlewaresampling)
28
+ 7. [E11y::Event::Base (Extended DSL)](#e11yeventbase-extended-dsl)
29
+
30
+ ---
31
+
32
+ ## E11y::Sampling::ErrorSpikeDetector
33
+
34
+ Detects error rate spikes using sliding windows and configurable absolute/relative thresholds.
35
+
36
+ ### Location
37
+
38
+ ```
39
+ lib/e11y/sampling/error_spike_detector.rb
40
+ ```
41
+
42
+ ### Class Definition
43
+
44
+ ```ruby
45
+ module E11y
46
+ module Sampling
47
+ class ErrorSpikeDetector
48
+ include MonitorMixin
49
+ end
50
+ end
51
+ end
52
+ ```
53
+
54
+ ### Constructor
55
+
56
+ ```ruby
57
+ def initialize(config = {})
58
+ ```
59
+
60
+ **Parameters:**
61
+ - `config` (Hash): Configuration options
62
+ - `:window` (Integer): Sliding window duration in seconds (default: 60)
63
+ - `:absolute_threshold` (Integer): Absolute error threshold (errors/min) (default: 100)
64
+ - `:relative_threshold` (Float): Relative threshold (ratio to baseline) (default: 3.0)
65
+ - `:spike_duration` (Integer): How long to maintain 100% sampling (default: 300)
66
+
67
+ **Example:**
68
+ ```ruby
69
+ detector = E11y::Sampling::ErrorSpikeDetector.new(
70
+ window: 60,
71
+ absolute_threshold: 100,
72
+ relative_threshold: 3.0,
73
+ spike_duration: 300
74
+ )
75
+ ```
76
+
77
+ ---
78
+
79
+ ### Instance Methods
80
+
81
+ #### `#error_spike?` โ†’ Boolean
82
+
83
+ Determines if an error spike is currently occurring.
84
+
85
+ **Returns:**
86
+ - `true` if error spike detected
87
+ - `false` otherwise
88
+
89
+ **Example:**
90
+ ```ruby
91
+ if detector.error_spike?
92
+ # Increase sampling to 100%
93
+ end
94
+ ```
95
+
96
+ ---
97
+
98
+ #### `#record_event(event_data)` โ†’ void
99
+
100
+ Records an event for error rate calculation.
101
+
102
+ **Parameters:**
103
+ - `event_data` (Hash): Event data with `:severity` key
104
+
105
+ **Example:**
106
+ ```ruby
107
+ detector.record_event({ severity: :error, message: "Payment failed" })
108
+ ```
109
+
110
+ ---
111
+
112
+ #### `#current_error_rate` โ†’ Float
113
+
114
+ Calculates current error rate (errors per minute).
115
+
116
+ **Returns:**
117
+ - Float: Current error rate
118
+
119
+ **Example:**
120
+ ```ruby
121
+ detector.current_error_rate # => 150.5 (errors/min)
122
+ ```
123
+
124
+ ---
125
+
126
+ #### `#baseline_error_rate` โ†’ Float
127
+
128
+ Returns the baseline error rate (exponential moving average).
129
+
130
+ **Returns:**
131
+ - Float: Baseline error rate
132
+
133
+ **Example:**
134
+ ```ruby
135
+ detector.baseline_error_rate # => 50.0 (errors/min)
136
+ ```
137
+
138
+ ---
139
+
140
+ #### `#reset!` โ†’ void
141
+
142
+ Resets the detector state (clears all events and baseline).
143
+
144
+ **Example:**
145
+ ```ruby
146
+ detector.reset!
147
+ ```
148
+
149
+ ---
150
+
151
+ ### Thread Safety
152
+
153
+ This class uses `MonitorMixin` for thread-safe operations. All public methods acquire a mutex before accessing shared state.
154
+
155
+ ---
156
+
157
+ ## E11y::Sampling::LoadMonitor
158
+
159
+ Tracks event volume over a sliding window and determines load levels with recommended sampling rates.
160
+
161
+ ### Location
162
+
163
+ ```
164
+ lib/e11y/sampling/load_monitor.rb
165
+ ```
166
+
167
+ ### Class Definition
168
+
169
+ ```ruby
170
+ module E11y
171
+ module Sampling
172
+ class LoadMonitor
173
+ include MonitorMixin
174
+ end
175
+ end
176
+ end
177
+ ```
178
+
179
+ ### Constructor
180
+
181
+ ```ruby
182
+ def initialize(config = {})
183
+ ```
184
+
185
+ **Parameters:**
186
+ - `config` (Hash): Configuration options
187
+ - `:window` (Integer): Sliding window duration in seconds (default: 60)
188
+ - `:normal_threshold` (Integer): Normal load threshold (events/sec) (default: 1,000)
189
+ - `:high_threshold` (Integer): High load threshold (events/sec) (default: 10,000)
190
+ - `:very_high_threshold` (Integer): Very high load threshold (events/sec) (default: 50,000)
191
+ - `:overload_threshold` (Integer): Overload threshold (events/sec) (default: 100,000)
192
+
193
+ **Example:**
194
+ ```ruby
195
+ monitor = E11y::Sampling::LoadMonitor.new(
196
+ window: 60,
197
+ normal_threshold: 1_000,
198
+ high_threshold: 10_000,
199
+ very_high_threshold: 50_000,
200
+ overload_threshold: 100_000
201
+ )
202
+ ```
203
+
204
+ ---
205
+
206
+ ### Instance Methods
207
+
208
+ #### `#record_event` โ†’ void
209
+
210
+ Records an event for load calculation.
211
+
212
+ **Example:**
213
+ ```ruby
214
+ monitor.record_event
215
+ ```
216
+
217
+ ---
218
+
219
+ #### `#current_rate` โ†’ Float
220
+
221
+ Calculates current event rate (events per second).
222
+
223
+ **Returns:**
224
+ - Float: Current event rate
225
+
226
+ **Example:**
227
+ ```ruby
228
+ monitor.current_rate # => 15_000.5 (events/sec)
229
+ ```
230
+
231
+ ---
232
+
233
+ #### `#load_level` โ†’ Symbol
234
+
235
+ Determines current load level based on event rate.
236
+
237
+ **Returns:**
238
+ - Symbol: One of `:normal`, `:high`, `:very_high`, or `:overload`
239
+
240
+ **Example:**
241
+ ```ruby
242
+ monitor.load_level # => :high
243
+ ```
244
+
245
+ ---
246
+
247
+ #### `#recommended_sample_rate` โ†’ Float
248
+
249
+ Gets recommended sample rate for current load level.
250
+
251
+ **Returns:**
252
+ - Float: Recommended sample rate (0.0 - 1.0)
253
+
254
+ | Load Level | Sample Rate |
255
+ |-----------|-------------|
256
+ | `:normal` | 1.0 (100%) |
257
+ | `:high` | 0.5 (50%) |
258
+ | `:very_high` | 0.1 (10%) |
259
+ | `:overload` | 0.01 (1%) |
260
+
261
+ **Example:**
262
+ ```ruby
263
+ monitor.recommended_sample_rate # => 0.5 (50%)
264
+ ```
265
+
266
+ ---
267
+
268
+ #### `#overloaded?` โ†’ Boolean
269
+
270
+ Checks if system is currently overloaded.
271
+
272
+ **Returns:**
273
+ - `true` if load level is `:overload`
274
+ - `false` otherwise
275
+
276
+ **Example:**
277
+ ```ruby
278
+ if monitor.overloaded?
279
+ # Apply aggressive sampling
280
+ end
281
+ ```
282
+
283
+ ---
284
+
285
+ #### `#stats` โ†’ Hash
286
+
287
+ Returns statistics about current load.
288
+
289
+ **Returns:**
290
+ - Hash with keys:
291
+ - `:current_rate` (Float): Events per second
292
+ - `:load_level` (Symbol): Current load level
293
+ - `:recommended_sample_rate` (Float): Recommended rate
294
+
295
+ **Example:**
296
+ ```ruby
297
+ monitor.stats
298
+ # => {
299
+ # current_rate: 15000.5,
300
+ # load_level: :high,
301
+ # recommended_sample_rate: 0.5
302
+ # }
303
+ ```
304
+
305
+ ---
306
+
307
+ #### `#reset!` โ†’ void
308
+
309
+ Resets the monitor state (clears all events).
310
+
311
+ **Example:**
312
+ ```ruby
313
+ monitor.reset!
314
+ ```
315
+
316
+ ---
317
+
318
+ ## E11y::Sampling::ValueExtractor
319
+
320
+ Extracts numeric values from nested event payloads for value-based sampling.
321
+
322
+ ### Location
323
+
324
+ ```
325
+ lib/e11y/sampling/value_extractor.rb
326
+ ```
327
+
328
+ ### Class Methods
329
+
330
+ #### `.extract(payload, field_path)` โ†’ Float
331
+
332
+ Extracts a numeric value from a (potentially nested) payload.
333
+
334
+ **Parameters:**
335
+ - `payload` (Hash): Event payload
336
+ - `field_path` (String): Dot-separated field path (e.g., `"order.amount"`)
337
+
338
+ **Returns:**
339
+ - Float: Extracted value (or 0.0 if not found/nil)
340
+
341
+ **Type Coercion:**
342
+ - Numeric strings โ†’ Float (e.g., `"5000"` โ†’ `5000.0`)
343
+ - Nil/missing โ†’ `0.0`
344
+ - Non-numeric โ†’ `0.0` (fallback)
345
+
346
+ **Examples:**
347
+
348
+ ```ruby
349
+ # Flat field
350
+ E11y::Sampling::ValueExtractor.extract({ "amount" => 5000 }, "amount")
351
+ # => 5000.0
352
+
353
+ # Nested field (dot notation)
354
+ E11y::Sampling::ValueExtractor.extract(
355
+ { "order" => { "amount" => 1500 } },
356
+ "order.amount"
357
+ )
358
+ # => 1500.0
359
+
360
+ # Numeric string (type coercion)
361
+ E11y::Sampling::ValueExtractor.extract({ "amount" => "5000" }, "amount")
362
+ # => 5000.0
363
+
364
+ # Nil/missing value
365
+ E11y::Sampling::ValueExtractor.extract({ "foo" => "bar" }, "amount")
366
+ # => 0.0
367
+ ```
368
+
369
+ ---
370
+
371
+ ## E11y::Event::ValueSamplingConfig
372
+
373
+ Configuration for value-based sampling rules per event class.
374
+
375
+ ### Location
376
+
377
+ ```
378
+ lib/e11y/event/value_sampling_config.rb
379
+ ```
380
+
381
+ ### Class Definition
382
+
383
+ ```ruby
384
+ module E11y
385
+ module Event
386
+ class ValueSamplingConfig
387
+ attr_reader :field, :operator, :threshold, :sample_rate
388
+ end
389
+ end
390
+ end
391
+ ```
392
+
393
+ ### Constructor
394
+
395
+ ```ruby
396
+ def initialize(field:, operator:, threshold:, sample_rate: 1.0)
397
+ ```
398
+
399
+ **Parameters:**
400
+ - `field` (String): Dot-separated field path
401
+ - `operator` (Symbol): Comparison operator (`:greater_than`, `:less_than`, `:equals`, `:in_range`)
402
+ - `threshold` (Numeric, Range, String): Value or range to compare against
403
+ - `sample_rate` (Float): Sample rate to apply if criteria met (default: 1.0)
404
+
405
+ **Example:**
406
+ ```ruby
407
+ config = E11y::Event::ValueSamplingConfig.new(
408
+ field: "amount",
409
+ operator: :greater_than,
410
+ threshold: 1000,
411
+ sample_rate: 1.0
412
+ )
413
+ ```
414
+
415
+ ---
416
+
417
+ ### Instance Methods
418
+
419
+ #### `#matches?(event_data)` โ†’ Boolean
420
+
421
+ Determines if an event's value meets the sampling criteria.
422
+
423
+ **Parameters:**
424
+ - `event_data` (Hash): Event data with payload
425
+
426
+ **Returns:**
427
+ - `true` if value meets criteria (should sample)
428
+ - `false` otherwise
429
+
430
+ **Examples:**
431
+
432
+ ```ruby
433
+ # Greater than
434
+ config = ValueSamplingConfig.new(field: "amount", operator: :greater_than, threshold: 1000)
435
+ config.matches?({ payload: { "amount" => 5000 } }) # => true
436
+ config.matches?({ payload: { "amount" => 500 } }) # => false
437
+
438
+ # Equals
439
+ config = ValueSamplingConfig.new(field: "user_segment", operator: :equals, threshold: "enterprise")
440
+ config.matches?({ payload: { "user_segment" => "enterprise" } }) # => true
441
+ config.matches?({ payload: { "user_segment" => "free" } }) # => false
442
+
443
+ # In range
444
+ config = ValueSamplingConfig.new(field: "amount", operator: :in_range, threshold: 100..500)
445
+ config.matches?({ payload: { "amount" => 250 } }) # => true
446
+ config.matches?({ payload: { "amount" => 750 } }) # => false
447
+ ```
448
+
449
+ ---
450
+
451
+ ### Supported Operators
452
+
453
+ | Operator | Description | Threshold Type | Example |
454
+ |----------|-------------|---------------|---------|
455
+ | `:greater_than` | Value > threshold | Numeric | `amount > 1000` |
456
+ | `:less_than` | Value < threshold | Numeric | `latency_ms < 100` |
457
+ | `:equals` | Value == threshold | Any | `user_segment == "enterprise"` |
458
+ | `:in_range` | Value in range | Range | `amount in 100..500` |
459
+
460
+ ---
461
+
462
+ ## E11y::Sampling::StratifiedTracker
463
+
464
+ Tracks sampled and total counts per severity stratum for SLO sampling correction.
465
+
466
+ ### Location
467
+
468
+ ```
469
+ lib/e11y/sampling/stratified_tracker.rb
470
+ ```
471
+
472
+ ### Class Definition
473
+
474
+ ```ruby
475
+ module E11y
476
+ module Sampling
477
+ class StratifiedTracker
478
+ include MonitorMixin
479
+ end
480
+ end
481
+ end
482
+ ```
483
+
484
+ ### Constructor
485
+
486
+ ```ruby
487
+ def initialize
488
+ ```
489
+
490
+ **Example:**
491
+ ```ruby
492
+ tracker = E11y::Sampling::StratifiedTracker.new
493
+ ```
494
+
495
+ ---
496
+
497
+ ### Instance Methods
498
+
499
+ #### `#record_sample(severity:, sample_rate:)` โ†’ void
500
+
501
+ Records a sampled event with its original sample rate.
502
+
503
+ **Parameters:**
504
+ - `severity` (Symbol): Event severity (`:debug`, `:info`, `:warn`, `:error`, `:fatal`)
505
+ - `sample_rate` (Float): Original sample rate (0.0 - 1.0)
506
+
507
+ **Example:**
508
+ ```ruby
509
+ tracker.record_sample(severity: :info, sample_rate: 0.1) # 10% sampled
510
+ tracker.record_sample(severity: :error, sample_rate: 1.0) # 100% sampled
511
+ ```
512
+
513
+ ---
514
+
515
+ #### `#sampling_correction(severity)` โ†’ Float
516
+
517
+ Calculates sampling correction factor for a given severity.
518
+
519
+ **Parameters:**
520
+ - `severity` (Symbol): Event severity
521
+
522
+ **Returns:**
523
+ - Float: Correction factor (1.0 / average_sample_rate)
524
+
525
+ **Formula:**
526
+ ```
527
+ correction_factor = total_sampled_count / sum_of_sample_rates
528
+ ```
529
+
530
+ **Examples:**
531
+
532
+ ```ruby
533
+ # 100 events sampled at 10% rate
534
+ 100.times { tracker.record_sample(severity: :info, sample_rate: 0.1) }
535
+
536
+ tracker.sampling_correction(:info) # => 10.0
537
+ # Interpretation: Multiply observed count by 10 to get true count
538
+ # Observed: 100 sampled, True: 100 ร— 10 = 1,000 total
539
+
540
+ # 50 events sampled at 100% rate (errors)
541
+ 50.times { tracker.record_sample(severity: :error, sample_rate: 1.0) }
542
+
543
+ tracker.sampling_correction(:error) # => 1.0
544
+ # Interpretation: No correction needed (100% sampled)
545
+ ```
546
+
547
+ ---
548
+
549
+ #### `#reset!` โ†’ void
550
+
551
+ Resets all tracked data.
552
+
553
+ **Example:**
554
+ ```ruby
555
+ tracker.reset!
556
+ ```
557
+
558
+ ---
559
+
560
+ ### Thread Safety
561
+
562
+ This class uses `MonitorMixin` for thread-safe operations.
563
+
564
+ ---
565
+
566
+ ## E11y::Middleware::Sampling
567
+
568
+ Extended sampling middleware with all 4 advanced strategies.
569
+
570
+ ### Location
571
+
572
+ ```
573
+ lib/e11y/middleware/sampling.rb
574
+ ```
575
+
576
+ ### Class Definition
577
+
578
+ ```ruby
579
+ module E11y
580
+ module Middleware
581
+ class Sampling
582
+ def initialize(config = {})
583
+ def call(event_data)
584
+ def capabilities
585
+ end
586
+ end
587
+ end
588
+ ```
589
+
590
+ ### Configuration Options
591
+
592
+ ```ruby
593
+ config = {
594
+ # Base sampling
595
+ default_sample_rate: 0.1, # Fallback rate (default: 0.1)
596
+
597
+ # Error-Based Adaptive (FEAT-4838)
598
+ error_based_adaptive: true, # Enable error spike detection
599
+ error_spike_config: {
600
+ window: 60,
601
+ absolute_threshold: 100,
602
+ relative_threshold: 3.0,
603
+ spike_duration: 300
604
+ },
605
+
606
+ # Load-Based Adaptive (FEAT-4842)
607
+ load_based_adaptive: true, # Enable load monitoring
608
+ load_monitor_config: {
609
+ window: 60,
610
+ normal_threshold: 1_000,
611
+ high_threshold: 10_000,
612
+ very_high_threshold: 50_000,
613
+ overload_threshold: 100_000
614
+ }
615
+ }
616
+ ```
617
+
618
+ ---
619
+
620
+ ### Instance Methods
621
+
622
+ #### `#call(event_data)` โ†’ Hash or nil
623
+
624
+ Processes an event through the sampling pipeline.
625
+
626
+ **Parameters:**
627
+ - `event_data` (Hash): Event data
628
+
629
+ **Returns:**
630
+ - Hash: Event data (if sampled)
631
+ - nil: (if dropped)
632
+
633
+ **Sampling Decision Logic (Precedence Order):**
634
+
635
+ 1. **Audit events**: Always processed (100%)
636
+ 2. **Error spike override**: If detected โ†’ 100% for ALL events
637
+ 3. **Value-based sampling**: If event has `sample_by_value` config and value meets criteria โ†’ 100%
638
+ 4. **Load-based sampling**: Base rate from `LoadMonitor` (100%/50%/10%/1%)
639
+ 5. **Event-level rate**: From `event_class.resolve_sample_rate`
640
+ 6. **Default rate**: From `config[:default_sample_rate]`
641
+
642
+ **Example:**
643
+ ```ruby
644
+ sampling = E11y::Middleware::Sampling.new(config)
645
+
646
+ event_data = {
647
+ event_name: "order.paid",
648
+ severity: :info,
649
+ payload: { amount: 5000 }
650
+ }
651
+
652
+ result = sampling.call(event_data)
653
+ # => { event_name: "order.paid", ... } (sampled)
654
+ # OR
655
+ # => nil (dropped)
656
+ ```
657
+
658
+ ---
659
+
660
+ #### `#capabilities` โ†’ Hash
661
+
662
+ Returns middleware capabilities.
663
+
664
+ **Returns:**
665
+ - Hash with capability flags:
666
+ - `:error_spike_aware` (Boolean)
667
+ - `:load_based_adaptive` (Boolean)
668
+ - `:value_based_sampling` (Boolean)
669
+ - `:stratified_tracking` (Boolean)
670
+
671
+ **Example:**
672
+ ```ruby
673
+ sampling.capabilities
674
+ # => {
675
+ # error_spike_aware: true,
676
+ # load_based_adaptive: true,
677
+ # value_based_sampling: true,
678
+ # stratified_tracking: true
679
+ # }
680
+ ```
681
+
682
+ ---
683
+
684
+ ## E11y::Event::Base (Extended DSL)
685
+
686
+ Extended event base class with `sample_by_value` DSL.
687
+
688
+ ### Location
689
+
690
+ ```
691
+ lib/e11y/event/base.rb
692
+ ```
693
+
694
+ ### Class Methods
695
+
696
+ #### `.sample_by_value(field:, operator:, threshold:, sample_rate: 1.0)` โ†’ ValueSamplingConfig
697
+
698
+ Configures value-based sampling for this event class.
699
+
700
+ **Parameters:**
701
+ - `field` (String): Dot-separated field path in payload
702
+ - `operator` (Symbol): Comparison operator
703
+ - `threshold` (Numeric, Range, String): Value or range to compare
704
+ - `sample_rate` (Float): Sample rate if criteria met (default: 1.0)
705
+
706
+ **Returns:**
707
+ - `ValueSamplingConfig` instance
708
+
709
+ **Examples:**
710
+
711
+ ```ruby
712
+ class Events::OrderPaid < E11y::Event::Base
713
+ schema do
714
+ required(:order_id).filled(:string)
715
+ required(:amount).filled(:decimal)
716
+ end
717
+
718
+ # Strategy 1: Always sample high-value orders
719
+ sample_by_value field: "amount",
720
+ operator: :greater_than,
721
+ threshold: 1000,
722
+ sample_rate: 1.0
723
+
724
+ # Strategy 2: Sample range (50%)
725
+ sample_by_value field: "amount",
726
+ operator: :in_range,
727
+ threshold: 100..500,
728
+ sample_rate: 0.5
729
+ end
730
+
731
+ class Events::ApiRequest < E11y::Event::Base
732
+ schema do
733
+ required(:endpoint).filled(:string)
734
+ required(:latency_ms).filled(:integer)
735
+ end
736
+
737
+ # Always sample slow requests
738
+ sample_by_value field: "latency_ms",
739
+ operator: :greater_than,
740
+ threshold: 1000,
741
+ sample_rate: 1.0
742
+ end
743
+
744
+ class Events::UserAction < E11y::Event::Base
745
+ schema do
746
+ required(:action).filled(:string)
747
+ required(:user_segment).filled(:string)
748
+ end
749
+
750
+ # Always sample enterprise users
751
+ sample_by_value field: "user_segment",
752
+ operator: :equals,
753
+ threshold: "enterprise",
754
+ sample_rate: 1.0
755
+ end
756
+ ```
757
+
758
+ ---
759
+
760
+ #### `.value_sampling_config` โ†’ ValueSamplingConfig or nil
761
+
762
+ Returns the value sampling configuration for this event class.
763
+
764
+ **Returns:**
765
+ - `ValueSamplingConfig` instance (if `sample_by_value` was called)
766
+ - `nil` (if no value-based sampling configured)
767
+
768
+ **Example:**
769
+ ```ruby
770
+ Events::OrderPaid.value_sampling_config
771
+ # => #<E11y::Event::ValueSamplingConfig @field="amount" @operator=:greater_than ...>
772
+
773
+ Events::DebugEvent.value_sampling_config
774
+ # => nil (no value-based sampling)
775
+ ```
776
+
777
+ ---
778
+
779
+ ## ๐Ÿ“Š Usage Examples
780
+
781
+ ### Example 1: Production Configuration
782
+
783
+ ```ruby
784
+ # config/initializers/e11y.rb
785
+ E11y.configure do |config|
786
+ config.pipeline.use E11y::Middleware::Sampling,
787
+ default_sample_rate: 0.1,
788
+
789
+ # Error-Based Adaptive
790
+ error_based_adaptive: true,
791
+ error_spike_config: {
792
+ window: 60,
793
+ absolute_threshold: 100,
794
+ relative_threshold: 3.0,
795
+ spike_duration: 300
796
+ },
797
+
798
+ # Load-Based Adaptive
799
+ load_based_adaptive: true,
800
+ load_monitor_config: {
801
+ window: 60,
802
+ normal_threshold: 1_000,
803
+ high_threshold: 10_000,
804
+ very_high_threshold: 50_000,
805
+ overload_threshold: 100_000
806
+ }
807
+ end
808
+
809
+ # Event with value-based sampling
810
+ class Events::OrderPaid < E11y::Event::Base
811
+ schema { required(:amount).filled(:decimal) }
812
+
813
+ sample_by_value field: "amount",
814
+ operator: :greater_than,
815
+ threshold: 1000
816
+ end
817
+ ```
818
+
819
+ ---
820
+
821
+ ### Example 2: Manual Detector Usage
822
+
823
+ ```ruby
824
+ # Create detector
825
+ detector = E11y::Sampling::ErrorSpikeDetector.new(
826
+ window: 60,
827
+ absolute_threshold: 100,
828
+ relative_threshold: 3.0
829
+ )
830
+
831
+ # Record events
832
+ 1000.times do |i|
833
+ event = {
834
+ severity: i % 10 == 0 ? :error : :info, # 10% error rate
835
+ message: "Event #{i}"
836
+ }
837
+
838
+ detector.record_event(event)
839
+ end
840
+
841
+ # Check if spike detected
842
+ if detector.error_spike?
843
+ puts "ERROR SPIKE DETECTED!"
844
+ puts "Current rate: #{detector.current_error_rate} errors/min"
845
+ puts "Baseline: #{detector.baseline_error_rate} errors/min"
846
+ end
847
+ ```
848
+
849
+ ---
850
+
851
+ ### Example 3: SLO Calculation with Stratified Sampling
852
+
853
+ ```ruby
854
+ # Track events with sampling
855
+ tracker = E11y::Sampling::StratifiedTracker.new
856
+
857
+ # Simulate 1000 events (950 success, 50 errors)
858
+ # Stratified sampling: errors 100%, success 10%
859
+ 950.times { tracker.record_sample(severity: :info, sample_rate: 0.1) }
860
+ 50.times { tracker.record_sample(severity: :error, sample_rate: 1.0) }
861
+
862
+ # Calculate corrected success rate
863
+ success_correction = tracker.sampling_correction(:info) # => 10.0
864
+ error_correction = tracker.sampling_correction(:error) # => 1.0
865
+
866
+ observed_success = 95 # 10% of 950
867
+ observed_errors = 50 # 100% of 50
868
+
869
+ corrected_success = observed_success * success_correction # => 950
870
+ corrected_errors = observed_errors * error_correction # => 50
871
+
872
+ corrected_success_rate = corrected_success / (corrected_success + corrected_errors)
873
+ # => 0.95 (95%) โœ… ACCURATE!
874
+ ```
875
+
876
+ ---
877
+
878
+ ## ๐Ÿงช Testing
879
+
880
+ All classes include comprehensive RSpec tests:
881
+
882
+ ```bash
883
+ # Error-Based Adaptive
884
+ bundle exec rspec spec/e11y/sampling/error_spike_detector_spec.rb
885
+
886
+ # Load-Based Adaptive
887
+ bundle exec rspec spec/e11y/sampling/load_monitor_spec.rb
888
+
889
+ # Value-Based Sampling
890
+ bundle exec rspec spec/e11y/sampling/value_extractor_spec.rb
891
+ bundle exec rspec spec/e11y/event/value_sampling_config_spec.rb
892
+
893
+ # Stratified Sampling
894
+ bundle exec rspec spec/e11y/sampling/stratified_tracker_spec.rb
895
+
896
+ # Integration Tests
897
+ bundle exec rspec spec/e11y/middleware/sampling_spec.rb
898
+ bundle exec rspec spec/e11y/slo/stratified_sampling_integration_spec.rb
899
+ ```
900
+
901
+ ---
902
+
903
+ ## ๐Ÿ“š Related Documentation
904
+
905
+ - **[Migration Guide](./guides/MIGRATION-L27-L28.md)** - Upgrade from L2.7 to L2.8
906
+ - **[Performance Benchmarks](./guides/PERFORMANCE-BENCHMARKS.md)** - Benchmark results
907
+ - **[ADR-009: Cost Optimization](./ADR-009-cost-optimization.md)** - Architecture details
908
+ - **[UC-014: Adaptive Sampling](./use_cases/UC-014-adaptive-sampling.md)** - Use cases
909
+
910
+ ---
911
+
912
+ **API Reference Version:** 1.0
913
+ **Last Updated:** January 20, 2026
914
+ **Test Coverage:** 117 tests (all passing)