lapsoss 0.4.0 → 0.4.2

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -7
  3. data/lib/lapsoss/client.rb +1 -3
  4. data/lib/lapsoss/configuration.rb +8 -17
  5. data/lib/lapsoss/fingerprinter.rb +52 -47
  6. data/lib/lapsoss/middleware/release_tracker.rb +11 -98
  7. data/lib/lapsoss/pipeline_builder.rb +2 -2
  8. data/lib/lapsoss/rails_middleware.rb +2 -2
  9. data/lib/lapsoss/railtie.rb +13 -2
  10. data/lib/lapsoss/registry.rb +7 -7
  11. data/lib/lapsoss/router.rb +1 -3
  12. data/lib/lapsoss/scrubber.rb +15 -152
  13. data/lib/lapsoss/validators.rb +48 -112
  14. data/lib/lapsoss/version.rb +1 -1
  15. metadata +1 -21
  16. data/lib/lapsoss/exclusion_configuration.rb +0 -30
  17. data/lib/lapsoss/exclusion_presets.rb +0 -249
  18. data/lib/lapsoss/middleware/sample_filter.rb +0 -23
  19. data/lib/lapsoss/middleware/sampling_middleware.rb +0 -18
  20. data/lib/lapsoss/middleware/user_context_enhancer.rb +0 -46
  21. data/lib/lapsoss/release_providers.rb +0 -110
  22. data/lib/lapsoss/sampling/adaptive_sampler.rb +0 -46
  23. data/lib/lapsoss/sampling/composite_sampler.rb +0 -26
  24. data/lib/lapsoss/sampling/consistent_hash_sampler.rb +0 -30
  25. data/lib/lapsoss/sampling/exception_type_sampler.rb +0 -44
  26. data/lib/lapsoss/sampling/health_based_sampler.rb +0 -19
  27. data/lib/lapsoss/sampling/sampling_factory.rb +0 -69
  28. data/lib/lapsoss/sampling/time_based_sampler.rb +0 -44
  29. data/lib/lapsoss/sampling/user_based_sampler.rb +0 -42
  30. data/lib/lapsoss/user_context.rb +0 -185
  31. data/lib/lapsoss/user_context_integrations.rb +0 -39
  32. data/lib/lapsoss/user_context_middleware.rb +0 -50
  33. data/lib/lapsoss/user_context_provider.rb +0 -93
  34. data/lib/lapsoss/utils.rb +0 -11
  35. 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: 93ff41a78372ad8d5080a5b7d87c2783a1f5834020df285e2db5eb28f0a54d61
4
+ data.tar.gz: 1cbceab257813d8e7659d9a31abd2cfba0a33a6f5585776ed426c984c32a61e4
5
5
  SHA512:
6
- metadata.gz: 5cfb22826215ffc2473410d6e984ecbd93f74ec0c011e742d7246549b29c1d77b9cb81c774cab311748c0b39cbfd9b9eccfdbb4324aa6da7a632ba8634eb3b19
7
- data.tar.gz: f641a491be8007a0bc6780094803724f66efc79aa1e10cadbb1ac96c84a8ff30af119c763e1611dc3e2218305c4d0b5958b6f5c5f2863c7b5694b5ff7c9f0856
6
+ metadata.gz: da64a5b1ec01779137189432e8dfba438e3768a1af44f71de44f3827c6ef3f2eb27b49d1b6d88479a23c51218e328f4068a86b4e210a2f73b869d2ff7167bb30
7
+ data.tar.gz: 126e54769b4f4ad8dad6617c27b5a80aeba5ae073879b462c8c6e01346fbc6a78029aca8589b028b6eccf9900fd1cc24e8072d119098190dbb86ac0b88521eba
data/README.md CHANGED
@@ -196,23 +196,82 @@ Lapsoss.configure do |config|
196
196
  # Data scrubbing (uses Rails filter_parameters automatically)
