lapsoss 0.1.0 → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest"
3
+ require 'digest'
4
4
 
5
5
  module Lapsoss
6
6
  class Fingerprinter
@@ -8,61 +8,61 @@ module Lapsoss
8
8
  # User/ID normalization patterns
9
9
  {
10
10
  pattern: /User \d+ (not found|invalid|missing)/i,
11
- fingerprint: "user-lookup-error"
11
+ fingerprint: 'user-lookup-error'
12
12
  },
13
13
  {
14
14
  pattern: /Record \d+ (not found|invalid|missing)/i,
15
- fingerprint: "record-lookup-error"
15
+ fingerprint: 'record-lookup-error'
16
16
  },
17
17
 
18
18
  # URL/Path normalization patterns
19
19
  {
20
20
  pattern: %r{/users/\d+(/.*)?},
21
- fingerprint: "users-id-endpoint"
21
+ fingerprint: 'users-id-endpoint'
22
22
  },
23
23
  {
24
24
  pattern: %r{/api/v\d+/.*},
25
- fingerprint: "api-endpoint"
25
+ fingerprint: 'api-endpoint'
26
26
  },
27
27
 
28
28
  # Database error patterns
29
29
  {
30
30
  pattern: /PG::ConnectionBad|Mysql2::Error|SQLite3::BusyException/,
31
- fingerprint: "database-connection-error"
31
+ fingerprint: 'database-connection-error'
32
32
  },
33
33
  {
34
34
  pattern: /ActiveRecord::RecordNotFound/,
35
- fingerprint: "record-not-found"
35
+ fingerprint: 'record-not-found'
36
36
  },
37
37
  {
38
38
  pattern: /ActiveRecord::StatementInvalid.*timeout/i,
39
- fingerprint: "database-timeout"
39
+ fingerprint: 'database-timeout'
40
40
  },
41
41
 
42
42
  # Network error patterns
43
43
  {
44
44
  pattern: /Net::(TimeoutError|ReadTimeout|OpenTimeout)/,
45
- fingerprint: "network-timeout"
45
+ fingerprint: 'network-timeout'
46
46
  },
47
47
  {
48
48
  pattern: /Errno::(ECONNREFUSED|ECONNRESET|EHOSTUNREACH)/,
49
- fingerprint: "network-connection-error"
49
+ fingerprint: 'network-connection-error'
50
50
  },
51
51
 
52
52
  # File system patterns
53
53
  {
54
- pattern: /Errno::(ENOENT|EACCES).*\/tmp\//,
55
- fingerprint: "tmp-file-error"
54
+ pattern: %r{Errno::(ENOENT|EACCES).*/tmp/},
55
+ fingerprint: 'tmp-file-error'
56
56
  },
57
57
  {
58
58
  pattern: /No such file or directory.*\.log/,
59
- fingerprint: "log-file-missing"
59
+ fingerprint: 'log-file-missing'
60
60
  },
61
61
 
62
62
  # Memory/Resource patterns
63
63
  {
64
64
  pattern: /NoMemoryError|SystemStackError/,
65
- fingerprint: "memory-resource-error"
65
+ fingerprint: 'memory-resource-error'
66
66
  }
67
67
  ].freeze
68
68
 
@@ -121,11 +121,9 @@ module Lapsoss
121
121
  parts << event.message if event.message
122
122
 
123
123
  # Include first few backtrace lines for context
124
- if event.exception&.backtrace
125
- parts.concat(event.exception.backtrace.first(3))
126
- end
124
+ parts.concat(event.exception.backtrace.first(3)) if event.exception&.backtrace
127
125
 
128
- parts.compact.join(" ")
126
+ parts.compact.join(' ')
129
127
  end
130
128
 
131
129
  def generate_default_fingerprint(event)
@@ -143,17 +141,15 @@ module Lapsoss
143
141
  primary_location = extract_primary_location(event.exception.backtrace)
144
142
  components << primary_location if primary_location
145
143
  elsif event.message
146
- components << "message"
144
+ components << 'message'
147
145
  components << normalize_message(event.message)
148
146
  end
149
147
 
150
148
  # Include environment if configured
151
- if @include_environment && event.environment
152
- components << event.environment
153
- end
149
+ components << event.environment if @include_environment && event.environment
154
150
 
