lapsoss 0.1.0 → 0.3.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +153 -733
  3. data/lib/lapsoss/adapters/appsignal_adapter.rb +7 -8
  4. data/lib/lapsoss/adapters/base.rb +0 -3
  5. data/lib/lapsoss/adapters/bugsnag_adapter.rb +12 -0
  6. data/lib/lapsoss/adapters/insight_hub_adapter.rb +102 -101
  7. data/lib/lapsoss/adapters/logger_adapter.rb +7 -7
  8. data/lib/lapsoss/adapters/rollbar_adapter.rb +93 -54
  9. data/lib/lapsoss/adapters/sentry_adapter.rb +11 -17
  10. data/lib/lapsoss/backtrace_frame.rb +35 -214
  11. data/lib/lapsoss/backtrace_frame_factory.rb +228 -0
  12. data/lib/lapsoss/backtrace_processor.rb +37 -37
  13. data/lib/lapsoss/client.rb +2 -6
  14. data/lib/lapsoss/configuration.rb +25 -22
  15. data/lib/lapsoss/current.rb +9 -1
  16. data/lib/lapsoss/event.rb +30 -6
  17. data/lib/lapsoss/exception_backtrace_frame.rb +39 -0
  18. data/lib/lapsoss/exclusion_configuration.rb +30 -0
  19. data/lib/lapsoss/exclusion_filter.rb +156 -0
  20. data/lib/lapsoss/{exclusions.rb → exclusion_presets.rb} +1 -181
  21. data/lib/lapsoss/fingerprinter.rb +9 -13
  22. data/lib/lapsoss/http_client.rb +42 -8
  23. data/lib/lapsoss/merged_scope.rb +63 -0
  24. data/lib/lapsoss/middleware/base.rb +15 -0
  25. data/lib/lapsoss/middleware/conditional_filter.rb +18 -0
  26. data/lib/lapsoss/middleware/event_enricher.rb +19 -0
  27. data/lib/lapsoss/middleware/event_transformer.rb +19 -0
  28. data/lib/lapsoss/middleware/exception_filter.rb +43 -0
  29. data/lib/lapsoss/middleware/metrics_collector.rb +44 -0
  30. data/lib/lapsoss/middleware/rate_limiter.rb +31 -0
  31. data/lib/lapsoss/middleware/release_tracker.rb +117 -0
  32. data/lib/lapsoss/middleware/sample_filter.rb +23 -0
  33. data/lib/lapsoss/middleware/sampling_middleware.rb +18 -0
  34. data/lib/lapsoss/middleware/user_context_enhancer.rb +46 -0
  35. data/lib/lapsoss/middleware.rb +0 -347
  36. data/lib/lapsoss/pipeline.rb +1 -73
  37. data/lib/lapsoss/pipeline_builder.rb +69 -0
  38. data/lib/lapsoss/rails_error_subscriber.rb +42 -0
  39. data/lib/lapsoss/rails_middleware.rb +78 -0
  40. data/lib/lapsoss/railtie.rb +22 -50
  41. data/lib/lapsoss/registry.rb +34 -20
  42. data/lib/lapsoss/release_providers.rb +110 -0
  43. data/lib/lapsoss/release_tracker.rb +112 -207
  44. data/lib/lapsoss/router.rb +3 -5
  45. data/lib/lapsoss/sampling/adaptive_sampler.rb +46 -0
  46. data/lib/lapsoss/sampling/base.rb +11 -0
  47. data/lib/lapsoss/sampling/composite_sampler.rb +26 -0
  48. data/lib/lapsoss/sampling/consistent_hash_sampler.rb +30 -0
  49. data/lib/lapsoss/sampling/exception_type_sampler.rb +44 -0
  50. data/lib/lapsoss/sampling/health_based_sampler.rb +19 -0
  51. data/lib/lapsoss/sampling/rate_limiter.rb +32 -0
  52. data/lib/lapsoss/sampling/sampling_factory.rb +69 -0
  53. data/lib/lapsoss/sampling/time_based_sampler.rb +44 -0
  54. data/lib/lapsoss/sampling/uniform_sampler.rb +15 -0
  55. data/lib/lapsoss/sampling/user_based_sampler.rb +42 -0
  56. data/lib/lapsoss/sampling.rb +0 -326
  57. data/lib/lapsoss/scope.rb +17 -57
  58. data/lib/lapsoss/scrubber.rb +16 -18
  59. data/lib/lapsoss/user_context.rb +18 -198
  60. data/lib/lapsoss/user_context_integrations.rb +39 -0
  61. data/lib/lapsoss/user_context_middleware.rb +50 -0
  62. data/lib/lapsoss/user_context_provider.rb +93 -0
  63. data/lib/lapsoss/utils.rb +13 -0
  64. data/lib/lapsoss/validators.rb +14 -27
  65. data/lib/lapsoss/version.rb +1 -1
  66. data/lib/lapsoss.rb +12 -25
  67. metadata +106 -21
data/README.md CHANGED
@@ -1,855 +1,275 @@
1
- # Lapsoss 🧬
1
+ # Lapsoss - Vendor-Neutral Error Reporting for Rails
2
2
 
3
- **Adaptive Trait Selection for Error Tracking**
3
+ ## The Problem We All Face
4
4
 
5
- Lapsoss is a vendor-neutral error tracking library for Ruby that applies the principle of "Adaptive Trait Selection" - intelligently selecting the best features from each error tracking ecosystem to create a superior, unified solution.
5
+ You're 6 months into production with Bugsnag. The CFO says "costs are too high, switch to Sentry."
6
6
 
7
- ## Philosophy
7
+ The migration estimate? 3 months. Why? Because your entire codebase is littered with:
8
+ - `Bugsnag.notify(exception)`
9
+ - `Bugsnag.leave_breadcrumb("User clicked checkout")`
10
+ - Bugsnag-specific configuration
11
+ - Custom Bugsnag metadata patterns
8
12
 
