lapsoss 0.4.0 → 0.4.4

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +195 -7
  3. data/lib/lapsoss/adapters/concerns/level_mapping.rb +1 -0
  4. data/lib/lapsoss/adapters/telebug_adapter.rb +58 -0
  5. data/lib/lapsoss/client.rb +1 -3
  6. data/lib/lapsoss/configuration.rb +14 -17
  7. data/lib/lapsoss/fingerprinter.rb +52 -47
  8. data/lib/lapsoss/middleware/release_tracker.rb +11 -98
  9. data/lib/lapsoss/pipeline_builder.rb +2 -2
  10. data/lib/lapsoss/rails_middleware.rb +2 -2
  11. data/lib/lapsoss/railtie.rb +14 -3
  12. data/lib/lapsoss/registry.rb +7 -7
  13. data/lib/lapsoss/router.rb +1 -3
  14. data/lib/lapsoss/scrubber.rb +15 -152
  15. data/lib/lapsoss/validators.rb +48 -112
  16. data/lib/lapsoss/version.rb +1 -1
  17. data/lib/lapsoss.rb +23 -0
  18. metadata +2 -21
  19. data/lib/lapsoss/exclusion_configuration.rb +0 -30
  20. data/lib/lapsoss/exclusion_presets.rb +0 -249
  21. data/lib/lapsoss/middleware/sample_filter.rb +0 -23
  22. data/lib/lapsoss/middleware/sampling_middleware.rb +0 -18
  23. data/lib/lapsoss/middleware/user_context_enhancer.rb +0 -46
  24. data/lib/lapsoss/release_providers.rb +0 -110
  25. data/lib/lapsoss/sampling/adaptive_sampler.rb +0 -46
  26. data/lib/lapsoss/sampling/composite_sampler.rb +0 -26
  27. data/lib/lapsoss/sampling/consistent_hash_sampler.rb +0 -30
  28. data/lib/lapsoss/sampling/exception_type_sampler.rb +0 -44
  29. data/lib/lapsoss/sampling/health_based_sampler.rb +0 -19
  30. data/lib/lapsoss/sampling/sampling_factory.rb +0 -69
  31. data/lib/lapsoss/sampling/time_based_sampler.rb +0 -44
  32. data/lib/lapsoss/sampling/user_based_sampler.rb +0 -42
  33. data/lib/lapsoss/user_context.rb +0 -185
  34. data/lib/lapsoss/user_context_integrations.rb +0 -39
  35. data/lib/lapsoss/user_context_middleware.rb +0 -50
  36. data/lib/lapsoss/user_context_provider.rb +0 -93
  37. data/lib/lapsoss/utils.rb +0 -11
  38. data/lib/tasks/cassettes.rake +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0faf08671ef4e1b0b36ccc9e29900b8e8b5e4a78ac61ffea6269294efee5881
4
- data.tar.gz: b2c76c33817f3a939388970d21c5fc341ad6ce46e0ce20ef9b71ca5b5c565ae0
3
+ metadata.gz: 3c9422cb2f9fa0e1ae582a771fd337d61177d9236db6cd3dfc96edf3fefc7777
4
+ data.tar.gz: d1dc595b8fcd5e00feb9f01087e77700bb49452a6b358a309ce630d6b29bd203
5
5
  SHA512:
6
- metadata.gz: 5cfb22826215ffc2473410d6e984ecbd93f74ec0c011e742d7246549b29c1d77b9cb81c774cab311748c0b39cbfd9b9eccfdbb4324aa6da7a632ba8634eb3b19
7
- data.tar.gz: f641a491be8007a0bc6780094803724f66efc79aa1e10cadbb1ac96c84a8ff30af119c763e1611dc3e2218305c4d0b5958b6f5c5f2863c7b5694b5ff7c9f0856
6
+ metadata.gz: 218bbe054b15df5457996cfda82aa6227477446dc0a5be24680acbae4c4ff3edfaf7217adfc96814aba94fe09331300253f4a499a85055b2fbac8df83a8336fb
7
+ data.tar.gz: d5b0f10802bfe6c938cf90e8a322b0bc940b11d3bca9fde5efcaf96964c5753c09e04ae453087844cb455056b2cd6fd242e846e99ce59270d25962367daa7bda
data/README.md CHANGED
@@ -163,6 +163,7 @@ All adapters are pure Ruby implementations with no external SDK dependencies:
163
163
  - **Rollbar** - Complete error tracking with grouping