155
151
  # Generate hash from components
156
- content = components.compact.join("|")
152
+ content = components.compact.join('|')
157
153
  Digest::SHA256.hexdigest(content)[0, 16] # Use first 16 chars for readability
158
154
  end
159
155
 
@@ -164,29 +160,29 @@ module Lapsoss
164
160
 
165
161
  if @normalize_ids
166
162
  # Replace UUIDs first (before numeric IDs)
167
- normalized.gsub!(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i, ":uuid")
163
+ normalized.gsub!(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i, ':uuid')
168
164
 
169
165
  # Replace hex hashes with placeholder
170
- normalized.gsub!(/\b[0-9a-f]{32,}\b/i, ":hash")
166
+ normalized.gsub!(/\b[0-9a-f]{32,}\b/i, ':hash')
171
167
 
172
168
  # Replace numeric IDs with placeholder (after UUIDs and hashes)
173
- normalized.gsub!(/\b\d{3,}\b/, ":id")
169
+ normalized.gsub!(/\b\d{3,}\b/, ':id')
174
170
  end
175
171
 
176
172
  if @normalize_paths
177
173
  # Replace absolute file paths with placeholder
178
- normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)*\.[a-zA-Z0-9]+}, ":filepath")
179
- normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)+(?:/)?}, ":dirpath")
174
+ normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)*\.[a-zA-Z0-9]+}, ':filepath')
175
+ normalized.gsub!(%r{/[^/\s]+(?:/[^/\s]+)+(?:/)?}, ':dirpath')
180
176
 
181
177
  # Replace timestamps
182
- normalized.gsub!(/\b\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/, ":timestamp")
178
+ normalized.gsub!(/\b\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/, ':timestamp')
183
179
 
184
180
  # Replace URLs with placeholder