9
- Lapsoss is built on the principle of absolute vendor neutrality and control. Unlike traditional error tracking libraries that often bundle proprietary SDKs, Lapsoss provides a pure Ruby implementation for error reporting. This ensures:
13
+ **This is vendor lock-in through API pollution.**
10
14
 
11
- - **No External SDK Dependencies**: Lapsoss handles payload construction and transport directly, eliminating the need for any third-party error tracking gems.
12
- - **Zero Monkey-Patching**: Your application's core behavior remains untouched.
13
- - **Complete Control**: You dictate how errors are processed and sent, without hidden telemetry or unwanted features.
14
- - **True Vendor Agnosticism**: Switch or combine error tracking services seamlessly, without changing your application's code.
15
-
16
- ## Rails-First Architecture
17
-
18
- Lapsoss is designed **primarily for Rails applications** and modern Ruby projects. This architectural choice provides clear benefits:
19
-
20
- ### Why ActiveSupport?
21
-
22
- Lapsoss depends on ActiveSupport (Rails 7.2+) as a deliberate design decision:
23
-
24
- - **Rails Integration**: Seamless integration with Rails' parameter filtering, configuration patterns, and error reporting API
25
- - **Essential Utilities**: ActiveSupport provides utilities we need anyway: `concurrent-ruby`, `zeitwerk`, inflection, configuration handling
26
- - **No Namespace Pollution**: Unlike vendor SDKs that bring Redis, Elasticsearch, and other external namespaces, ActiveSupport provides clean Ruby utilities
27
- - **Your Choice**: Use Lapsoss with ActiveSupport, or use official vendor SDKs that monkey-patch your entire application
28
-
29
- ### The Real Bloat Problem
30
-
31
- The issue isn't dependency count - it's **namespace pollution** and **monkey-patching**:
32
-
33
- - **Vendor SDKs**: Force Redis, Elasticsearch, proprietary gems, and monkey-patch core Ruby classes
34
- - **Lapsoss + ActiveSupport**: Clean utilities that enhance Ruby without breaking your application's behavior
35
-
36
- ### Ruby Implementation Support
37
-
38
- Lapsoss is built and tested exclusively for **CRuby (MRI) 3.2+**. Other Ruby implementations (JRuby, TruffleRuby) are not supported. This focused approach allows us to:
39
-
40
- - Optimize for the most common Ruby implementation
41
- - Avoid complexity of cross-implementation compatibility
42
- - Provide reliable backtrace processing and code context extraction
43
-
44
- ### Rails Version Support
45
-
46
- Lapsoss targets **Rails 7.2+** applications. If you're running an older Rails version, upgrade first. We follow Rails' release support policy and focus on modern Rails applications.
47
-
48
- ### What Lapsoss Is (and Isn't)
49
-
50
- **Lapsoss is not a full replacement for vendor SDKs.** It focuses on core error tracking functionality, providing a Rails-optimized alternative for teams that:
51
-
52
- - Want clean, vendor-neutral error reporting without external namespace pollution
53
- - Need to use multiple error tracking services simultaneously
54
- - Are tired of vendor SDKs that monkey-patch core Ruby classes
55
- - Want to avoid the complexity of legacy support and premium features you don't use
56
-
57
- **What's included:**
58
- - ✅ Exception and error message capture
59
- - ✅ Contextual data (user, tags, extra data)
60
- - ✅ Breadcrumbs and error grouping
61
- - ✅ Deploy markers and release tracking
62
- - ✅ Basic health checks/heartbeats
63
- - ✅ **Custom fingerprinting for intelligent error grouping**
64
- - ✅ **Automatic data scrubbing with Rails parameter filtering**
65
- - ✅ **Transport reliability with retry logic and client-side rate limiting**
66
- - ✅ **Configuration validation for all adapter settings**
67
- - ✅ **Advanced backtrace processing with code context extraction**
68
- - ✅ **Intelligent frame filtering and normalization**
69
- - ✅ **Performance-optimized file caching for backtrace processing**
70
-
71
- **What's NOT included:**
72
- - ❌ Application Performance Monitoring (APM)
73
- - ❌ Distributed tracing
74
- - ❌ Host metrics and system monitoring
75
- - ❌ Advanced profiling
76
- - ❌ Database query analysis
77
-
78
- Most vendor SDKs contain significant boilerplate to support legacy Ruby versions, deprecated features, and premium functionality that requires additional subscriptions. Lapsoss strips away this complexity, focusing on what most applications actually need: reliable error tracking.
79
-
80
- > 📖 **Want the full story?** Read [STORY.md](STORY.md) for detailed research on SDK bloat, including actual gem sizes and feature creep analysis across 25+ releases of major error tracking libraries.
81
-
82
- ## Core Principles (COSS-Compliant)
83
-
84
- Lapsoss adheres strictly to the [Contriboss (COSS) standard](https://www.contriboss.com), ensuring software neutrality and freedom from vendor lock-in.
85
-
86
- - 🔓 **Absolute Vendor Neutrality**: Lapsoss provides its own pure Ruby implementations for error reporting, eliminating the need for any external vendor SDKs.
87
- - 🔌 **Pluggable Adapters**: Configure and use one or multiple built-in or custom adapters simultaneously.
88
- - 🚀 **Rails 7.2+ Native Integration**: Seamlessly integrates with Rails' native error reporting API, without any monkey-patching.
89
- - 🧬 **Adaptive Trait Selection**: I've carefully selected and implemented the most valuable error reporting features directly within Lapsoss, providing a unified, best-of-breed solution.
90
-
91
- ## Installation
15
+ ## The Solution: Write Once, Deploy Anywhere
92
16
 
93
17
  ```ruby
94
- gem 'lapsoss'
95
- ```
96
-
97
- ## Quick Start
18
+ # Your code never changes:
19
+ Lapsoss.capture_exception(e)
98
20
 
99
- Lapsoss currently provides 4 built-in adapters:
100
- - **Sentry** - Full error tracking support with pure Ruby implementation
101
- - **AppSignal** - Error tracking, deploy markers, and check-ins support (messages limited to :error, :fatal, :critical levels)
102
- - **Rollbar** - Complete error tracking with grouping and person tracking
103
- - **Insight Hub** (formerly Bugsnag) - Error tracking with breadcrumbs and session support
21
+ # Switch vendors in config, not code:
22
+ Lapsoss.configure do |config|
23
+ # Monday: Using Bugsnag
24
+ config.use_bugsnag(api_key: ENV['BUGSNAG_KEY'])
104
25
 
105
- All adapters are pure Ruby implementations with no external SDK dependencies or monkey-patching. Additional adapters can be easily added by the community or vendors.
26
+ # Tuesday: Add Sentry for comparison
27
+ config.use_sentry(dsn: ENV['SENTRY_DSN'])
106
28
 
107
- ### Single Adapter (Simple Mode)
108
- ```ruby
109
- # config/initializers/lapsoss.rb
110
- Lapsoss.configure do |config|
111
- # Configure the built-in Sentry adapter
112
- config.use_sentry(dsn: ENV["SENTRY_DSN"])
29
+ # Wednesday: Drop Bugsnag, keep Sentry
30
+ # Just remove the line. Zero code changes.
113
31
  end
114
32
  ```
115
33
 
116
- ### Multi-Adapter (Advanced Mode)
117
- ```ruby
118
- # config/initializers/lapsoss.rb
119
- Lapsoss.configure do |config|
120
- # Run multiple built-in adapters simultaneously!
121
- config.use_sentry(name: :sentry_us, dsn: ENV['SENTRY_US_DSN'])
122
- config.use_sentry(name: :sentry_eu, dsn: ENV['SENTRY_EU_DSN'])
123
- config.use_appsignal(name: :appsignal, api_key: ENV['APPSIGNAL_API_KEY'])
124
- config.use_rollbar(name: :rollbar, access_token: ENV['ROLLBAR_ACCESS_TOKEN'])
125
- config.use_insight_hub(name: :insight_hub, api_key: ENV['INSIGHT_HUB_API_KEY'])
126
- config.use_logger(name: :local_backup) # Example: log errors to a local file
127
- end
34
+ ## Installation
128
35
 
129
- # For Rails applications, errors are automatically captured via the Rails error reporting API.
130
- # For other Ruby applications, use Lapsoss.capture_exception or Lapsoss.capture_message.
36
+ ```ruby
37
+ gem 'lapsoss'
131
38
  ```
132
39
 
133
40
  ## Usage
134
41
 
135
- ### Core API
136
-
137
- Lapsoss provides a simple, intuitive API for error tracking:
138
-
139
42
  ```ruby
140
43
  # Capture exceptions
141
- Lapsoss.capture_exception(exception, **context)
44
+ Lapsoss.capture_exception(e)
142
45
 
143
46
  # Capture messages
144
- Lapsoss.capture_message(message, level: :info, **context)
47
+ Lapsoss.capture_message("Payment processed", level: :info)
145
48
 
146
- # Add breadcrumbs for debugging context
147
- Lapsoss.add_breadcrumb(message, type: :default, **metadata)
148
-
149
- # Work with scoped context
150
- Lapsoss.with_scope(context = {}, &block)
151
- Lapsoss.current_scope
152
- ```
153
-
154
- ### Rails Integration
155
-
156
- For Rails applications, errors are automatically captured:
157
-
158
- ```ruby
159
- # config/initializers/lapsoss.rb
160
- Lapsoss.configure do |config|
161
- config.use_sentry(dsn: ENV["SENTRY_DSN"])
49
+ # Add context
50
+ Lapsoss.with_scope(user_id: current_user.id) do
51
+ process_payment
162
52
  end
163
53
 
164
- # That's it! All unhandled exceptions are automatically captured
165
- # No need to modify your controllers or models
54
+ # Add breadcrumbs
55
+ Lapsoss.add_breadcrumb("User clicked checkout", type: :navigation)
166
56
  ```
167
57
 
168
- ### Manual Error Capture
58
+ That's it. No 500-line examples needed.
59
+
60
+ ## Built for Rails, Not Around It
169
61
 
170
- For non-Rails applications or custom error handling:
62
+ Lapsoss integrates with Rails' native error reporting API introduced in Rails 7. No monkey-patching, no middleware gymnastics:
171
63
 
172
64
  ```ruby
173
- # Basic exception capture
174
- begin
65
+ # It just works with Rails.error:
66
+ Rails.error.handle(context: {user_id: current_user.id}) do
175
67
  risky_operation
176
- rescue => e
177
- Lapsoss.capture_exception(e)
178
- end
179
-
180
- # With additional context
181
- begin
182
- process_payment(user, amount)
183
- rescue PaymentError => e
184
- Lapsoss.capture_exception(e,
185
- user: { id: user.id, email: user.email },
186
- tags: { payment_method: "credit_card", amount: amount },
187
- extra: { order_id: order.id, retry_count: 3 }
188
- )
189
68
  end
69
+ # Automatically captured by whatever service you configured
190
70
  ```
191
71
 
192
- ### Message Logging
193
-
194
- For important events or custom alerts:
72
+ ## Zero-Downtime Vendor Migration
195
73
 
196
74
  ```ruby
197
- # Simple message
198
- Lapsoss.capture_message("Payment processing started", level: :info)
199
-
200
- # With context
201
- Lapsoss.capture_message("High CPU usage detected",
202
- level: :warning,
203
- tags: { server: "web-01", cpu_usage: "85%" },
204
- extra: { pid: Process.pid, memory_usage: "2.1GB" }
205
- )
206
- ```
75
+ # Step 1: Add Lapsoss alongside your current setup
76
+ gem 'lapsoss'
77
+ gem 'bugsnag' # Keep your existing gem for now
207
78
 
208
- ### Breadcrumbs for Debugging
79
+ # Step 2: Configure dual reporting
80
+ Lapsoss.configure do |config|
81
+ config.use_bugsnag(api_key: ENV['BUGSNAG_KEY'])
82
+ config.use_sentry(dsn: ENV['SENTRY_DSN'])
83
+ end
209
84
 
210
- Track user actions and system events leading up to errors:
85
+ # Step 3: Gradually replace Bugsnag calls
86
+ # Old: Bugsnag.notify(e)
87
+ # New: Lapsoss.capture_exception(e)
211
88
 
212
- ```ruby
213
- # User actions
214
- Lapsoss.add_breadcrumb("User logged in",
215
- type: :user,
216
- user_id: user.id,
217
- ip: request.remote_ip
218
- )
219
-
220
- # System events
221
- Lapsoss.add_breadcrumb("Database connection established",
222
- type: :system,
223
- host: "db-primary",
224
- latency: "45ms"
225
- )
226
-
227
- # Navigation
228
- Lapsoss.add_breadcrumb("User visited checkout",
229
- type: :navigation,
230
- url: "/checkout",
231
- referrer: "/cart"
232
- )
233
-
234
- # When an error occurs, all breadcrumbs are included automatically
235
- begin
236
- complete_checkout
237
- rescue => e
238
- Lapsoss.capture_exception(e) # Includes all breadcrumbs above
239
- end
89
+ # Step 4: Remove bugsnag gem when ready
90
+ # Your app keeps running, now on Sentry
240
91
  ```
241
92
 
242
- ### Scoped Context
93
+ ## Why Not Just Use Vendor SDKs?
243
94
 
244
- Use scoped context to apply data to multiple operations:
95
+ **Vendor SDKs monkey-patch your application:**
96
+ - Sentry patches Net::HTTP, Redis, and 20+ other gems
97
+ - Each vendor races to patch the same methods
98
+ - Multiple SDKs = multiple layers of patches
99
+ - Your app behavior changes based on load order
245
100
 
246
- ```ruby
247
- # Request-scoped context
248
- Lapsoss.with_scope(
249
- user: { id: current_user.id, email: current_user.email },
250
- tags: { request_id: request.id, controller: "checkout" }
251
- ) do
252
-
253
- # Any errors in this block will include the scope context
254
- process_payment
255
- update_inventory
256
- send_confirmation_email
257
-
258
- end
259
-
260
- # Background job context
261
- Lapsoss.with_scope(
262
- tags: { job: "invoice_generator", queue: "high_priority" },
263
- extra: { batch_size: invoices.count }
264
- ) do
265
-
266
- invoices.each do |invoice|
267
- Lapsoss.add_breadcrumb("Processing invoice #{invoice.id}")
268
- generate_invoice(invoice)
269
- end
270
-
271
- end
272
- ```
101
+ **Lapsoss doesn't patch anything:**
102
+ - Pure Ruby implementation
103
+ - Uses Rails' error API
104
+ - Your app behavior remains unchanged
105
+ - No competing instrumentation
273
106
 
274
- ### Real-World Examples
107
+ ## Real-World Use Cases
275
108
 
276
- #### E-commerce Checkout Flow
109
+ ### GDPR Compliance
277
110
  ```ruby
278
- class CheckoutController < ApplicationController
279
- def create
280
- Lapsoss.with_scope(
281
- user: { id: current_user.id, email: current_user.email },
282
- tags: { controller: "checkout", action: "create" },
283
- extra: { cart_total: cart.total, item_count: cart.items.count }
284
- ) do
285
-
286
- Lapsoss.add_breadcrumb("Checkout initiated", type: :user)
287
-
288
- begin
289
- payment = process_payment(cart.total)
290
- Lapsoss.add_breadcrumb("Payment processed", type: :system,
291
- payment_id: payment.id, method: payment.method)
292
-
293
- order = create_order(cart, payment)
294
- Lapsoss.add_breadcrumb("Order created", type: :system, order_id: order.id)
295
-
296
- redirect_to order_path(order)
297
-
298
- rescue PaymentError => e
299
- Lapsoss.capture_exception(e,
300
- tags: { error_type: "payment_failure" },
301
- extra: { payment_method: params[:payment_method] }
302
- )
303
- redirect_to checkout_path, alert: "Payment failed"
304
-
305
- rescue InventoryError => e
306
- Lapsoss.capture_exception(e,
307
- tags: { error_type: "inventory_failure" },
308
- extra: { requested_items: cart.items.pluck(:id) }
309
- )
310
- redirect_to cart_path, alert: "Item no longer available"
311
- end
312
-
313
- end
314
- end
315
- end
111
+ # Route EU data to EU servers, US data to US servers
112
+ config.use_sentry(name: :us, dsn: ENV['US_DSN'])
113
+ config.use_sentry(name: :eu, dsn: ENV['EU_DSN'])
316
114
  ```
317
115
 
318
- #### Background Job Error Handling
116
+ ### A/B Testing Error Services
319
117
  ```ruby
320
- class InvoiceGeneratorJob < ApplicationJob
321
- def perform(batch_id)
322
- Lapsoss.with_scope(
323
- tags: { job: "invoice_generator", batch_id: batch_id },
324
- extra: { queue: queue_name, priority: priority }
325
- ) do
326
-
327
- invoices = Invoice.where(batch_id: batch_id)
328
-
329
- invoices.each_with_index do |invoice, index|
330
- Lapsoss.add_breadcrumb("Processing invoice #{invoice.id}",
331
- type: :system,
332
- progress: "#{index + 1}/#{invoices.count}"
333
- )
334
-
335
- begin
336
- generate_pdf(invoice)
337
- send_email(invoice)
338
- invoice.update!(status: :sent)
339
-
340
- rescue => e
341
- Lapsoss.capture_exception(e,
342
- tags: { error_type: "invoice_generation_failure" },
343
- extra: { invoice_id: invoice.id, customer_id: invoice.customer_id }
344
- )
345
-
346
- invoice.update!(status: :failed)
347
- end
348
- end
349
-
350
- end
351
- end
352
- end
118
+ # Run both services, compare effectiveness
119
+ config.use_rollbar(name: :current, access_token: ENV['ROLLBAR_TOKEN'])
120
+ config.use_sentry(name: :candidate, dsn: ENV['SENTRY_DSN'])
353
121
  ```
354
122
 
355
- #### API Error Tracking
123
+ ### High Availability
356
124
  ```ruby
357
- class ApiController < ApplicationController
358
- around_action :track_api_errors
359
-
360
- private
361
-
362
- def track_api_errors
363
- Lapsoss.with_scope(
364
- tags: {
365
- api_version: request.headers["API-Version"],
366
- endpoint: "#{params[:controller]}##{params[:action]}"
367
- },
368
- extra: {
369
- user_agent: request.user_agent,
370
- ip_address: request.remote_ip
371
- }
372
- ) do
373
-
374
- Lapsoss.add_breadcrumb("API request started",
375
- type: :http,
376
- url: request.path,
377
- method: request.method
378
- )
379
-
380
- begin
381
- yield
382
-
383
- rescue RateLimitError => e
384
- Lapsoss.capture_exception(e,
385
- tags: { error_type: "rate_limit_exceeded" },
386
- extra: { limit: e.limit, reset_time: e.reset_time }
387
- )
388
- render json: { error: "Rate limit exceeded" }, status: 429
389
-
390
- rescue AuthenticationError => e
391
- Lapsoss.capture_exception(e,
392
- tags: { error_type: "authentication_failure" },
393
- extra: { auth_method: request.headers["Authorization"]&.split(" ")&.first }
394
- )
395
- render json: { error: "Authentication required" }, status: 401
396
-
397
- rescue => e
398
- Lapsoss.capture_exception(e,
399
- tags: { error_type: "unexpected_api_error" }
400
- )
401
- render json: { error: "Internal server error" }, status: 500
402
- end
403
-
404
- end
405
- end
406
- end
125
+ # Multiple providers for redundancy
126
+ config.use_sentry(name: :primary, dsn: ENV['PRIMARY_DSN'])
127
+ config.use_rollbar(name: :backup, access_token: ENV['BACKUP_TOKEN'])
407
128
  ```
408
129
 
409
- > 💡 **Pro Tip**: Check out the `/examples` directory for more comprehensive configuration examples and use cases.
130
+ ## Yes, We Require ActiveSupport
410
131
 
411
- ### Rollbar Integration
412
- ```ruby
413
- # Traditional Rollbar setup:
414
- # rails generate rollbar YOUR_ACCESS_TOKEN
415
-
416
- # With Lapsoss:
417
- config.use_rollbar(
418
- name: :rollbar,
419
- access_token: ENV['ROLLBAR_ACCESS_TOKEN'] # or your token directly
420
- )
421
- ```
132
+ This is a Rails gem for Rails applications. We use ActiveSupport because:
133
+ - You already have it (you're using Rails)
134
+ - It provides the exact utilities we need
135
+ - It's better than reimplementing Rails patterns poorly
422
136
 
423
- ### Insight Hub Integration
424
- ```ruby
425
- config.use_insight_hub(
426
- name: :insight_hub,
427
- api_key: ENV['INSIGHT_HUB_API_KEY'],
428
- release_stage: Rails.env,
429
- app_version: '1.0.0'
430
- )
431
-
432
- # For backwards compatibility, you can still use:
433
- # config.use_bugsnag(...) which maps to Insight Hub
434
- ```
137
+ If you need pure Ruby error tracking, use the vendor SDKs directly for now.
435
138
 
436
- ## Advanced Features
139
+ ## Who This Is For
437
140
 
438
- ### Custom Fingerprinting for Intelligent Error Grouping
141
+ - Teams that have been burned by vendor lock-in
142
+ - Apps that need regional data compliance (GDPR)
143
+ - Developers who value clean, maintainable code
144
+ - Rails applications that embrace change
439
145
 
440
- Lapsoss provides sophisticated error fingerprinting to group related errors together, reducing noise and improving debugging efficiency.
146
+ ## Who This Is NOT For
441
147
 
442
- #### Built-in Smart Patterns
443
- ```ruby
444
- # These errors get automatically grouped:
445
- "User 123 not found" } → "user-lookup-error"
446
- "User 456 not found" }
148
+ - Pure Ruby libraries (use vendor SDKs)
149
+ - Teams happy with their current vendor forever
150
+ - Applications that need APM features
151
+ - Non-Rails applications
447
152
 
