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.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +69 -0
- data/CHANGELOG.md +26 -0
- data/CODE_OF_CONDUCT.md +64 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/Rakefile +37 -0
- data/benchmarks/run_all.rb +33 -0
- data/config/README.md +83 -0
- data/config/loki-local-config.yaml +35 -0
- data/config/prometheus.yml +15 -0
- data/docker-compose.yml +78 -0
- data/docs/00-ICP-AND-TIMELINE.md +483 -0
- data/docs/01-SCALE-REQUIREMENTS.md +858 -0
- data/docs/ADR-001-architecture.md +2617 -0
- data/docs/ADR-002-metrics-yabeda.md +1395 -0
- data/docs/ADR-003-slo-observability.md +3337 -0
- data/docs/ADR-004-adapter-architecture.md +2385 -0
- data/docs/ADR-005-tracing-context.md +1372 -0
- data/docs/ADR-006-security-compliance.md +4143 -0
- data/docs/ADR-007-opentelemetry-integration.md +1385 -0
- data/docs/ADR-008-rails-integration.md +1911 -0
- data/docs/ADR-009-cost-optimization.md +2993 -0
- data/docs/ADR-010-developer-experience.md +2166 -0
- data/docs/ADR-011-testing-strategy.md +1836 -0
- data/docs/ADR-012-event-evolution.md +958 -0
- data/docs/ADR-013-reliability-error-handling.md +2750 -0
- data/docs/ADR-014-event-driven-slo.md +1533 -0
- data/docs/ADR-015-middleware-order.md +1061 -0
- data/docs/ADR-016-self-monitoring-slo.md +1234 -0
- data/docs/API-REFERENCE-L28.md +914 -0
- data/docs/COMPREHENSIVE-CONFIGURATION.md +2366 -0
- data/docs/IMPLEMENTATION_NOTES.md +2804 -0
- data/docs/IMPLEMENTATION_PLAN.md +1971 -0
- data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +586 -0
- data/docs/PLAN.md +148 -0
- data/docs/QUICK-START.md +934 -0
- data/docs/README.md +296 -0
- data/docs/design/00-memory-optimization.md +593 -0
- data/docs/guides/MIGRATION-L27-L28.md +692 -0
- data/docs/guides/PERFORMANCE-BENCHMARKS.md +434 -0
- data/docs/guides/README.md +44 -0
- data/docs/prd/01-overview-vision.md +440 -0
- data/docs/use_cases/README.md +119 -0
- data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +813 -0
- data/docs/use_cases/UC-002-business-event-tracking.md +1953 -0
- data/docs/use_cases/UC-003-pattern-based-metrics.md +1627 -0
- data/docs/use_cases/UC-004-zero-config-slo-tracking.md +728 -0
- data/docs/use_cases/UC-005-sentry-integration.md +759 -0
- data/docs/use_cases/UC-006-trace-context-management.md +905 -0
- data/docs/use_cases/UC-007-pii-filtering.md +2648 -0
- data/docs/use_cases/UC-008-opentelemetry-integration.md +1153 -0
- data/docs/use_cases/UC-009-multi-service-tracing.md +1043 -0
- data/docs/use_cases/UC-010-background-job-tracking.md +1018 -0
- data/docs/use_cases/UC-011-rate-limiting.md +1906 -0
- data/docs/use_cases/UC-012-audit-trail.md +2301 -0
- data/docs/use_cases/UC-013-high-cardinality-protection.md +2127 -0
- data/docs/use_cases/UC-014-adaptive-sampling.md +1940 -0
- data/docs/use_cases/UC-015-cost-optimization.md +735 -0
- data/docs/use_cases/UC-016-rails-logger-migration.md +785 -0
- data/docs/use_cases/UC-017-local-development.md +867 -0
- data/docs/use_cases/UC-018-testing-events.md +1081 -0
- data/docs/use_cases/UC-019-tiered-storage-migration.md +562 -0
- data/docs/use_cases/UC-020-event-versioning.md +708 -0
- data/docs/use_cases/UC-021-error-handling-retry-dlq.md +956 -0
- data/docs/use_cases/UC-022-event-registry.md +648 -0
- data/docs/use_cases/backlog.md +226 -0
- data/e11y.gemspec +76 -0
- data/lib/e11y/adapters/adaptive_batcher.rb +207 -0
- data/lib/e11y/adapters/audit_encrypted.rb +239 -0
- data/lib/e11y/adapters/base.rb +580 -0
- data/lib/e11y/adapters/file.rb +224 -0
- data/lib/e11y/adapters/in_memory.rb +216 -0
- data/lib/e11y/adapters/loki.rb +333 -0
- data/lib/e11y/adapters/otel_logs.rb +203 -0
- data/lib/e11y/adapters/registry.rb +141 -0
- data/lib/e11y/adapters/sentry.rb +230 -0
- data/lib/e11y/adapters/stdout.rb +108 -0
- data/lib/e11y/adapters/yabeda.rb +370 -0
- data/lib/e11y/buffers/adaptive_buffer.rb +339 -0
- data/lib/e11y/buffers/base_buffer.rb +40 -0
- data/lib/e11y/buffers/request_scoped_buffer.rb +246 -0
- data/lib/e11y/buffers/ring_buffer.rb +267 -0
- data/lib/e11y/buffers.rb +14 -0
- data/lib/e11y/console.rb +122 -0
- data/lib/e11y/current.rb +48 -0
- data/lib/e11y/event/base.rb +894 -0
- data/lib/e11y/event/value_sampling_config.rb +84 -0
- data/lib/e11y/events/base_audit_event.rb +43 -0
- data/lib/e11y/events/base_payment_event.rb +33 -0
- data/lib/e11y/events/rails/cache/delete.rb +21 -0
- data/lib/e11y/events/rails/cache/read.rb +23 -0
- data/lib/e11y/events/rails/cache/write.rb +22 -0
- data/lib/e11y/events/rails/database/query.rb +45 -0
- data/lib/e11y/events/rails/http/redirect.rb +21 -0
- data/lib/e11y/events/rails/http/request.rb +26 -0
- data/lib/e11y/events/rails/http/send_file.rb +21 -0
- data/lib/e11y/events/rails/http/start_processing.rb +26 -0
- data/lib/e11y/events/rails/job/completed.rb +22 -0
- data/lib/e11y/events/rails/job/enqueued.rb +22 -0
- data/lib/e11y/events/rails/job/failed.rb +22 -0
- data/lib/e11y/events/rails/job/scheduled.rb +23 -0
- data/lib/e11y/events/rails/job/started.rb +22 -0
- data/lib/e11y/events/rails/log.rb +56 -0
- data/lib/e11y/events/rails/view/render.rb +23 -0
- data/lib/e11y/events.rb +18 -0
- data/lib/e11y/instruments/active_job.rb +201 -0
- data/lib/e11y/instruments/rails_instrumentation.rb +141 -0
- data/lib/e11y/instruments/sidekiq.rb +175 -0
- data/lib/e11y/logger/bridge.rb +205 -0
- data/lib/e11y/metrics/cardinality_protection.rb +172 -0
- data/lib/e11y/metrics/cardinality_tracker.rb +134 -0
- data/lib/e11y/metrics/registry.rb +234 -0
- data/lib/e11y/metrics/relabeling.rb +226 -0
- data/lib/e11y/metrics.rb +102 -0
- data/lib/e11y/middleware/audit_signing.rb +174 -0
- data/lib/e11y/middleware/base.rb +140 -0
- data/lib/e11y/middleware/event_slo.rb +167 -0
- data/lib/e11y/middleware/pii_filter.rb +266 -0
- data/lib/e11y/middleware/pii_filtering.rb +280 -0
- data/lib/e11y/middleware/rate_limiting.rb +214 -0
- data/lib/e11y/middleware/request.rb +163 -0
- data/lib/e11y/middleware/routing.rb +157 -0
- data/lib/e11y/middleware/sampling.rb +254 -0
- data/lib/e11y/middleware/slo.rb +168 -0
- data/lib/e11y/middleware/trace_context.rb +131 -0
- data/lib/e11y/middleware/validation.rb +118 -0
- data/lib/e11y/middleware/versioning.rb +132 -0
- data/lib/e11y/middleware.rb +12 -0
- data/lib/e11y/pii/patterns.rb +90 -0
- data/lib/e11y/pii.rb +13 -0
- data/lib/e11y/pipeline/builder.rb +155 -0
- data/lib/e11y/pipeline/zone_validator.rb +110 -0
- data/lib/e11y/pipeline.rb +12 -0
- data/lib/e11y/presets/audit_event.rb +65 -0
- data/lib/e11y/presets/debug_event.rb +34 -0
- data/lib/e11y/presets/high_value_event.rb +51 -0
- data/lib/e11y/presets.rb +19 -0
- data/lib/e11y/railtie.rb +138 -0
- data/lib/e11y/reliability/circuit_breaker.rb +216 -0
- data/lib/e11y/reliability/dlq/file_storage.rb +277 -0
- data/lib/e11y/reliability/dlq/filter.rb +117 -0
- data/lib/e11y/reliability/retry_handler.rb +207 -0
- data/lib/e11y/reliability/retry_rate_limiter.rb +117 -0
- data/lib/e11y/sampling/error_spike_detector.rb +225 -0
- data/lib/e11y/sampling/load_monitor.rb +161 -0
- data/lib/e11y/sampling/stratified_tracker.rb +92 -0
- data/lib/e11y/sampling/value_extractor.rb +82 -0
- data/lib/e11y/self_monitoring/buffer_monitor.rb +79 -0
- data/lib/e11y/self_monitoring/performance_monitor.rb +97 -0
- data/lib/e11y/self_monitoring/reliability_monitor.rb +146 -0
- data/lib/e11y/slo/event_driven.rb +150 -0
- data/lib/e11y/slo/tracker.rb +119 -0
- data/lib/e11y/version.rb +9 -0
- data/lib/e11y.rb +283 -0
- 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)
|