164
164
  - **AppSignal** - Error tracking and deploy markers
165
165
  - **Insight Hub** (formerly Bugsnag) - Error tracking with breadcrumbs
166
+ - **Telebug** - Sentry-compatible protocol (perfect for self-hosted alternatives)
166
167
 
167
168
  ## Configuration
168
169
 
@@ -186,6 +187,17 @@ Lapsoss.configure do |config|
186
187
  end
187
188
  ```
188
189
 
190
+ ### Using Sentry-Compatible Services
191
+
192
+ ```ruby
193
+ # Telebug, Glitchtip, or any Sentry-compatible service
194
+ Lapsoss.configure do |config|
195
+ config.use_telebug(dsn: ENV['TELEBUG_DSN'])
196
+ # Or use use_sentry with a custom endpoint
197
+ config.use_sentry(dsn: ENV['SELF_HOSTED_SENTRY_DSN'])
198
+ end
199
+ ```
200
+
189
201
  ### Advanced Configuration
190
202
 
191
203
  ```ruby
@@ -196,23 +208,82 @@ Lapsoss.configure do |config|
196
208
  # Data scrubbing (uses Rails filter_parameters automatically)
197
209
  config.scrub_fields = %w[password credit_card ssn] # Or leave nil to use Rails defaults
198
210
 
199
- # Error filtering
211
+ # Performance
212
+ config.async = true # Send errors in background
213
+
214
+ # Sampling (see docs/sampling_strategies.md for advanced examples)
215
+ config.sample_rate = Rails.env.production? ? 0.25 : 1.0
216
+
217
+ # Transport settings
218
+ config.transport_timeout = 10 # seconds
219
+ config.transport_max_retries = 3
220
+ end
221
+ ```
222
+
223
+ ### Filtering Errors
224
+
225
+ You decide what errors to track. Lapsoss doesn't make assumptions:
226
+
227
+ ```ruby
228
+ Lapsoss.configure do |config|
229
+ # Use the before_send callback for simple filtering
200
230
  config.before_send = lambda do |event|
201
231
  # Return nil to prevent sending
202
232
  return nil if event.exception.is_a?(ActiveRecord::RecordNotFound)
203
233
  event
204
234
  end
235
+
236
+ # Or use the exclusion filter for more complex rules
237
+ config.exclusion_filter = Lapsoss::ExclusionFilter.new(
238
+ # Exclude specific exception types
239
+ excluded_exceptions: [
240
+ "ActionController::RoutingError", # Your choice
241
+ "ActiveRecord::RecordNotFound" # Your decision
242
+ ],
243
+
244
+ # Exclude by pattern matching
245
+ excluded_patterns: [
246
+ /timeout/i, # If timeouts are expected in your app
247
+ /user not found/i # If these are normal in your workflow
248
+ ],
249
+
250
+ # Exclude specific error messages
251
+ excluded_messages: [
252
+ "No route matches",
253
+ "Invalid authenticity token"
254
+ ]
255
+ )
256
+
257
+ # Add custom exclusion logic
258
+ config.exclusion_filter.add_exclusion(:custom, lambda do |event|
259
+ # Your business logic here
260
+ event.context[:request]&.dig(:user_agent)&.match?(/bot/i)
261
+ end)
262
+ end
263
+ ```
205
264
 
206
- # Sampling
207
- config.sample_rate = Rails.env.production? ? 0.25 : 1.0
265
+ #### Common Patterns (Your Choice)
208
266
 
209
- # Performance
210
- config.async = true # Send errors in background
211
- config.transport_timeout = 10 # seconds
212
- config.transport_max_retries = 3
267
+ ```ruby
268
+ # Development/Test exclusions
269
+ if Rails.env.development?
270
+ config.exclusion_filter.add_exclusion(:exception, "RSpec::Expectations::ExpectationNotMetError")
271
+ config.exclusion_filter.add_exclusion(:exception, "Minitest::Assertion")
213
272
  end
273
+
274
+ # User input errors (if you don't want to track them)
275
+ config.exclusion_filter.add_exclusion(:exception, "ActiveRecord::RecordInvalid")
276
+ config.exclusion_filter.add_exclusion(:exception, "ActionController::ParameterMissing")
277
+
278
+ # Bot traffic (if you want to exclude it)
279
+ config.exclusion_filter.add_exclusion(:custom, lambda do |event|
280
+ request = event.context[:request]
281
+ request && request[:user_agent]&.match?(/googlebot|bingbot/i)
282
+ end)
214
283
  ```
215
284
 
285
+ Your app, your rules. Lapsoss just provides the mechanism.
286
+
216
287
  ### Data Protection
217
288
 
218
289
  Lapsoss automatically integrates with Rails' parameter filtering:
@@ -251,6 +322,103 @@ config.transport_timeout = 10
251
322
  config.transport_jitter = true # Prevent thundering herd
252
323
  ```