448
- "Payment processing failed for order #123" } → "payment-failure"
449
- "Payment failed for card ending in 4567" }
153
+ ## Supported Adapters
450
154
 
451
- "Database connection lost" } "database-connection-error"
452
- "PG::ConnectionBad: timeout" }
453
- ```
155
+ All adapters are pure Ruby implementations with no external SDK dependencies:
454
156
 
455
- #### Custom Fingerprint Callbacks
456
- ```ruby
457
- Lapsoss.configure do |config|
458
- config.fingerprint_callback = lambda do |event|
459
- case event.exception&.class&.name
460
- when "ActiveRecord::RecordNotFound"
461
- # Group all record not found errors
462
- "database-record-error"
463
- when /Net::/
464
- # Group all network errors
465
- "network-error"
466
- when "Redis::ConnectionError", "Redis::TimeoutError"
467
- # Group Redis issues
468
- "redis-error"
469
- else
470
- nil # Use default fingerprinting
471
- end
472
- end
473
- end
474
- ```
157
+ - **Sentry** - Full error tracking support
158
+ - **Rollbar** - Complete error tracking with grouping
159
+ - **AppSignal** - Error tracking and deploy markers
160
+ - **Insight Hub** (formerly Bugsnag) - Error tracking with breadcrumbs
475
161
 
476
- #### Pattern-Based Fingerprinting
477
- ```ruby
478
- Lapsoss.configure do |config|
479
- config.fingerprint_patterns = [
480
- # E-commerce patterns
481
- {
482
- pattern: /Payment.*failed/i,
483
- fingerprint: "payment-failure"
484
- },
485
- {
486
- pattern: /Order \d+ (invalid|missing|not found)/i,
487
- fingerprint: "order-lookup-error"
488
- },
489
-
490
- # API patterns
491
- {
492
- pattern: %r{/api/v\d+/users/\d+},
493
- fingerprint: "api-user-endpoint-error"
494
- },
495
- {
496
- pattern: /Rate limit exceeded/i,
497
- fingerprint: "rate-limit-exceeded"
498
- }
499
- ]
500
- end
501
- ```
162
+ ## Configuration
502
163
 
503
- #### Smart Normalization
504
- ```ruby
505
- Lapsoss.configure do |config|
506
- # Normalize IDs: "User 12345 not found" → "User :id not found"
507
- config.normalize_fingerprint_ids = true
508
-
509
- # Normalize paths: "/home/user/file.txt" → ":filepath"
510
- config.normalize_fingerprint_paths = true
511
-
512
- # Include environment in fingerprint (useful for staging vs production)
513
- config.fingerprint_include_environment = false
514
- end
515
- ```
164
+ ### Basic Setup
516
165
 
517
- #### Advanced Use Cases
518
166
  ```ruby