197
197
  config.scrub_fields = %w[password credit_card ssn] # Or leave nil to use Rails defaults
198
198
 
199
- # Error filtering
199
+ # Performance
200
+ config.async = true # Send errors in background
201
+
202
+ # Sampling (see docs/sampling_strategies.md for advanced examples)
203
+ config.sample_rate = Rails.env.production? ? 0.25 : 1.0
204
+
205
+ # Transport settings
206
+ config.transport_timeout = 10 # seconds
207
+ config.transport_max_retries = 3
208
+ end
209
+ ```
210
+
211
+ ### Filtering Errors
212
+
213
+ You decide what errors to track. Lapsoss doesn't make assumptions:
214
+
215
+ ```ruby
216
+ Lapsoss.configure do |config|
217
+ # Use the before_send callback for simple filtering
200
218
  config.before_send = lambda do |event|
201
219
  # Return nil to prevent sending
202
220
  return nil if event.exception.is_a?(ActiveRecord::RecordNotFound)
203
221
  event
204
222
  end
223
+
224
+ # Or use the exclusion filter for more complex rules
225
+ config.exclusion_filter = Lapsoss::ExclusionFilter.new(
226
+ # Exclude specific exception types
227
+ excluded_exceptions: [
228
+ "ActionController::RoutingError", # Your choice
229
+ "ActiveRecord::RecordNotFound" # Your decision
230
+ ],
231
+
232
+ # Exclude by pattern matching
233
+ excluded_patterns: [
234
+ /timeout/i, # If timeouts are expected in your app
235
+ /user not found/i # If these are normal in your workflow
236
+ ],
237
+
238
+ # Exclude specific error messages
239
+ excluded_messages: [
240
+ "No route matches",
241
+ "Invalid authenticity token"
242
+ ]
243
+ )
244
+
245
+ # Add custom exclusion logic
246
+ config.exclusion_filter.add_exclusion(:custom, lambda do |event|
247
+ # Your business logic here
248
+ event.context[:request]&.dig(:user_agent)&.match?(/bot/i)
249
+ end)
250
+ end
251
+ ```
205
252
 
206
- # Sampling
207
- config.sample_rate = Rails.env.production? ? 0.25 : 1.0
253
+ #### Common Patterns (Your Choice)
208
254
 
209
- # Performance
210
- config.async = true # Send errors in background
211
- config.transport_timeout = 10 # seconds
212
- config.transport_max_retries = 3
255
+ ```ruby
256
+ # Development/Test exclusions
257
+ if Rails.env.development?
258
+ config.exclusion_filter.add_exclusion(:exception, "RSpec::Expectations::ExpectationNotMetError")
259
+ config.exclusion_filter.add_exclusion(:exception, "Minitest::Assertion")
213
260
  end