253
324
 
325
+ ## Testing in Rails Console
326
+
327
+ Want to see Lapsoss in action? Try this in your Rails console:
328
+
329
+ ```ruby
330
+ # Configure Lapsoss with the logger adapter for immediate visibility
331
+ Lapsoss.configure do |config|
332
+ config.use_logger(name: :console_test)
333
+ config.async = false # Synchronous for immediate output
334
+ config.debug = true # Verbose logging
335
+ end
336
+
337
+ # Create a class that demonstrates error handling
338
+ class Liberation
339
+ def self.liberate!
340
+ Rails.error.handle do
341
+ raise StandardError, "Freedom requires breaking chains!"
342
+ end
343
+ puts "✅ Continued execution after error"
344
+ end
345
+
346
+ def self.revolt!
347
+ Rails.error.record do
348
+ raise RuntimeError, "Revolution cannot be stopped!"
349
+ end
350
+ puts "This won't print - error was re-raised"
351
+ end
352
+ end
353
+
354
+ # Test error capture (error is swallowed)
355
+ Liberation.liberate!
356
+ # You'll see the error logged but execution continues
357
+
358
+ # Test error recording (error is re-raised)
359
+ begin
360
+ Liberation.revolt!
361
+ rescue => e
362
+ puts "Caught re-raised error: #{e.message}"
363
+ end
364
+
365
+ # Manual error reporting with context
366
+ begin
367
+ 1 / 0
368
+ rescue => e
369
+ Rails.error.report(e, context: { user_id: 42, action: "console_test" })
370
+ end
371
+
372
+ # Check what was captured
373
+ puts "\n🎉 Lapsoss captured all errors through Rails.error!"
374
+ ```
375
+
376
+ You'll see all errors logged to your console with full backtraces and context. This same integration works automatically for all Rails controllers, jobs, and mailers.
377
+
378
+ ## Using Lapsoss Outside Rails
379
+
380
+ Lapsoss provides the same convenient error handling methods directly, perfect for background jobs, rake tasks, or standalone scripts:
381
+
382
+ ```ruby
383
+ # In your Sidekiq job, rake task, or any Ruby code
384
+ require 'lapsoss'
385
+
386
+ Lapsoss.configure do |config|
387
+ config.use_sentry(dsn: ENV['SENTRY_DSN'])
388
+ end
389
+
390
+ # Handle errors (swallow them)
391
+ result = Lapsoss.handle do
392
+ risky_operation
393
+ end
394
+ # Returns nil if error occurred, or the block's result
395
+
396
+ # Handle with fallback
397
+ user = Lapsoss.handle(fallback: User.anonymous) do
398
+ User.find(id)
399
+ end
400
+
401
+ # Record errors (re-raise them)
402
+ Lapsoss.record do
403
+ critical_operation # Error is captured then re-raised
404
+ end
405
+
406
+ # Report errors manually
407
+ begin
408
+ something_dangerous
409
+ rescue => e
410
+ Lapsoss.report(e, user_id: user.id, context: 'background_job')
411
+ # Continue processing...
412
+ end
413
+
414
+ # These methods mirror Rails.error exactly:
415
+ # - Lapsoss.handle → Rails.error.handle
416
+ # - Lapsoss.record → Rails.error.record
417
+ # - Lapsoss.report → Rails.error.report
418
+ ```
419
+
420
+ This means your error handling code works the same way everywhere - in Rails controllers, background jobs, rake tasks, or standalone scripts.
421
+
254
422
  ## Creating Custom Adapters
255
423
 
256
424
  ```ruby
@@ -264,6 +432,26 @@ end
264
432
  Lapsoss::Registry.register(:my_service, MyAdapter)
265
433
  ```
266
434
 
435
+ ### Extending Existing Adapters
436
+
437
+ For Sentry-compatible services, just extend the SentryAdapter:
438
+
439
+ ```ruby
440
+ class TelebugAdapter < Lapsoss::Adapters::SentryAdapter
441
+ def initialize(name = :telebug, settings = {})
442
+ super(name, settings)
443
+ end
444
+
445
+ private
446
+
447
+ def build_headers(public_key)
448
+ super(public_key).merge(
449
+ "X-Telebug-Client" => "lapsoss/#{Lapsoss::VERSION}"
450
+ )
451
+ end
452
+ end
453
+ ```
454
+
267
455
  ## Contributing