519
- # Environment-specific fingerprinting
520
- config.fingerprint_callback = lambda do |event|
521
- if Rails.env.development?
522
- # More detailed fingerprints for debugging
523
- "#{event.exception.class.name}-#{event.exception.backtrace&.first}"
524
- else
525
- # Simpler fingerprints for production
526
- case event.exception&.class&.name
527
- when /ActiveRecord/ then "database-error"
528
- when /Net::/ then "network-error"
529
- else nil
530
- end
531
- end
532
- end
533
-
534
- # Group by controller/model for Rails apps
535
- config.fingerprint_callback = lambda do |event|
536
- backtrace = event.exception&.backtrace || []
537
-
538
- if controller_line = backtrace.find { |line| line.include?("app/controllers/") }
539
- controller = controller_line.match(%r{app/controllers/(\w+)_controller\.rb})[1]
540
- "controller-#{controller}-error"
541
- elsif model_line = backtrace.find { |line| line.include?("app/models/") }
542
- model = model_line.match(%r{app/models/(\w+)\.rb})[1]
543
- "model-#{model}-error"
544
- else
545
- nil
546
- end
167
+ # config/initializers/lapsoss.rb
168
+ Lapsoss.configure do |config|
169
+ config.use_sentry(dsn: ENV["SENTRY_DSN"])
547
170
  end