261
+
262
+ # User input errors (if you don't want to track them)
263
+ config.exclusion_filter.add_exclusion(:exception, "ActiveRecord::RecordInvalid")
264
+ config.exclusion_filter.add_exclusion(:exception, "ActionController::ParameterMissing")
265
+
266
+ # Bot traffic (if you want to exclude it)
267
+ config.exclusion_filter.add_exclusion(:custom, lambda do |event|
268
+ request = event.context[:request]
269
+ request && request[:user_agent]&.match?(/googlebot|bingbot/i)
270
+ end)
214
271
  ```
215
272
 
273
+ Your app, your rules. Lapsoss just provides the mechanism.
274
+
216
275
  ### Data Protection
217
276
 
218
277
  Lapsoss automatically integrates with Rails' parameter filtering:
@@ -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
@@ -21,7 +21,7 @@ module Lapsoss
21
21
  def initialize
22
22
  @adapter_configs = {}
23
23
  @async = true
24
- @logger = nil
24
+ @logger = Logger.new(nil) # Default null logger
25
25
  @environment = nil
26
26
  @enabled = true
27
27
  @release = nil
@@ -193,17 +193,8 @@ module Lapsoss
193
193
 
194
194
  def create_sampling_strategy
195
195
  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
196
+ when Numeric
197
+ Sampling::UniformSampler.new(@sampling_strategy)
207
198
  when Proc
208
199
  @sampling_strategy
209
200
  when nil
@@ -276,7 +267,7 @@ module Lapsoss
276
267
  def validate!
277
268
  # Check sample rate is between 0 and 1
278
269
  if @sample_rate && (@sample_rate < 0 || @sample_rate > 1)
279
- logger&.warn "[Lapsoss] sample_rate should be between 0 and 1, got #{@sample_rate}"
270
+ logger.warn "sample_rate should be between 0 and 1, got #{@sample_rate}"
280
271
  end
281
272
 
282
273
  # Check callables
@@ -289,21 +280,21 @@ module Lapsoss
289
280
 
290
281
  # Just log if transport settings look unusual
291
282
  if @transport_timeout && @transport_timeout <= 0
292
- logger&.warn "[Lapsoss] transport_timeout should be positive, got #{@transport_timeout}"
283
+ logger.warn "transport_timeout should be positive, got #{@transport_timeout}"
293
284
  end
294
285
 
295
286
  if @transport_max_retries && @transport_max_retries < 0
296
- logger&.warn "[Lapsoss] transport_max_retries should be non-negative, got #{@transport_max_retries}"
287
+ logger.warn "transport_max_retries should be non-negative, got #{@transport_max_retries}"
297
288
  end
298
289
 
299
290
  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})"
291
+ logger.warn "transport_initial_backoff (#{@transport_initial_backoff}) should be less than transport_max_backoff (#{@transport_max_backoff})"
301
292
  end
302
293
 
303
294
  # Validate adapter configurations exist
304
295
  @adapter_configs.each do |name, config|
305
296
  if config[:type].blank?
306
- logger&.warn "[Lapsoss] Adapter '#{name}' has no type specified"
297
+ logger.warn "Adapter '#{name}' has no type specified"
307
298
  end
308
299
  end
309
300
 
@@ -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
@@ -3,114 +3,27 @@
3
3
  module Lapsoss
4
4
  module Middleware
5
5
  class ReleaseTracker < Base
6
- def initialize(app, release_provider: nil)
6
+ def initialize(app, release: nil)
7
7
  super(app)
8
- @release_provider = release_provider
8
+ @release = release
9
9
  end
10
10
 
11
11
  def call(event, hint = {})
12
- add_release_info(event, hint)
12
+ if release = detect_release
13
+ event.context[:release] = release
14
+ end
13
15
  @app.call(event, hint)
14
16
  end
15
17
 
16
18
  private
17
19
 
18
- def add_release_info(event, hint)
19
- release_info = @release_provider&.call(event, hint) || auto_detect_release
20
-
21
- event.context[:release] = release_info if release_info
22
- end
23
-
24
- def auto_detect_release
25
- release_info = {}
26
-
27
- # Try to detect Git information
28
- if git_info = detect_git_info
29
- release_info.merge!(git_info)
30
- end
31
-
32
- # Try to detect deployment info
33
- if deployment_info = detect_deployment_info
34
- release_info.merge!(deployment_info)
35
- end
36
-
37
- release_info.empty? ? nil : release_info
38
- end
39
-
40
- def detect_git_info
41
- return nil unless File.exist?(".git")
42
-
43
- begin
44
- # Get current commit SHA
45
- commit_sha = `git rev-parse HEAD`.strip
46
- return nil if commit_sha.empty?
47
-
48
- # Get branch name
49
- branch = `git rev-parse --abbrev-ref HEAD`.strip
50
- branch = nil if branch.empty? || branch == "HEAD"
51
-
52
- # Get commit timestamp
53
- commit_time = `git log -1 --format=%ct`.strip
54
- commit_timestamp = commit_time.empty? ? nil : Time.zone.at(commit_time.to_i)
55
-
56
- # Get tag if on a tag
57
- tag = `git describe --exact-match --tags HEAD 2>/dev/null`.strip
58
- tag = nil if tag.empty?
59
-
60
- {
61
- commit_sha: commit_sha,
62
- branch: branch,
63
- tag: tag,
64
- commit_timestamp: commit_timestamp
65
- }.compact
66
- rescue StandardError
67
- nil
68
- end
69
- end
70
-
71
- def detect_deployment_info
72
- info = {}
73
-
74
- # Check common deployment environment variables
75
- info[:deployment_id] = ENV["DEPLOYMENT_ID"] if ENV["DEPLOYMENT_ID"]
76
- info[:build_number] = ENV["BUILD_NUMBER"] if ENV["BUILD_NUMBER"]
77
- info[:deployment_time] = parse_deployment_time(ENV["DEPLOYMENT_TIME"]) if ENV["DEPLOYMENT_TIME"]
78
-
79
- # Check Heroku
80
- if ENV["HEROKU_APP_NAME"]
81
- info[:platform] = "heroku"
82
- info[:app_name] = ENV["HEROKU_APP_NAME"]
83
- info[:dyno] = ENV.fetch("DYNO", nil)
84
- info[:slug_commit] = ENV.fetch("HEROKU_SLUG_COMMIT", nil)
85
- end
86
-
87
- # Check AWS
88
- if ENV["AWS_EXECUTION_ENV"]
89
- info[:platform] = "aws"
90
- info[:execution_env] = ENV["AWS_EXECUTION_ENV"]
91
- info[:region] = ENV.fetch("AWS_REGION", nil)
92
- end
93
-
94
- # Check Docker
95
- if ENV["DOCKER_CONTAINER_ID"] || File.exist?("/.dockerenv")
96
- info[:platform] = "docker"
97
- info[:container_id] = ENV["DOCKER_CONTAINER_ID"]
98
- end
99
-
100
- # Check Kubernetes
101
- if ENV["KUBERNETES_SERVICE_HOST"]
102
- info[:platform] = "kubernetes"
103
- info[:namespace] = ENV.fetch("KUBERNETES_NAMESPACE", nil)
104
- info[:pod_name] = ENV.fetch("HOSTNAME", nil)
105
- end
106
-
107
- info.empty? ? nil : info
108
- end
20
+ def detect_release
21
+ # Use configured release
22
+ return @release.call if @release.respond_to?(:call)
23
+ return @release if @release.present?
109
24
 
110
- def parse_deployment_time(time_str)
111
- Time.zone.parse(time_str)
112
- rescue StandardError
113
- nil
25
+ # Use rails_app_version gem if available
26
+ Rails.application.version.to_s if defined?(Rails) && Rails.application.respond_to?(:version)
114
27
  end
115
28
  end
116
29
  end
@@ -25,8 +25,8 @@ module Lapsoss
25
25
  self
26
26
  end
27
27
 
28
- def track_releases(provider: nil)
29
- @pipeline.use(Middleware::ReleaseTracker, release_provider: provider)
28
+ def track_releases(release: nil)
29
+ @pipeline.use(Middleware::ReleaseTracker, release: release)
30
30
  self
31
31
  end
32
32
 
@@ -10,14 +10,14 @@ module Lapsoss
10
10
  Lapsoss::Current.with_clean_scope do
11
11
  # Add request context to current scope
12
12
  if Lapsoss.configuration.capture_request_context
13
- Rails.logger.debug "[Lapsoss] Adding request context" if Rails.env.test?
13
+ Rails.logger.tagged("Lapsoss") { Rails.logger.debug "Adding request context" } if Rails.env.test?
14
14
  add_request_context(env)
15
15
  end
16
16
 
17
17
  begin
18
18
  @app.call(env)
19
19
  rescue Exception => e
20
- Rails.logger.debug { "[Lapsoss] Capturing exception: #{e.class} - #{e.message}" } if Rails.env.test?
20
+ Rails.logger.tagged("Lapsoss") { Rails.logger.debug "Capturing exception: #{e.class} - #{e.message}" } if Rails.env.test?
21
21
  # Capture the exception
22
22
  Lapsoss.capture_exception(e)
23
23
  # Re-raise the exception to maintain Rails error handling
@@ -2,7 +2,13 @@
2
2
 
3
3
  module Lapsoss
4
4
  class Railtie < Rails::Railtie
5
- Rails.logger.debug "[Lapsoss] Railtie loaded" if ENV["DEBUG_LAPSOSS"]
5
+ if ENV["DEBUG_LAPSOSS"]
6
+ if Rails.logger.respond_to?(:tagged)
7
+ Rails.logger.tagged("Lapsoss") { Rails.logger.debug "Railtie loaded" }
8
+ else
9
+ Rails.logger.debug "[Lapsoss] Railtie loaded"
10
+ end
11
+ end
6
12
  config.lapsoss = ActiveSupport::OrderedOptions.new
7
13
 
8
14
  initializer "lapsoss.configure" do |_app|
@@ -14,7 +20,12 @@ module Lapsoss
14
20
  Rails.env
15
21
  end
16
22
 
17
- config.logger ||= Rails.logger
23
+ # Use tagged logger for all Lapsoss logs
24
+ config.logger ||= if Rails.logger.respond_to?(:tagged)
25
+ Rails.logger.tagged("Lapsoss")
26
+ else
27
+ ActiveSupport::TaggedLogging.new(Rails.logger).tagged("Lapsoss")
28
+ end
18
29
 
19
30
  config.release ||= if Rails.application.respond_to?(:version)
20
31
  Rails.application.version.to_s
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "singleton"
4
4
  require "concurrent"
5
+ require "active_support/core_ext/string/inflections"
5
6
 
6
7
  module Lapsoss
7
8
  class Registry
@@ -42,7 +43,7 @@ module Lapsoss
42
43
  name = if adapter.respond_to?(:name) && adapter.name
43
44
  adapter.name.to_sym
44
45
  elsif adapter.class.name
45
- adapter.class.name.split("::").last.to_sym
46
+ adapter.class.name.demodulize.underscore.to_sym
46
47
  else
47
48
  # Generate a unique name if class name is nil (anonymous class)
48
49
  :"adapter_#{adapter.object_id}"
@@ -116,13 +117,12 @@ module Lapsoss
116
117
  # Resolve adapter type to class
117
118
  def resolve_adapter_class(type)
118
119
  # Try to get the class by convention: Adapters::{Type}Adapter
119
- class_name = "#{type.to_s.split('_').map(&:capitalize).join}Adapter"
120
+ class_name = "#{type.to_s.camelize}Adapter"
121
+ full_class_name = "Lapsoss::Adapters::#{class_name}"
120
122
 
121
- begin
122
- Adapters.const_get(class_name)
123
- rescue NameError
124
- raise AdapterNotFoundError, "Unknown adapter type: #{type}. Expected class: Lapsoss::Adapters::#{class_name}"
125
- end
123
+ full_class_name.constantize
124
+ rescue NameError
125
+ raise AdapterNotFoundError, "Unknown adapter type: #{type}. Expected class: #{full_class_name}"
126
126
  end
127
127
  end
128
128
  end
@@ -19,10 +19,8 @@ module Lapsoss
19
19
 
20
20
  # Handle adapter errors gracefully
21
21
  def handle_adapter_error(adapter, event, error)
22
- return unless Lapsoss.configuration.logger
23
-
24
22
  Lapsoss.configuration.logger.error(
25
- "[Lapsoss] Adapter '#{adapter.name}' failed to capture event (type: #{event.type}): #{error.message}"
23
+ "Adapter '#{adapter.name}' failed to capture event (type: #{event.type}): #{error.message}"
26
24
  )
27
25
 
28
26
  # Call error handler if configured