268
456
 
269
457
  1. Fork it
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
+ require "active_support/core_ext/class/attribute"
4
5
  require "active_support/core_ext/hash/indifferent_access"
5
6
 
6
7
  module Lapsoss
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sentry_adapter"
4
+
5
+ module Lapsoss
6
+ module Adapters
7
+ # Telebug adapter - uses Sentry protocol with Telebug endpoints
8
+ # Telebug is compatible with Sentry's API, so we inherit from SentryAdapter
9
+ class TelebugAdapter < SentryAdapter
10
+ def initialize(name = :telebug, settings = {})
11
+ super(name, settings)
12
+ end
13
+
14
+ private
15
+
16
+ # Override to parse Telebug DSN format
17
+ def parse_dsn(dsn_string)
18
+ uri = URI.parse(dsn_string)
19
+ {
20
+ public_key: uri.user,
21
+ project_id: uri.path.split("/").last,
22
+ host: uri.host,
23
+ path: uri.path
24
+ }
25
+ end
26
+
27
+ # Override to build Telebug-specific API path
28
+ def build_api_path(uri)
29
+ # Telebug uses: https://[key]@[host]/api/v1/sentry_errors/[project_id]
30
+ # The path is already complete: /api/v1/sentry_errors/4
31
+ # Unlike Sentry which needs /api/[project_id]/envelope/
32
+ uri.path
33
+ end
34
+
35
+ # Override to setup Telebug endpoint
36
+ def setup_endpoint
37
+ uri = URI.parse(@settings[:dsn])
38
+ # For Telebug, we use the full URL without port (unless non-standard)
39
+ port = (uri.port == 443 || uri.port == 80) ? "" : ":#{uri.port}"
40
+ self.class.api_endpoint = "#{uri.scheme}://#{uri.host}#{port}"
41
+ self.class.api_path = build_api_path(uri)
42
+ end
43
+
44
+ # Override headers builder to add Telebug-specific headers
45
+ def headers_for(envelope)
46
+ base_headers = super(envelope)
47
+ base_headers.merge(
48
+ "X-Telebug-Client" => "lapsoss/#{Lapsoss::VERSION}"
49
+ )
50
+ end
51
+
52
+ # Override user agent for Telebug
53
+ def user_agent
54
+ "lapsoss-telebug/#{Lapsoss::VERSION}"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -103,9 +103,7 @@ module Lapsoss
103
103
  end
104
104
 
105
105
  def handle_capture_error(error)
106
- return unless @configuration.logger
107
-
108
- @configuration.logger.error("[Lapsoss] Failed to capture event: #{error.message}")
106
+ @configuration.logger.error("Failed to capture event: #{error.message}")
109
107
  end
110
108
  end
111
109
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
3
4
  require "active_support/configurable"
4
5
 
5
6
  module Lapsoss
@@ -21,7 +22,7 @@ module Lapsoss
21
22
  def initialize
22
23
  @adapter_configs = {}
23
24
  @async = true
24
- @logger = nil
25
+ @logger = Logger.new(nil) # Default null logger
25
26
  @environment = nil
26
27
  @enabled = true
27
28
  @release = nil
@@ -84,6 +85,11 @@ module Lapsoss
84
85
  register_adapter(name, :sentry, **settings)
85
86
  end
86
87
 
88
+ # Convenience method for Telebug (Sentry-compatible)
89
+ def use_telebug(name: :telebug, **settings)
90
+ register_adapter(name, :telebug, **settings)
91
+ end
92
+
87
93
  # Convenience method for AppSignal
88
94
  def use_appsignal(name: :appsignal, **settings)
89
95
  register_adapter(name, :appsignal, **settings)
@@ -193,17 +199,8 @@ module Lapsoss
193
199
 
194
200
  def create_sampling_strategy
195
201
  case @sampling_strategy
196
- when Symbol
197
- case @sampling_strategy
198
- when :production
199
- Sampling::SamplingFactory.create_production_sampling
200
- when :development
201
- Sampling::SamplingFactory.create_development_sampling
202
- when :user_focused
203
- Sampling::SamplingFactory.create_user_focused_sampling
204
- else
205
- Sampling::UniformSampler.new(@sample_rate)
206
- end
202
+ when Numeric
203
+ Sampling::UniformSampler.new(@sampling_strategy)
207
204
  when Proc