548
171
  ```
549
172
 
550
- ### Transport Reliability & Rate Limiting
551
-
552
- Lapsoss includes built-in transport reliability to ensure your errors reach their destination even under adverse conditions.
173
+ ### Multi-Adapter Setup
553
174
 
554
175
  ```ruby
555
176
  Lapsoss.configure do |config|
556
- # Transport reliability settings
557
- config.transport_timeout = 10 # Request timeout in seconds
558
- config.transport_max_retries = 3 # Number of retry attempts
559
- config.transport_initial_backoff = 1.0 # Initial backoff delay
560
- config.transport_max_backoff = 64.0 # Maximum backoff delay
561
- config.transport_backoff_multiplier = 2.0 # Exponential backoff multiplier
562
- config.transport_jitter = true # Add random jitter to prevent thundering herd
177
+ # Named adapters for different purposes
178
+ config.use_sentry(name: :errors, dsn: ENV['SENTRY_DSN'])
179
+ config.use_rollbar(name: :business_events, access_token: ENV['ROLLBAR_TOKEN'])
180
+ config.use_logger(name: :local_backup) # Local file backup
563
181
  end
564
182
  ```
565
183
 
566
- **Features:**
567
- - **Exponential Backoff**: Intelligent retry timing to avoid overwhelming services (transport layer)
568
- - **Rate Limiting**: Pipeline middleware with time-window and token bucket algorithms
569
- - **Jitter**: Random delays to prevent synchronized retry storms (transport layer)
570
- - **Circuit Breaking**: Automatic failure detection and recovery (transport layer)
571
-
572
- ### Backtrace Processing
573
-
574
- Lapsoss includes advanced backtrace processing that enhances error reports across all adapters:
184
+ ### Advanced Configuration
575
185
 
576
186
  ```ruby