185
- normalized.gsub!(%r{https?://[^\s]+}, ":url")
181
+ normalized.gsub!(%r{https?://[^\s]+}, ':url')
186
182
  end
187
183
 
188
184
  # Clean up extra whitespace
189
- normalized.strip.squeeze(" ")
185
+ normalized.strip.squeeze(' ')
190
186
  end
191
187
 
192
188
  def extract_primary_location(backtrace)
@@ -194,18 +190,18 @@ module Lapsoss
194
190
 
195
191
  # Find first non-gem, non-framework line
196
192
  app_line = backtrace.find do |line|
197
- !line.include?("/gems/") &&
198
- !line.include?("/ruby/") &&
199
- !line.include?("(eval)") &&
200
- !line.start_with?("[")
193
+ line.exclude?('/gems/') &&
194
+ line.exclude?('/ruby/') &&
195
+ line.exclude?('(eval)') &&
196
+ !line.start_with?('[')
201
197
  end
202
198
 
203
199
  line_to_use = app_line || backtrace.first
204
200
 
205
201
  if @normalize_paths
206
202
  # Extract just filename:line_number
207
- if line_to_use.match(%r{([^/]+):(\d+)})
208
- "#{$1}:#{$2}"
203
+ if line_to_use =~ %r{([^/]+):(\d+)}
204
+ "#{::Regexp.last_match(1)}:#{::Regexp.last_match(2)}"
209
205
  else
210
206
  line_to_use
211
207
  end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "faraday"
4
- require "faraday/retry"
5
- require "json"
6
- require "zlib"
3
+ require 'faraday'
4
+ require 'faraday/retry'
5
+ require 'json'
6
+ require 'zlib'
7
7
 
8
8
  module Lapsoss
9
9
  # HTTP client wrapper using Faraday with retry logic
10
10
  class HttpClient
11
- USER_AGENT = "lapsoss/#{Lapsoss::VERSION}"
11
+ USER_AGENT = "lapsoss/#{Lapsoss::VERSION}".freeze
12
12
 
13
13
  def initialize(base_url, config = {})
14
14
  @base_url = base_url
@@ -53,18 +53,52 @@ module Lapsoss
53
53
  conn.options.open_timeout = @config[:timeout] || 5
54
54
 
55
55
  # Configure SSL
56
- if @config.key?(:ssl_verify)
57
- conn.ssl.verify = @config[:ssl_verify]
58
- end
56
+ conn.ssl.verify = @config[:ssl_verify] if @config.key?(:ssl_verify)
59
57
 
60
58
  # Set user agent
61
- conn.headers["User-Agent"] = USER_AGENT
59
+ conn.headers['User-Agent'] = USER_AGENT
62
60
 
63
- # Use default adapter
64
- conn.adapter Faraday.default_adapter
61
+ # Auto-detect and use appropriate adapter
62
+ conn.adapter detect_optimal_adapter
65
63
  end
66
64
  end
67
65
 
66
+ def detect_optimal_adapter
67
+ if fiber_scheduler_active? && async_adapter_available? && !force_sync_mode?
68
+ log_adapter_selection(:async)
69
+ :async_http
70
+ else
71
+ log_adapter_selection(:sync)
72
+ Faraday.default_adapter
73
+ end
74
+ end
75
+
76
+ def fiber_scheduler_active?
77
+ Fiber.current_scheduler != nil
78
+ end
79
+
80
+ def async_adapter_available?
81
+ require 'async/http/faraday'
82
+ true
83
+ rescue LoadError
84
+ false
85
+ end
86
+
87
+ def force_sync_mode?
88
+ Lapsoss.configuration.force_sync_http
89
+ end
90
+
91
+ def log_adapter_selection(adapter_type)
92
+ return unless Lapsoss.configuration.debug?
93
+
94
+ Lapsoss.configuration.logger&.debug(
95
+ "[Lapsoss::HttpClient] Using #{adapter_type} HTTP adapter " \
96
+ "(fiber_scheduler: #{fiber_scheduler_active?}, " \
97
+ "async_available: #{async_adapter_available?}, " \
98
+ "force_sync: #{force_sync_mode?})"
99
+ )
100
+ end
101
+
68
102
  def retry_options
69
103
  {
70
104
  max: @config[:max_retries] || 3,
@@ -22,14 +22,10 @@ module Lapsoss
22
22
 
23
23
  def call(event, hint = {})
24
24
  # Apply custom sampling logic first
25
- if @sample_callback
26
- return nil unless @sample_callback.call(event, hint)
27
- end
25
+ return nil if @sample_callback && !@sample_callback.call(event, hint)
28
26
 
29
27
  # Apply rate-based sampling
30
- if @sample_rate < 1.0
31
- return nil if rand > @sample_rate
32
- end
28
+ return nil if (@sample_rate < 1.0) && (rand > @sample_rate)
33
29
 
34
30
  @app.call(event, hint)
35
31
  end
@@ -44,6 +40,7 @@ module Lapsoss
44
40
 
45
41
  def call(event, hint = {})
46
42
  return nil if should_exclude?(event)
43
+
47
44
  @app.call(event, hint)
48
45
  end
49
46
 
@@ -95,22 +92,18 @@ module Lapsoss
95
92
  enhanced_user = existing_user.merge(user_data)
96
93
 
97
94
  # Apply privacy filtering if enabled
98
- if @privacy_mode
99
- enhanced_user = apply_privacy_filtering(enhanced_user)
100
- end
95
+ enhanced_user = apply_privacy_filtering(enhanced_user) if @privacy_mode
101
96
 
102
97
  event.context[:user] = enhanced_user unless enhanced_user.empty?
103
98
  end
104
99
 
105
100
  def apply_privacy_filtering(user_data)
106
101
  # Remove sensitive fields in privacy mode
107
- sensitive_fields = [:email, :phone, :address, :ssn, :credit_card]
102
+ sensitive_fields = %i[email phone address ssn credit_card]
108
103
  filtered = user_data.dup
109
104
 
110
105
  sensitive_fields.each do |field|
111
- if filtered.key?(field)
112
- filtered[field] = "[FILTERED]"
113
- end
106
+ filtered[field] = '[FILTERED]' if filtered.key?(field)
114
107
  end
115
108
 
116
109
  filtered
@@ -133,9 +126,7 @@ module Lapsoss
133
126
  def add_release_info(event, hint)
134
127
  release_info = @release_provider&.call(event, hint) || auto_detect_release
135
128
 
136
- if release_info
137
- event.context[:release] = release_info
138
- end
129
+ event.context[:release] = release_info if release_info
139
130
  end
140
131
 
141
132
  def auto_detect_release
@@ -155,7 +146,7 @@ module Lapsoss
155
146
  end
156
147
 
157
148
  def detect_git_info
158
- return nil unless File.exist?(".git")
149
+ return nil unless File.exist?('.git')
159
150
 
160
151
  begin
161
152
  # Get current commit SHA
@@ -164,11 +155,11 @@ module Lapsoss
164
155
 
165
156
  # Get branch name
166
157
  branch = `git rev-parse --abbrev-ref HEAD`.strip
167
- branch = nil if branch.empty? || branch == "HEAD"
158
+ branch = nil if branch.empty? || branch == 'HEAD'
168
159
 
169
160
  # Get commit timestamp
170
161
  commit_time = `git log -1 --format=%ct`.strip
171
- commit_timestamp = commit_time.empty? ? nil : Time.at(commit_time.to_i)
162
+ commit_timestamp = commit_time.empty? ? nil : Time.zone.at(commit_time.to_i)
172
163
 
173
164
  # Get tag if on a tag
174
165
  tag = `git describe --exact-match --tags HEAD 2>/dev/null`.strip
@@ -189,43 +180,43 @@ module Lapsoss
189
180
  info = {}
190
181
 
191
182
  # Check common deployment environment variables
192
- info[:deployment_id] = ENV["DEPLOYMENT_ID"] if ENV["DEPLOYMENT_ID"]
193
- info[:build_number] = ENV["BUILD_NUMBER"] if ENV["BUILD_NUMBER"]
194
- info[:deployment_time] = parse_deployment_time(ENV["DEPLOYMENT_TIME"]) if ENV["DEPLOYMENT_TIME"]
183
+ info[:deployment_id] = ENV['DEPLOYMENT_ID'] if ENV['DEPLOYMENT_ID']
184
+ info[:build_number] = ENV['BUILD_NUMBER'] if ENV['BUILD_NUMBER']
185
+ info[:deployment_time] = parse_deployment_time(ENV['DEPLOYMENT_TIME']) if ENV['DEPLOYMENT_TIME']
195
186
 
196
187
  # Check Heroku
197
- if ENV["HEROKU_APP_NAME"]
198
- info[:platform] = "heroku"
199
- info[:app_name] = ENV["HEROKU_APP_NAME"]
200
- info[:dyno] = ENV["DYNO"]
201
- info[:slug_commit] = ENV["HEROKU_SLUG_COMMIT"]
188
+ if ENV['HEROKU_APP_NAME']
189
+ info[:platform] = 'heroku'
190
+ info[:app_name] = ENV['HEROKU_APP_NAME']
191
+ info[:dyno] = ENV.fetch('DYNO', nil)
192
+ info[:slug_commit] = ENV.fetch('HEROKU_SLUG_COMMIT', nil)
202
193
  end
203
194
 
204
195
  # Check AWS
205
- if ENV["AWS_EXECUTION_ENV"]
206
- info[:platform] = "aws"
207
- info[:execution_env] = ENV["AWS_EXECUTION_ENV"]
208
- info[:region] = ENV["AWS_REGION"]
196
+ if ENV['AWS_EXECUTION_ENV']
197
+ info[:platform] = 'aws'
198
+ info[:execution_env] = ENV['AWS_EXECUTION_ENV']
199
+ info[:region] = ENV.fetch('AWS_REGION', nil)
209
200
  end
210
201
 
211
202
  # Check Docker
212
- if ENV["DOCKER_CONTAINER_ID"] || File.exist?("/.dockerenv")
213
- info[:platform] = "docker"
214
- info[:container_id] = ENV["DOCKER_CONTAINER_ID"]
203
+ if ENV['DOCKER_CONTAINER_ID'] || File.exist?('/.dockerenv')
204
+ info[:platform] = 'docker'
205
+ info[:container_id] = ENV['DOCKER_CONTAINER_ID']
215
206
  end
216
207
 
217
208
  # Check Kubernetes
218
- if ENV["KUBERNETES_SERVICE_HOST"]
219
- info[:platform] = "kubernetes"
220
- info[:namespace] = ENV["KUBERNETES_NAMESPACE"]
221
- info[:pod_name] = ENV["HOSTNAME"]
209
+ if ENV['KUBERNETES_SERVICE_HOST']
210
+ info[:platform] = 'kubernetes'
211
+ info[:namespace] = ENV.fetch('KUBERNETES_NAMESPACE', nil)
212
+ info[:pod_name] = ENV.fetch('HOSTNAME', nil)
222
213
  end
223
214
 
224
215
  info.empty? ? nil : info
225
216
  end
226
217
 
227
218
  def parse_deployment_time(time_str)
228
- Time.parse(time_str)
219
+ Time.zone.parse(time_str)
229
220
  rescue StandardError
230
221
  nil
231
222
  end
@@ -253,6 +244,7 @@ module Lapsoss
253
244
 
254
245
  def call(event, hint = {})
255
246
  return nil unless @condition.call(event, hint)
247
+
256
248
  @app.call(event, hint)
257
249
  end
258
250
  end
@@ -266,6 +258,7 @@ module Lapsoss
266
258
  def call(event, hint = {})
267
259
  transformed_event = @transformer.call(event, hint)
268
260
  return nil unless transformed_event
261
+
269
262
  @app.call(transformed_event, hint)
270
263
  end
271
264
  end
@@ -281,14 +274,12 @@ module Lapsoss
281
274
 
282
275
  def call(event, hint = {})
283
276
  @mutex.synchronize do
284
- now = Time.now
277
+ now = Time.zone.now
285
278
  # Remove old events outside time window
286
279
  @events.reject! { |timestamp| now - timestamp > @time_window }
287
280
 
288
281
  # Check if we're over the limit
289
- if @events.length >= @max_events
290
- return nil
291
- end
282
+ return nil if @events.length >= @max_events
292
283
 
293
284
  # Add current event
294
285
  @events << now
@@ -346,6 +337,7 @@ module Lapsoss
346
337
 
347
338
  def call(event, hint = {})
348
339
  return nil unless @sampler.sample?(event, hint)
340
+
349
341
  @app.call(event, hint)
350
342
  end
351
343
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "middleware"
3
+ require_relative 'middleware'
4
4
 
5
5
  module Lapsoss
6
6
  class Pipeline
@@ -46,7 +46,7 @@ module Lapsoss
46
46
 
47
47
  def build_chain
48
48
  # The final app just returns the event
49
- final_app = lambda { |event, hint| event }
49
+ final_app = ->(event, _hint) { event }
50
50
 
51
51
  # Build middleware chain from right to left
52
52
  @middlewares.reverse.reduce(final_app) do |app, middleware_config|
@@ -115,17 +115,13 @@ module Lapsoss
115
115
  self
116
116
  end
117
117
 
118
- def use_middleware(middleware_class, *args, **kwargs)
119
- @pipeline.use(middleware_class, *args, **kwargs)
118
+ def use_middleware(middleware_class, *, **)
119
+ @pipeline.use(middleware_class, *, **)
120
120
  self
121
121
  end
122
122
 
123
- def build
124
- @pipeline.build
125
- end
123
+ delegate :build, to: :@pipeline
126
124
 
127
- def pipeline
128
- @pipeline
129
- end
125
+ attr_reader :pipeline
130
126
  end
131
127
  end
@@ -4,7 +4,7 @@ module Lapsoss
4
4
  class Railtie < Rails::Railtie
5
5
  config.lapsoss = ActiveSupport::OrderedOptions.new
6
6
 
7
- initializer "lapsoss.configure" do |app|
7
+ initializer 'lapsoss.configure' do |_app|
8
8
  Lapsoss.configure do |config|
9
9
  config.environment ||= Rails.env
10
10
  config.logger ||= Rails.logger
@@ -18,13 +18,13 @@ module Lapsoss
18
18
  end
19
19
  end
20
20
 
21
- initializer "lapsoss.rails_error_subscriber", after: "lapsoss.configure" do |app|
22
- if Rails.version.to_f >= 7.0
23
- app.executor.error_reporter.subscribe(Lapsoss::RailsErrorSubscriber.new)
24
- end
21
+ initializer 'lapsoss.add_middleware', after: 'lapsoss.configure' do |app|
22
+ app.middleware.insert_before ActionDispatch::ShowExceptions, Lapsoss::Middleware
25
23
  end
26
24
 
27
-
25
+ initializer 'lapsoss.rails_error_subscriber', after: 'lapsoss.add_middleware' do |app|
26
+ app.executor.error_reporter.subscribe(Lapsoss::RailsErrorSubscriber.new)
27
+ end
28
28
 
29
29
  rake_tasks do
30
30
  # Add any Lapsoss-specific rake tasks here
@@ -43,7 +43,7 @@ module Lapsoss
43
43
  level: level,
44
44
  tags: {
45
45
  handled: handled,
46
- source: source || "rails"
46
+ source: source || 'rails'
47
47
  },
48
48
  context: context
49
49
  )
@@ -53,8 +53,8 @@ module Lapsoss
53
53
 
54
54
  def skip_error?(error, source)
55
55
  # Skip cache-related Redis errors if configured to do so
56
- if Lapsoss.configuration.skip_rails_cache_errors
57
- return true if source&.include?("cache") && error.is_a?(Redis::CannotConnectError)
56
+ if Lapsoss.configuration.skip_rails_cache_errors && source&.include?('cache') && error.is_a?(Redis::CannotConnectError)
57
+ return true
58
58
  end
59
59
 
60
60
  false
@@ -1,13 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
4
- require "concurrent"
5
- require_relative "adapters/base"
6
- require_relative "adapters/logger_adapter"
7
- require_relative "adapters/sentry_adapter"
8
- require_relative "adapters/appsignal_adapter"
9
- require_relative "adapters/rollbar_adapter"
10
- require_relative "adapters/insight_hub_adapter"
3
+ require 'singleton'
4
+ require 'concurrent'
11
5
 
12
6
  module Lapsoss
13
7
  class Registry
@@ -38,12 +32,24 @@ module Lapsoss
38
32
  adapter
39
33
  end
40
34
 
35
+ # Register an adapter instance directly (for testing)
36
+ #
37
+ # @param adapter [Adapter] The adapter instance to register
38
+ def register_adapter(adapter)
39
+ name = if adapter.respond_to?(:name) && adapter.name
40
+ adapter.name.to_sym
41
+ else
42
+ adapter.class.name.split('::').last.to_sym
43
+ end
44
+ @adapters[name] = adapter
45
+ end
46
+
41
47
  # Unregister an adapter
42
48
  #
43
49
  # @param name [Symbol] The adapter name to remove
44
50
  def unregister(name)
45
51
  adapter = @adapters.delete(name.to_sym)
46
- adapter&.shutdown if adapter&.respond_to?(:shutdown)
52
+ adapter&.shutdown if adapter.respond_to?(:shutdown)
47
53
  adapter
48
54
  end
49
55
 
@@ -79,7 +85,7 @@ module Lapsoss
79
85
 
80
86
  # Clear all adapters
81
87
  def clear!
82
- @adapters.values.each do |adapter|
88
+ @adapters.each_value do |adapter|
83
89
  adapter.shutdown if adapter.respond_to?(:shutdown)
84
90
  end
85
91
  @adapters.clear
@@ -96,19 +102,14 @@ module Lapsoss
96
102
 
97
103
  # Resolve adapter type to class
98
104
  def resolve_adapter_class(type)
99
- adapter_map[type.to_sym] || raise(AdapterNotFoundError, "Unknown adapter type: #{type}")
100
- end
105
+ # Try to get the class by convention: Adapters::{Type}Adapter
106
+ class_name = "#{type.to_s.split('_').map(&:capitalize).join}Adapter"
101
107
 
102
- # Map of adapter types to classes
103
- def adapter_map
104
- {
105
- logger: Adapters::LoggerAdapter,
106
- sentry: Adapters::SentryAdapter,
107
- appsignal: Adapters::AppsignalAdapter,
108
- rollbar: Adapters::RollbarAdapter,
109
- insight_hub: Adapters::InsightHubAdapter,
110
- bugsnag: Adapters::InsightHubAdapter # Backwards compatibility
111
- }
108
+ begin
109
+ Adapters.const_get(class_name)
110
+ rescue NameError
111
+ raise AdapterNotFoundError, "Unknown adapter type: #{type}. Expected class: Lapsoss::Adapters::#{class_name}"
112
+ end
112
113
  end
113
114
  end
114
115
  end