208
205
  @sampling_strategy
209
206
  when nil
@@ -276,7 +273,7 @@ module Lapsoss
276
273
  def validate!
277
274
  # Check sample rate is between 0 and 1
278
275
  if @sample_rate && (@sample_rate < 0 || @sample_rate > 1)
279
- logger&.warn "[Lapsoss] sample_rate should be between 0 and 1, got #{@sample_rate}"
276
+ logger.warn "sample_rate should be between 0 and 1, got #{@sample_rate}"
280
277
  end
281
278
 
282
279
  # Check callables
@@ -289,21 +286,21 @@ module Lapsoss
289
286
 
290
287
  # Just log if transport settings look unusual
291
288
  if @transport_timeout && @transport_timeout <= 0
292
- logger&.warn "[Lapsoss] transport_timeout should be positive, got #{@transport_timeout}"
289
+ logger.warn "transport_timeout should be positive, got #{@transport_timeout}"
293
290
  end
294
291
 
295
292
  if @transport_max_retries && @transport_max_retries < 0
296
- logger&.warn "[Lapsoss] transport_max_retries should be non-negative, got #{@transport_max_retries}"
293
+ logger.warn "transport_max_retries should be non-negative, got #{@transport_max_retries}"
297
294
  end
298
295
 
299
296
  if @transport_initial_backoff && @transport_max_backoff && @transport_initial_backoff > @transport_max_backoff
300
- logger&.warn "[Lapsoss] transport_initial_backoff (#{@transport_initial_backoff}) should be less than transport_max_backoff (#{@transport_max_backoff})"
297
+ logger.warn "transport_initial_backoff (#{@transport_initial_backoff}) should be less than transport_max_backoff (#{@transport_max_backoff})"
301
298
  end
302
299
 
303
300
  # Validate adapter configurations exist
304
301
  @adapter_configs.each do |name, config|
305
302
  if config[:type].blank?
306
- logger&.warn "[Lapsoss] Adapter '#{name}' has no type specified"
303
+ logger.warn "Adapter '#{name}' has no type specified"
307
304
  end
308
305
  end
309
306
 
@@ -4,8 +4,9 @@ require "digest"
4
4
 
5
5
  module Lapsoss
6
6
  class Fingerprinter
7
- DEFAULT_PATTERNS = [
8
- # User/ID normalization patterns
7
+ # Base patterns that are always available
8
+ BASE_PATTERNS = [
9
+ # Generic error message normalization
9
10
  {
10
11
  pattern: /User \d+ (not found|invalid|missing)/i,
11
12
  fingerprint: "user-lookup-error"
@@ -15,21 +16,25 @@ module Lapsoss
15
16
  fingerprint: "record-lookup-error"
16
17
  },
17
18
 
18
- # URL/Path normalization patterns
19
+ # Network error patterns
19
20
  {
20
- pattern: %r{/users/\d+(/.*)?},
21
- fingerprint: "users-id-endpoint"
21
+ pattern: /Net::(TimeoutError|ReadTimeout|OpenTimeout)/,
22
+ fingerprint: "network-timeout"
22
23
  },
23
24
  {
24
- pattern: %r{/api/v\d+/.*},
25
- fingerprint: "api-endpoint"
25
+ pattern: /Errno::(ECONNREFUSED|ECONNRESET|EHOSTUNREACH)/,
26
+ fingerprint: "network-connection-error"
26
27
  },
27
28
 
28
- # Database error patterns
29
+ # Memory/Resource patterns
29
30
  {
30
- pattern: /PG::ConnectionBad|Mysql2::Error|SQLite3::BusyException/,
31
- fingerprint: "database-connection-error"
32
- },
31
+ pattern: /NoMemoryError|SystemStackError/,
32
+ fingerprint: "memory-resource-error"
33
+ }
34
+ ].freeze
35
+
36
+ # ActiveRecord-specific patterns (only loaded if ActiveRecord is defined)
37
+ ACTIVERECORD_PATTERNS = [
33
38
  {
34
39
  pattern: /ActiveRecord::RecordNotFound/,
35
40
  fingerprint: "record-not-found"
@@ -38,38 +43,34 @@ module Lapsoss
38
43
  pattern: /ActiveRecord::StatementInvalid.*timeout/i,
39
44
  fingerprint: "database-timeout"
40
45
  },
41
-
42
- # Network error patterns
43
- {
44
- pattern: /Net::(TimeoutError|ReadTimeout|OpenTimeout)/,
45
- fingerprint: "network-timeout"
46
- },
47
46
  {
48
- pattern: /Errno::(ECONNREFUSED|ECONNRESET|EHOSTUNREACH)/,
49
- fingerprint: "network-connection-error"
50
- },
47
+ pattern: /ActiveRecord::ConnectionTimeoutError/,
48
+ fingerprint: "database-connection-timeout"
49
+ }
50
+ ].freeze
51
51
 