577
187
  Lapsoss.configure do |config|
578
- # Code context extraction
579
- config.backtrace_context_lines = 3 # Lines of code context (before/after)
580
- config.backtrace_enable_code_context = true # Enable/disable context extraction
581
-
582
- # Path normalization
583
- config.backtrace_strip_load_path = true # Strip Ruby load paths for cleaner traces
584
-
585
- # Frame limiting (for deep stack traces)
586
- config.backtrace_max_frames = 100 # Prevent memory issues with stack overflows
587
-
588
- # Application code detection
589
- config.backtrace_in_app_patterns = [
590
- %r{^/app/}, # Rails app directory
591
- %r{^lib/}, # Custom libraries
592
- %r{/my_company/} # Company code
593
- ]
594
-
595
- # Noise filtering
596
- config.backtrace_exclude_patterns = [
597
- %r{/ruby/gems/.*/rack-}, # Rack internals
598
- %r{/activesupport-.*/notifications}, # AS internals
599
- %r{/newrelic_rpm/} # Monitoring gems
600
- ]
601
- end
602
- ```
188
+ # Adapter setup
189
+ config.use_sentry(dsn: ENV['SENTRY_DSN'])
603
190
 
604
- **Features:**
605
- - **Code Context**: Include source code snippets around error locations
606
- - **Smart Filtering**: Remove framework noise, focus on your code
607
- - **In-App Detection**: Automatically identify application vs library code
608
- - **Performance Optimized**: LRU file caching for efficient processing
609
- - **Format Agnostic**: Consistent processing across all adapters
191
+ # Data scrubbing (uses Rails filter_parameters automatically)
192
+ config.scrub_fields = %w[password credit_card ssn] # Or leave nil to use Rails defaults
610
193
 
611
- ### Configuration Validation
194
+ # Error filtering
195
+ config.before_send = lambda do |event|
196
+ # Return nil to prevent sending
197
+ return nil if event.exception.is_a?(ActiveRecord::RecordNotFound)
198
+ event
199
+ end
612
200
 
613
- All adapter settings are validated to catch configuration errors early:
201
+ # Sampling
202
+ config.sample_rate = Rails.env.production? ? 0.25 : 1.0
614
203
 
615
- ```ruby
616
- # Valid configurations
617
- config.use_sentry(dsn: "https://public_key@sentry.io/project_id")
618
- config.use_rollbar(access_token: "abc123def456")
619
- config.use_appsignal(push_api_key: "12345678-1234-1234-1234-123456789abc")
620
-
621
- # ❌ Invalid configurations (caught at startup)
622
- config.use_sentry(dsn: "invalid-dsn") # ValidationError: DSN scheme is required
623
- config.use_rollbar(access_token: "invalid@token") # ValidationError: must be alphanumeric
624
- config.use_appsignal(push_api_key: "not-a-uuid") # ValidationError: must be valid UUID
204
+ # Performance
205
+ config.async = true # Send errors in background
206
+ config.transport_timeout = 10 # seconds
207
+ config.transport_max_retries = 3
208
+ end
625
209
  ```
626
210
 
627
- ## Data Security & Privacy
211
+ ### Data Protection
628
212
 
629
- Lapsoss automatically protects sensitive data by integrating with Rails' parameter filtering system.
213
+ Lapsoss automatically integrates with Rails' parameter filtering:
630
214
 
631
- ### Automatic Rails Integration
632
215
  ```ruby