52
- # File system patterns
52
+ # Database adapter patterns (only loaded if adapters are defined)
53
+ DATABASE_PATTERNS = [
53
54
  {
54
- pattern: %r{Errno::(ENOENT|EACCES).*/tmp/},
55
- fingerprint: "tmp-file-error"
55
+ pattern: /PG::ConnectionBad/,
56
+ fingerprint: "postgres-connection-error",
57
+ condition: -> { defined?(PG) }
56
58
  },
57
59
  {
58
- pattern: /No such file or directory.*\.log/,
59
- fingerprint: "log-file-missing"
60
+ pattern: /Mysql2::Error/,
61
+ fingerprint: "mysql-connection-error",
62
+ condition: -> { defined?(Mysql2) }
60
63
  },
61
-
62
- # Memory/Resource patterns
63
64
  {
64
- pattern: /NoMemoryError|SystemStackError/,
65
- fingerprint: "memory-resource-error"
65
+ pattern: /SQLite3::BusyException/,
66
+ fingerprint: "sqlite-busy-error",
67
+ condition: -> { defined?(SQLite3) }
66
68
  }
67
69
  ].freeze
68
70
 
69
71
  def initialize(config = {})
70
72
  @custom_callback = config[:custom_callback]
71
- @patterns = config[:patterns] || DEFAULT_PATTERNS
72
- @normalize_paths = config.fetch(:normalize_paths, true)
73
+ @patterns = build_patterns(config[:patterns])
73
74
  @normalize_ids = config.fetch(:normalize_ids, true)
74
75
  @include_environment = config.fetch(:include_environment, false)
75
76
  end
@@ -91,6 +92,23 @@ module Lapsoss
91
92
 
92
93
  private
93
94
 
95
+ def build_patterns(custom_patterns)
96
+ return custom_patterns if custom_patterns
97
+
98
+ patterns = BASE_PATTERNS.dup
99
+
100
+ # Always include ActiveRecord patterns - they match on string names
101
+ patterns.concat(ACTIVERECORD_PATTERNS)
102
+
103
+ # Add database-specific patterns - they also match on string names
104
+ DATABASE_PATTERNS.each do |pattern_config|
105
+ # Skip the condition check - just match on error names
106
+ patterns << pattern_config.except(:condition)
107
+ end
108
+
109
+ patterns
110
+ end
111
+
94
112
  def match_patterns(event)
95
113
  full_error_text = build_error_text(event)
96
114
 
@@ -170,18 +188,9 @@ module Lapsoss
170
188
 
171
189
  # Replace numeric IDs with placeholder (after UUIDs and hashes)
172
190
  normalized.gsub!(/\b\d{3,}\b/, ":id")
173
- end
174
-
175
- if @normalize_paths
176
- # Replace absolute file paths with placeholder
177
- normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)*\.[a-zA-Z0-9]+}, ":filepath")
178
- normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)+(?:/)?}, ":dirpath")
179
191
 
180
192
  # Replace timestamps
181
193
  normalized.gsub!(/\b\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/, ":timestamp")
182
-
183
- # Replace URLs with placeholder
184
- normalized.gsub!(%r{https?://[^\s]+}, ":url")
185
194
  end
186
195
 
187
196
  # Clean up extra whitespace
@@ -201,13 +210,9 @@ module Lapsoss
201
210
 
202
211
  line_to_use = app_line || backtrace.first
203
212
 
204
- if @normalize_paths
205
- # Extract just filename:line_number
206
- if line_to_use =~ %r{([^/]+):(\d+)}
207
- "#{::Regexp.last_match(1)}:#{::Regexp.last_match(2)}"
208
- else
209
- line_to_use
210
- end
213
+ # Extract just filename:line_number
214
+ if line_to_use =~ %r{([^/]+):(\d+)}
215
+ "#{::Regexp.last_match(1)}:#{::Regexp.last_match(2)}"
211
216
  else
212
217
  line_to_use
213
218
  end