633
- # config/initializers/filter_parameter_logging.rb (Standard Rails)
634
- Rails.application.config.filter_parameters += [
635
- :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
636
- ]
216
+ # config/initializers/filter_parameter_logging.rb
217
+ Rails.application.config.filter_parameters += [:password, :token]
637
218
 
638
219
  # Lapsoss automatically uses these filters - no additional configuration needed!
639
- Lapsoss.configure do |config|
640
- config.use_sentry(dsn: ENV['SENTRY_DSN'])
641
- # Sensitive fields are automatically scrubbed using Rails filter_parameters
642
- end
643
- ```
644
-
645
- ### Custom Data Scrubbing
646
- ```ruby
647
- Lapsoss.configure do |config|
648
- # Add custom fields to scrub (in addition to Rails filters)
649
- config.scrub_fields = %w[internal_token custom_secret]
650
-
651
- # Override Rails filters completely
652
- config.scrub_fields = %w[password api_key credit_card] # Ignores Rails config
653
-
654
- # Scrub everything except whitelisted fields
655
- config.scrub_all = true
656
- config.whitelist_fields = %w[debug_info safe_field]
657
-
658
- # Randomize scrubbed value lengths (security through obscurity)
659
- config.randomize_scrub_length = true # "******" to "*********"
660
- end
661
220
  ```
662
221
 
663
- ### What Gets Protected
664
- - **Passwords**: `password`, `passwd`, `pwd`
665
- - **Tokens**: `token`, `access_token`, `auth_token`, `csrf_token`
666
- - **Keys**: `key`, `api_key`, `secret_key`
667
- - **PII**: `ssn`, `social_security_number`, `credit_card`, `phone`
668
- - **File Uploads**: Metadata extracted, content scrubbed
669
- - **Rails filter_parameters**: Automatically respected
670
-
671
- ## Adaptive Traits We've Selected
672
-
673
- ### From Sentry 🔍
674
- - **Custom fingerprinting** for intelligent error grouping
675
- - Robust error capture with context
676
- - Release tracking and environment awareness
677
-
678
- ### From Rollbar 🎲
679
- - **Advanced data scrubbing** with Rails parameter filter integration
680
- - **Transport reliability** with exponential backoff and retry logic
681
- - Person tracking and user context
222
+ ### Custom Fingerprinting
682
223
 
683
- ### From AppSignal 📊
684
- - **Configuration validation** to catch setup errors early
685
- - Check-in monitoring for cron jobs
686
- - Deploy markers and health checks
224
+ Control how errors are grouped:
687
225
 
688
- ### From Insight Hub (formerly Bugsnag) 🐛
689
- - Feature flag correlation with errors
690
- - Session tracking for crash-free rates
691
- - Typed breadcrumbs for debugging context
692
-
693
-
694
- ### Lapsoss Innovations 🧬
695
- - **Pure Ruby implementations** - no external SDK dependencies
696
- - **Multi-adapter architecture** - use multiple services simultaneously
697
- - **Vendor-neutral configuration** - switch providers without code changes
698
- - **Zero monkey-patching** - your application remains untouched
699
-
700
- ## Usage Examples
701
-
702
- ### Basic Error Capture
703
226
  ```ruby
704
- begin
705
- risky_operation
706
- rescue => e
707
- Lapsoss.capture_exception(e, user: current_user)
227
+ config.fingerprint_callback = lambda do |event|
228
+ case event.exception&.class&.name
229
+ when "ActiveRecord::RecordNotFound"
230
+ "record-not-found" # Group all together
231
+ when "Stripe::CardError"
232
+ "payment-failure"
233
+ else
234
+ nil # Use default fingerprinting
235
+ end
708
236
  end
709
237
  ```
710
238
 
711
- ### Custom Fingerprinting in Action
712
- ```ruby
713
- # These errors will be automatically grouped:
714
- begin
715
- User.find(123) # → "user-lookup-error"
716
- rescue ActiveRecord::RecordNotFound => e
717
- Lapsoss.capture_exception(e)
718
- end
239
+ ### Transport Reliability
719
240
 
720
- begin
721
- User.find(456) # → Same "user-lookup-error" group
722
- rescue ActiveRecord::RecordNotFound => e
723
- Lapsoss.capture_exception(e)
724
- end
725
- ```
241
+ Built-in retry logic with exponential backoff:
726
242
 
727
- ### Advanced Configuration Example
728
243
  ```ruby
729
- Lapsoss.configure do |config|
730
- # Multi-vendor setup with custom fingerprinting
731
- config.use_sentry(name: :sentry, dsn: ENV['SENTRY_DSN'])
732
- config.use_rollbar(name: :rollbar, access_token: ENV['ROLLBAR_TOKEN'])
733
-
734
- # Custom fingerprinting for your domain
735
- config.fingerprint_callback = lambda do |event|
736
- case event.exception&.message
737
- when /payment.*failed/i
738
- "payment-processing-error"
739
- when /user.*not.*found/i
740
- "user-lookup-error"
741
- else
742
- nil # Use built-in patterns and defaults
743
- end
744
- end
745
-
746
- # Enhanced data protection
747
- config.scrub_fields = %w[credit_card_number api_token internal_id]
748
- config.randomize_scrub_length = true
749
-
750
- # Reliable transport
751
- config.transport_max_retries = 5
752
- config.transport_timeout = 15
753
- end
244
+ config.transport_max_retries = 3
245
+ config.transport_timeout = 10
246
+ config.transport_jitter = true # Prevent thundering herd
754
247
  ```
755
248
 
756
-
757
249
  ## Creating Custom Adapters
758
250
 
759
251
  ```ruby
760
- class MyCustomAdapter < Lapsoss::Adapters::Base
252
+ class MyAdapter < Lapsoss::Adapters::Base
761
253
  def capture(event)
762
254
  # Send to your service
255
+ HttpClient.post("/errors", event.to_h)
763
256
  end
764
257
  end
765
258
 
766
- Lapsoss::Registry.register(:custom, MyCustomAdapter)
767
- ```
768
-
769
- ## Multi-Adapter Use Cases
770
-
771
- ### GDPR Region Migration
772
- ```ruby
773
- # Step 1: Start with US
774
- config.use_sentry(name: :sentry_us, dsn: ENV['SENTRY_US_DSN'])
775
-
776
- # Step 2: Add EU, run both
777
- config.use_sentry(name: :sentry_us, dsn: ENV['SENTRY_US_DSN'])
778
- config.use_sentry(name: :sentry_eu, dsn: ENV['SENTRY_EU_DSN'])
779
-
780
- # Step 3: Complete migration
781
- config.use_sentry(name: :sentry_eu, dsn: ENV['SENTRY_EU_DSN'])
782
- ```
783
-
784
- ### Vendor Migration
785
- ```ruby
786
- # Gradual migration with zero downtime
787
- config.use_bugsnag(name: :old_vendor, api_key: ENV['BUGSNAG_KEY'])
788
- config.use_sentry(name: :new_vendor, dsn: ENV['SENTRY_DSN'])
789
- # Remove old vendor when ready
259
+ Lapsoss::Registry.register(:my_service, MyAdapter)
790
260
  ```
791
261
 
792
- ### AppSignal Integration
793
- ```ruby
794
- # config/initializers/lapsoss.rb
795
- config.use_appsignal(
796
- name: :appsignal,
797
- push_api_key: ENV['APPSIGNAL_PUSH_API_KEY'], # For markers & check-ins
798
- frontend_api_key: ENV['APPSIGNAL_FRONTEND_API_KEY'], # For error tracking
799
- app_name: 'my-app'
800
- )
801
- ```
802
-
803
- **AppSignal API Keys**: AppSignal uses two different API keys:
804
- - **Push API Key**: For deploy markers and check-ins (found in Push & Deploy settings)
805
- - **Frontend API Key**: For error tracking via public API (found in App settings > "Front-end error monitoring")
806
-
807
- The Lapsoss AppSignal adapter provides:
808
- - ✅ Error tracking (via public `/errors` endpoint with frontend key)
809
- - ✅ Deploy markers (via `/markers` endpoint with push key)
810
- - ✅ Check-ins/Heartbeats (via `/heartbeats` endpoint with push key)
811
- - ✅ Breadcrumbs (included with errors)
812
- - ❌ Performance monitoring (requires native extension)
813
- - ❌ Host metrics (requires native extension)
814
-
815
- ### High Availability
816
- ```ruby
817
- # Multiple regions + vendors for redundancy
818
- config.use_sentry(name: :primary, dsn: ENV['PRIMARY_DSN'])
819
- config.use_sentry(name: :backup, dsn: ENV['BACKUP_DSN'])
820
- config.use_rollbar(name: :backup_service, access_token: ENV['ROLLBAR_TOKEN'])
821
- ```
822
-
823
- ## Why "Adaptive Trait Selection"?
824
-
825
- Traditional error tracking gems lock you into their ecosystem. Lapsoss applies evolutionary principles:
826
-
827
- 1. **Observation**: Each vendor evolved unique strengths
828
- 2. **Selection**: I identify the most beneficial traits
829
- 3. **Adaptation**: I create vendor-neutral interfaces
830
- 4. **Evolution**: Your app gains the best of all worlds
831
-
832
- ### The Multi-Adapter Advantage
833
-
834
- - **Zero-downtime migrations** between providers
835
- - **Regional compliance** (GDPR, data residency)
836
- - **Vendor independence** - never locked in
837
- - **Cost optimization** - route by importance
838
- - **A/B testing** different services
839
-
840
262
  ## Contributing
841
263
 
842
- Help me evolve! I'm actively selecting new adaptive traits:
843
-
844
- 1. Fork the repo
264
+ 1. Fork it
845
265
  2. Create your feature branch
846
- 3. Add your trait selection
847
- 4. Submit a PR
266
+ 3. Add tests for your changes
267
+ 4. Submit a pull request
848
268
 
849
269
  ## License
850
270
 
851
- MIT License - Because good traits should spread freely
271
+ MIT License - Because good code should be free
852
272
 
853
273
  ---
854
274
 
855
- Built with ❤️ and evolutionary principles
275
+ Built for Rails developers who refuse to be locked in.