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.
- checksums.yaml +4 -4
- data/README.md +153 -733
- data/lib/lapsoss/adapters/appsignal_adapter.rb +7 -8
- data/lib/lapsoss/adapters/base.rb +0 -3
- data/lib/lapsoss/adapters/bugsnag_adapter.rb +12 -0
- data/lib/lapsoss/adapters/insight_hub_adapter.rb +102 -101
- data/lib/lapsoss/adapters/logger_adapter.rb +7 -7
- data/lib/lapsoss/adapters/rollbar_adapter.rb +93 -54
- data/lib/lapsoss/adapters/sentry_adapter.rb +11 -17
- data/lib/lapsoss/backtrace_frame.rb +35 -214
- data/lib/lapsoss/backtrace_frame_factory.rb +228 -0
- data/lib/lapsoss/backtrace_processor.rb +37 -37
- data/lib/lapsoss/client.rb +2 -6
- data/lib/lapsoss/configuration.rb +25 -22
- data/lib/lapsoss/current.rb +9 -1
- data/lib/lapsoss/event.rb +30 -6
- data/lib/lapsoss/exception_backtrace_frame.rb +39 -0
- data/lib/lapsoss/exclusion_configuration.rb +30 -0
- data/lib/lapsoss/exclusion_filter.rb +156 -0
- data/lib/lapsoss/{exclusions.rb → exclusion_presets.rb} +1 -181
- data/lib/lapsoss/fingerprinter.rb +9 -13
- data/lib/lapsoss/http_client.rb +42 -8
- data/lib/lapsoss/merged_scope.rb +63 -0
- data/lib/lapsoss/middleware/base.rb +15 -0
- data/lib/lapsoss/middleware/conditional_filter.rb +18 -0
- data/lib/lapsoss/middleware/event_enricher.rb +19 -0
- data/lib/lapsoss/middleware/event_transformer.rb +19 -0
- data/lib/lapsoss/middleware/exception_filter.rb +43 -0
- data/lib/lapsoss/middleware/metrics_collector.rb +44 -0
- data/lib/lapsoss/middleware/rate_limiter.rb +31 -0
- data/lib/lapsoss/middleware/release_tracker.rb +117 -0
- data/lib/lapsoss/middleware/sample_filter.rb +23 -0
- data/lib/lapsoss/middleware/sampling_middleware.rb +18 -0
- data/lib/lapsoss/middleware/user_context_enhancer.rb +46 -0
- data/lib/lapsoss/middleware.rb +0 -347
- data/lib/lapsoss/pipeline.rb +1 -73
- data/lib/lapsoss/pipeline_builder.rb +69 -0
- data/lib/lapsoss/rails_error_subscriber.rb +42 -0
- data/lib/lapsoss/rails_middleware.rb +78 -0
- data/lib/lapsoss/railtie.rb +22 -50
- data/lib/lapsoss/registry.rb +34 -20
- data/lib/lapsoss/release_providers.rb +110 -0
- data/lib/lapsoss/release_tracker.rb +112 -207
- data/lib/lapsoss/router.rb +3 -5
- data/lib/lapsoss/sampling/adaptive_sampler.rb +46 -0
- data/lib/lapsoss/sampling/base.rb +11 -0
- data/lib/lapsoss/sampling/composite_sampler.rb +26 -0
- data/lib/lapsoss/sampling/consistent_hash_sampler.rb +30 -0
- data/lib/lapsoss/sampling/exception_type_sampler.rb +44 -0
- data/lib/lapsoss/sampling/health_based_sampler.rb +19 -0
- data/lib/lapsoss/sampling/rate_limiter.rb +32 -0
- data/lib/lapsoss/sampling/sampling_factory.rb +69 -0
- data/lib/lapsoss/sampling/time_based_sampler.rb +44 -0
- data/lib/lapsoss/sampling/uniform_sampler.rb +15 -0
- data/lib/lapsoss/sampling/user_based_sampler.rb +42 -0
- data/lib/lapsoss/sampling.rb +0 -326
- data/lib/lapsoss/scope.rb +17 -57
- data/lib/lapsoss/scrubber.rb +16 -18
- data/lib/lapsoss/user_context.rb +18 -198
- data/lib/lapsoss/user_context_integrations.rb +39 -0
- data/lib/lapsoss/user_context_middleware.rb +50 -0
- data/lib/lapsoss/user_context_provider.rb +93 -0
- data/lib/lapsoss/utils.rb +13 -0
- data/lib/lapsoss/validators.rb +14 -27
- data/lib/lapsoss/version.rb +1 -1
- data/lib/lapsoss.rb +12 -25
- metadata +106 -21
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "json"
|
4
4
|
require "socket"
|
5
|
-
require_relative "../http_client"
|
6
5
|
|
7
6
|
module Lapsoss
|
8
7
|
module Adapters
|
@@ -12,10 +11,10 @@ module Lapsoss
|
|
12
11
|
JSON_CONTENT_TYPE = "application/json; charset=UTF-8"
|
13
12
|
|
14
13
|
def initialize(name, settings = {})
|
15
|
-
super
|
16
|
-
@push_api_key = settings[:push_api_key] || ENV
|
17
|
-
@frontend_api_key = settings[:frontend_api_key] || ENV
|
18
|
-
@app_name = settings[:app_name] || ENV
|
14
|
+
super
|
15
|
+
@push_api_key = settings[:push_api_key] || ENV.fetch("APPSIGNAL_PUSH_API_KEY", nil)
|
16
|
+
@frontend_api_key = settings[:frontend_api_key] || ENV.fetch("APPSIGNAL_FRONTEND_API_KEY", nil)
|
17
|
+
@app_name = settings[:app_name] || ENV.fetch("APPSIGNAL_APP_NAME", nil)
|
19
18
|
@environment = Lapsoss.configuration.environment
|
20
19
|
|
21
20
|
validate_settings!
|
@@ -83,7 +82,7 @@ module Lapsoss
|
|
83
82
|
# Instead of creating fake exceptions, we'll structure the message properly
|
84
83
|
# but clearly indicate it's a log message, not an exception
|
85
84
|
|
86
|
-
unless [
|
85
|
+
unless %i[error fatal critical].include?(event.level)
|
87
86
|
# Log when messages are dropped due to level filtering
|
88
87
|
Lapsoss.configuration.logger&.debug(
|
89
88
|
"[Lapsoss::AppsignalAdapter] Dropping message with level '#{event.level}' - " \
|
@@ -98,9 +97,9 @@ module Lapsoss
|
|
98
97
|
exception: {
|
99
98
|
# AppSignal requires exception format for messages - this isn't a real exception
|
100
99
|
# but rather a way to send structured log messages through their error API
|
101
|
-
name: "LogMessage",
|
100
|
+
name: "LogMessage", # Clear indication this is a log message
|
102
101
|
message: event.message,
|
103
|
-
backtrace: []
|
102
|
+
backtrace: [] # No fake backtrace for log messages
|
104
103
|
},
|
105
104
|
tags: stringify_hash(event.context[:tags]),
|
106
105
|
params: stringify_hash(event.context[:params]),
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lapsoss
|
4
|
+
module Adapters
|
5
|
+
# Bugsnag adapter - backwards compatibility with InsightHub adapter
|
6
|
+
# This allows users to configure with :bugsnag type but uses InsightHub implementation
|
7
|
+
# The InsightHub adapter already checks for BUGSNAG_API_KEY environment variable
|
8
|
+
class BugsnagAdapter < InsightHubAdapter
|
9
|
+
# Inherits all functionality from InsightHubAdapter
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,30 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "json"
|
4
|
-
require "socket"
|
5
|
-
require_relative "../http_client"
|
6
|
-
require_relative "../backtrace_processor"
|
7
4
|
|
8
5
|
module Lapsoss
|
9
6
|
module Adapters
|
10
|
-
# Adapter for Insight Hub (formerly Bugsnag)
|
11
|
-
# Note: The API endpoints still use bugsnag.com domains for backwards compatibility
|
12
7
|
class InsightHubAdapter < Base
|
13
|
-
|
14
|
-
SESSION_URI = "https://sessions.bugsnag.com"
|
8
|
+
API_URI = "https://notify.bugsnag.com"
|
15
9
|
JSON_CONTENT_TYPE = "application/json"
|
16
10
|
|
17
11
|
def initialize(name, settings = {})
|
18
|
-
super
|
19
|
-
@api_key = settings[:api_key] || ENV
|
20
|
-
@release_stage = settings[:release_stage] || Lapsoss.configuration.environment
|
21
|
-
@app_version = settings[:app_version]
|
22
|
-
@app_type = settings[:app_type] || "ruby"
|
12
|
+
super
|
13
|
+
@api_key = settings[:api_key] || ENV.fetch("INSIGHT_HUB_API_KEY", nil)
|
23
14
|
|
24
15
|
validate_settings!
|
25
16
|
|
26
|
-
@
|
27
|
-
@session_client = create_http_client(SESSION_URI) if settings[:enable_sessions]
|
17
|
+
@client = create_http_client(API_URI)
|
28
18
|
@backtrace_processor = BacktraceProcessor.new
|
29
19
|
end
|
30
20
|
|
@@ -34,134 +24,113 @@ module Lapsoss
|
|
34
24
|
payload = build_payload(event)
|
35
25
|
return unless payload
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
}
|
43
|
-
|
44
|
-
begin
|
45
|
-
@notify_client.post("/", body: JSON.generate(payload), headers: headers)
|
46
|
-
rescue DeliveryError => e
|
47
|
-
# Log the error and potentially notify error handler
|
48
|
-
Lapsoss.configuration.logger&.error("[Lapsoss::InsightHubAdapter] Failed to deliver event: #{e.message}")
|
49
|
-
Lapsoss.configuration.error_handler&.call(e)
|
27
|
+
response = @client.post("/", body: payload.to_json, headers: {
|
28
|
+
"Content-Type" => JSON_CONTENT_TYPE,
|
29
|
+
"Bugsnag-Api-Key" => @api_key,
|
30
|
+
"Bugsnag-Payload-Version" => "5"
|
31
|
+
})
|
50
32
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def shutdown
|
57
|
-
@notify_client&.shutdown
|
58
|
-
@session_client&.shutdown
|
59
|
-
super
|
33
|
+
handle_response(response, event)
|
34
|
+
rescue StandardError => e
|
35
|
+
handle_delivery_error(e)
|
60
36
|
end
|
61
37
|
|
62
38
|
def capabilities
|
63
39
|
super.merge(
|
64
|
-
code_context: true,
|
65
40
|
breadcrumbs: true,
|
41
|
+
user_tracking: true,
|
42
|
+
custom_context: true,
|
43
|
+
release_tracking: true,
|
66
44
|
sessions: true
|
67
45
|
)
|
68
46
|
end
|
69
47
|
|
48
|
+
def validate!
|
49
|
+
validate_settings!
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
70
53
|
private
|
71
54
|
|
72
55
|
def build_payload(event)
|
73
56
|
{
|
74
57
|
apiKey: @api_key,
|
75
|
-
payloadVersion: "5
|
58
|
+
payloadVersion: "5",
|
76
59
|
notifier: {
|
77
|
-
name: "Lapsoss",
|
60
|
+
name: "Lapsoss Ruby",
|
78
61
|
version: Lapsoss::VERSION,
|
79
|
-
url: "https://github.com/
|
62
|
+
url: "https://github.com/yourusername/lapsoss"
|
80
63
|
},
|
81
|
-
events: [build_event(event)]
|
64
|
+
events: [ build_event(event) ]
|
82
65
|
}
|
83
66
|
end
|
84
67
|
|
85
68
|
def build_event(event)
|
86
|
-
|
87
|
-
app:
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
runtimeVersions: {
|
95
|
-
ruby: RUBY_VERSION
|
96
|
-
}
|
97
|
-
},
|
69
|
+
{
|
70
|
+
app: build_app_data(event),
|
71
|
+
device: build_device_data,
|
72
|
+
exceptions: build_exceptions(event),
|
73
|
+
breadcrumbs: build_breadcrumbs(event),
|
74
|
+
request: event.request_context,
|
75
|
+
user: build_user_data(event),
|
76
|
+
context: event.context[:custom]&.dig(:context) || "production",
|
98
77
|
severity: map_severity(event.level),
|
99
78
|
unhandled: event.context[:unhandled] || false,
|
100
|
-
|
101
|
-
|
102
|
-
},
|
103
|
-
user: build_user(event),
|
104
|
-
context: event.context[:context],
|
105
|
-
groupingHash: event.context[:fingerprint],
|
106
|
-
breadcrumbs: build_breadcrumbs(event),
|
107
|
-
metaData: event.context[:extra] || {}
|
108
|
-
}
|
109
|
-
|
110
|
-
case event.type
|
111
|
-
when :exception
|
112
|
-
base_event.merge(build_exception_data(event))
|
113
|
-
when :message
|
114
|
-
base_event.merge(build_message_data(event))
|
115
|
-
end
|
79
|
+
metaData: event.context[:custom] || {}
|
80
|
+
}.compact
|
116
81
|
end
|
117
82
|
|
118
|
-
def
|
119
|
-
|
120
|
-
exceptions: [{
|
121
|
-
errorClass: event.exception.class.name,
|
122
|
-
message: event.exception.message,
|
123
|
-
stacktrace: build_stacktrace(event.exception),
|
124
|
-
type: "ruby"
|
125
|
-
}]
|
126
|
-
}
|
127
|
-
end
|
83
|
+
def build_exceptions(event)
|
84
|
+
return [] unless event.type == :exception && event.exception
|
128
85
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
message: event.message,
|
136
|
-
stacktrace: [], # No fake backtrace for log messages
|
137
|
-
type: "log" # Mark as log type, not ruby exception
|
138
|
-
}]
|
139
|
-
}
|
86
|
+
[ {
|
87
|
+
errorClass: event.exception_type,
|
88
|
+
message: event.message,
|
89
|
+
stacktrace: build_stacktrace(event.exception),
|
90
|
+
type: "ruby"
|
91
|
+
} ]
|
140
92
|
end
|
141
93
|
|
142
94
|
def build_stacktrace(exception)
|
143
|
-
|
144
|
-
|
145
|
-
frames = @backtrace_processor.process_exception(exception)
|
95
|
+
frames = @backtrace_processor.process_exception(exception, follow_cause: true)
|
146
96
|
@backtrace_processor.format_frames(frames, :bugsnag)
|
147
97
|
end
|
148
98
|
|
99
|
+
def build_app_data(event)
|
100
|
+
{
|
101
|
+
id: event.context[:app]&.dig(:id),
|
102
|
+
version: event.context[:release]&.dig(:version),
|
103
|
+
releaseStage: @environment || "production",
|
104
|
+
type: detect_app_type
|
105
|
+
}.compact
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_device_data
|
109
|
+
{
|
110
|
+
hostname: Socket.gethostname,
|
111
|
+
osName: RUBY_PLATFORM,
|
112
|
+
runtimeVersions: {
|
113
|
+
ruby: RUBY_VERSION
|
114
|
+
}
|
115
|
+
}
|
116
|
+
end
|
149
117
|
|
150
118
|
def build_breadcrumbs(event)
|
151
119
|
breadcrumbs = event.context[:breadcrumbs] || []
|
120
|
+
|
152
121
|
breadcrumbs.map do |crumb|
|
153
122
|
{
|
154
|
-
timestamp: crumb[:timestamp]&.iso8601
|
155
|
-
name: crumb[:message]
|
123
|
+
timestamp: crumb[:timestamp]&.iso8601,
|
124
|
+
name: crumb[:message],
|
156
125
|
type: crumb[:type] || "manual",
|
157
126
|
metaData: crumb[:data] || {}
|
158
127
|
}
|
159
128
|
end
|
160
129
|
end
|
161
130
|
|
162
|
-
def
|
131
|
+
def build_user_data(event)
|
163
132
|
user = event.context[:user]
|
164
|
-
return unless user
|
133
|
+
return nil unless user
|
165
134
|
|
166
135
|
{
|
167
136
|
id: user[:id]&.to_s,
|
@@ -173,17 +142,49 @@ module Lapsoss
|
|
173
142
|
def map_severity(level)
|
174
143
|
case level
|
175
144
|
when :debug, :info then "info"
|
176
|
-
when :warning
|
177
|
-
when :error, :fatal
|
145
|
+
when :warning then "warning"
|
146
|
+
when :error, :fatal then "error"
|
178
147
|
else "error"
|
179
148
|
end
|
180
149
|
end
|
181
150
|
|
151
|
+
def detect_app_type
|
152
|
+
return "rails" if defined?(Rails)
|
153
|
+
return "rack" if defined?(Rack)
|
154
|
+
|
155
|
+
"ruby"
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_response(response, _event)
|
159
|
+
case response.status
|
160
|
+
when 200
|
161
|
+
true
|
162
|
+
when 400
|
163
|
+
body = begin
|
164
|
+
JSON.parse(response.body)
|
165
|
+
rescue
|
166
|
+
{}
|
167
|
+
end
|
168
|
+
raise DeliveryError.new("Bad request: #{body['errors']&.join(', ')}", response: response)
|
169
|
+
when 401
|
170
|
+
raise DeliveryError.new("Unauthorized: Invalid API key", response: response)
|
171
|
+
when 413
|
172
|
+
raise DeliveryError.new("Payload too large", response: response)
|
173
|
+
when 429
|
174
|
+
raise DeliveryError.new("Rate limit exceeded", response: response)
|
175
|
+
else
|
176
|
+
raise DeliveryError.new("Unexpected response: #{response.status}", response: response)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
182
180
|
def validate_settings!
|
183
181
|
validate_presence!(@api_key, "Insight Hub API key")
|
184
182
|
validate_api_key!(@api_key, "Insight Hub API key", format: :alphanumeric) if @api_key
|
185
|
-
|
186
|
-
|
183
|
+
end
|
184
|
+
|
185
|
+
def handle_delivery_error(error, response = nil)
|
186
|
+
message = "Insight Hub delivery failed: #{error.message}"
|
187
|
+
raise DeliveryError.new(message, response: response, cause: error)
|
187
188
|
end
|
188
189
|
end
|
189
190
|
end
|
@@ -7,7 +7,7 @@ module Lapsoss
|
|
7
7
|
class LoggerAdapter < Base
|
8
8
|
def initialize(name, settings = {})
|
9
9
|
@logger = settings[:logger] || Logger.new($stdout)
|
10
|
-
super
|
10
|
+
super
|
11
11
|
end
|
12
12
|
|
13
13
|
def capabilities
|
@@ -28,12 +28,12 @@ module Lapsoss
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# Log breadcrumbs if present in the event context
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
return unless event.context[:breadcrumbs]&.any?
|
32
|
+
|
33
|
+
event.context[:breadcrumbs].each do |breadcrumb|
|
34
|
+
breadcrumb_msg = "[BREADCRUMB] [#{breadcrumb[:type].upcase}] #{breadcrumb[:message]}"
|
35
|
+
breadcrumb_msg += " | #{breadcrumb[:metadata].inspect}" unless breadcrumb[:metadata].empty?
|
36
|
+
@logger.debug(breadcrumb_msg)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -3,8 +3,6 @@
|
|
3
3
|
require "json"
|
4
4
|
require "socket"
|
5
5
|
require "securerandom"
|
6
|
-
require_relative "../http_client"
|
7
|
-
require_relative "../backtrace_processor"
|
8
6
|
|
9
7
|
module Lapsoss
|
10
8
|
module Adapters
|
@@ -14,8 +12,8 @@ module Lapsoss
|
|
14
12
|
JSON_CONTENT_TYPE = "application/json"
|
15
13
|
|
16
14
|
def initialize(name, settings = {})
|
17
|
-
super
|
18
|
-
@access_token = settings[:access_token] || ENV
|
15
|
+
super
|
16
|
+
@access_token = settings[:access_token] || ENV.fetch("ROLLBAR_ACCESS_TOKEN", nil)
|
19
17
|
@environment = settings[:environment] || Lapsoss.configuration.environment || "development"
|
20
18
|
|
21
19
|
validate_settings!
|
@@ -30,62 +28,50 @@ module Lapsoss
|
|
30
28
|
payload = build_payload(event)
|
31
29
|
return unless payload
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
}
|
38
|
-
|
39
|
-
begin
|
40
|
-
@client.post(path, body: JSON.generate(payload), headers: headers)
|
41
|
-
rescue DeliveryError => e
|
42
|
-
# Log the error and potentially notify error handler
|
43
|
-
Lapsoss.configuration.logger&.error("[Lapsoss::RollbarAdapter] Failed to deliver event: #{e.message}")
|
44
|
-
Lapsoss.configuration.error_handler&.call(e)
|
45
|
-
|
46
|
-
# Re-raise to let the caller know delivery failed
|
47
|
-
raise
|
48
|
-
end
|
49
|
-
end
|
31
|
+
response = @client.post("/api/#{API_VERSION}/item/", body: payload.to_json, headers: {
|
32
|
+
"Content-Type" => JSON_CONTENT_TYPE,
|
33
|
+
"X-Rollbar-Access-Token" => @access_token
|
34
|
+
})
|
50
35
|
|
51
|
-
|
52
|
-
|
53
|
-
|
36
|
+
handle_response(response, event)
|
37
|
+
rescue StandardError => e
|
38
|
+
handle_delivery_error(e)
|
54
39
|
end
|
55
40
|
|
56
41
|
def capabilities
|
57
42
|
super.merge(
|
58
|
-
|
43
|
+
breadcrumbs: true,
|
44
|
+
user_tracking: true,
|
45
|
+
custom_context: true,
|
46
|
+
release_tracking: true
|
59
47
|
)
|
60
48
|
end
|
61
49
|
|
50
|
+
def validate!
|
51
|
+
validate_settings!
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
62
55
|
private
|
63
56
|
|
64
57
|
def build_payload(event)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
name: "lapsoss",
|
81
|
-
version: Lapsoss::VERSION
|
58
|
+
{
|
59
|
+
access_token: @access_token,
|
60
|
+
data: {
|
61
|
+
environment: @environment,
|
62
|
+
body: build_body(event),
|
63
|
+
level: map_level(event.level),
|
64
|
+
timestamp: event.timestamp.to_i,
|
65
|
+
code_version: event.context[:release]&.dig(:commit_sha),
|
66
|
+
platform: "ruby",
|
67
|
+
language: "ruby",
|
68
|
+
framework: detect_framework,
|
69
|
+
server: build_server_data,
|
70
|
+
person: build_person_data(event),
|
71
|
+
request: event.request_context,
|
72
|
+
custom: event.context[:custom] || {}
|
82
73
|
}
|
83
74
|
}
|
84
|
-
|
85
|
-
# Only add fingerprint if it exists
|
86
|
-
data[:fingerprint] = event.context[:fingerprint].to_s if event.context[:fingerprint]
|
87
|
-
|
88
|
-
{ data: data }
|
89
75
|
end
|
90
76
|
|
91
77
|
def build_body(event)
|
@@ -95,9 +81,9 @@ module Lapsoss
|
|
95
81
|
trace: {
|
96
82
|
frames: build_backtrace_frames(event.exception),
|
97
83
|
exception: {
|
98
|
-
class: event.
|
99
|
-
message: event.
|
100
|
-
description: event.exception.
|
84
|
+
class: event.exception_type,
|
85
|
+
message: event.message,
|
86
|
+
description: event.exception.to_s
|
101
87
|
}
|
102
88
|
}
|
103
89
|
}
|
@@ -119,9 +105,9 @@ module Lapsoss
|
|
119
105
|
formatted_frames.reverse
|
120
106
|
end
|
121
107
|
|
122
|
-
def
|
108
|
+
def build_person_data(event)
|
123
109
|
user = event.context[:user]
|
124
|
-
return unless user
|
110
|
+
return nil unless user
|
125
111
|
|
126
112
|
{
|
127
113
|
id: user[:id]&.to_s,
|
@@ -130,13 +116,22 @@ module Lapsoss
|
|
130
116
|
}.compact
|
131
117
|
end
|
132
118
|
|
119
|
+
def build_server_data
|
120
|
+
{
|
121
|
+
host: Socket.gethostname,
|
122
|
+
root: defined?(Rails) ? Rails.root.to_s : Dir.pwd,
|
123
|
+
branch: git_branch,
|
124
|
+
code_version: git_sha
|
125
|
+
}.compact
|
126
|
+
end
|
127
|
+
|
133
128
|
def map_level(level)
|
134
129
|
case level
|
135
130
|
when :debug then "debug"
|
136
131
|
when :info then "info"
|
137
|
-
when :warning
|
132
|
+
when :warning then "warning"
|
138
133
|
when :error then "error"
|
139
|
-
when :fatal
|
134
|
+
when :fatal then "critical"
|
140
135
|
else "error"
|
141
136
|
end
|
142
137
|
end
|
@@ -144,14 +139,58 @@ module Lapsoss
|
|
144
139
|
def detect_framework
|
145
140
|
return "rails" if defined?(Rails)
|
146
141
|
return "sinatra" if defined?(Sinatra)
|
142
|
+
|
147
143
|
"ruby"
|
148
144
|
end
|
149
145
|
|
146
|
+
def git_branch
|
147
|
+
`git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip.presence
|
148
|
+
rescue StandardError
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def git_sha
|
153
|
+
`git rev-parse HEAD 2>/dev/null`.strip.presence
|
154
|
+
rescue StandardError
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def handle_response(response, event)
|
159
|
+
case response.code.to_i
|
160
|
+
when 200
|
161
|
+
true
|
162
|
+
when 400
|
163
|
+
handle_client_error(response, event)
|
164
|
+
when 401
|
165
|
+
raise DeliveryError.new("Unauthorized: Invalid access token", response: response)
|
166
|
+
when 429
|
167
|
+
raise DeliveryError.new("Rate limit exceeded", response: response)
|
168
|
+
else
|
169
|
+
raise DeliveryError.new("Unexpected response: #{response.code}", response: response)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def handle_client_error(response, _event)
|
174
|
+
body = begin
|
175
|
+
JSON.parse(response.body)
|
176
|
+
rescue
|
177
|
+
{}
|
178
|
+
end
|
179
|
+
error_msg = body["message"] || "Bad request"
|
180
|
+
|
181
|
+
raise DeliveryError.new("Client error: #{error_msg}", response: response)
|
182
|
+
end
|
183
|
+
|
150
184
|
def validate_settings!
|
151
185
|
validate_presence!(@access_token, "Rollbar access token")
|
152
186
|
validate_api_key!(@access_token, "Rollbar access token", format: :alphanumeric) if @access_token
|
153
187
|
validate_environment!(@environment, "Rollbar environment") if @environment
|
154
188
|
end
|
189
|
+
|
190
|
+
def handle_delivery_error(error, response = nil)
|
191
|
+
message = "Rollbar delivery failed: #{error.message}"
|
192
|
+
raise DeliveryError.new(message, response: response, cause: error)
|
193
|
+
end
|
155
194
|
end
|
156
195
|
end
|
157
196
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
-
require_relative "../http_client"
|
5
|
-
require_relative "../backtrace_processor"
|
6
4
|
|
7
5
|
module Lapsoss
|
8
6
|
module Adapters
|
@@ -10,10 +8,10 @@ module Lapsoss
|
|
10
8
|
PROTOCOL_VERSION = 7
|
11
9
|
CONTENT_TYPE = "application/x-sentry-envelope"
|
12
10
|
GZIP_THRESHOLD = 1024 * 30 # 30KB
|
13
|
-
USER_AGENT = "lapsoss/#{Lapsoss::VERSION}"
|
11
|
+
USER_AGENT = "lapsoss/#{Lapsoss::VERSION}".freeze
|
14
12
|
|
15
13
|
def initialize(name, settings = {})
|
16
|
-
super
|
14
|
+
super
|
17
15
|
validate_settings!
|
18
16
|
return unless settings[:dsn]
|
19
17
|
|
@@ -69,7 +67,7 @@ module Lapsoss
|
|
69
67
|
item_header = { type: item_type, content_type: "application/json" }
|
70
68
|
item_payload = build_event_payload(event)
|
71
69
|
|
72
|
-
[header, item_header, item_payload]
|
70
|
+
[ header, item_header, item_payload ]
|
73
71
|
end
|
74
72
|
|
75
73
|
def serialize_envelope(envelope)
|
@@ -82,9 +80,9 @@ module Lapsoss
|
|
82
80
|
].join("\n")
|
83
81
|
|
84
82
|
if body.bytesize >= GZIP_THRESHOLD
|
85
|
-
[Zlib.gzip(body), true]
|
83
|
+
[ Zlib.gzip(body), true ]
|
86
84
|
else
|
87
|
-
[body, false]
|
85
|
+
[ body, false ]
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
@@ -107,11 +105,11 @@ module Lapsoss
|
|
107
105
|
when :exception
|
108
106
|
{
|
109
107
|
exception: {
|
110
|
-
values: [{
|
108
|
+
values: [ {
|
111
109
|
type: event.exception.class.name,
|
112
110
|
value: event.exception.message,
|
113
111
|
stacktrace: { frames: parse_backtrace(event.exception.backtrace) }
|
114
|
-
}]
|
112
|
+
} ]
|
115
113
|
}
|
116
114
|
}
|
117
115
|
when :message
|
@@ -151,9 +149,7 @@ module Lapsoss
|
|
151
149
|
api_path = uri.path
|
152
150
|
|
153
151
|
# For standard Sentry DSNs (just /project_id), build the envelope path
|
154
|
-
if path_parts.length == 1 && project_id.match?(/^\d+$/)
|
155
|
-
api_path = "/api/#{project_id}/envelope/"
|
156
|
-
end
|
152
|
+
api_path = "/api/#{project_id}/envelope/" if path_parts.length == 1 && project_id.match?(/^\d+$/)
|
157
153
|
|
158
154
|
{
|
159
155
|
public_key: uri.user,
|
@@ -186,11 +182,9 @@ module Lapsoss
|
|
186
182
|
end
|
187
183
|
|
188
184
|
def validate_settings!
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
raise ValidationError, "Sentry DSN is required"
|
193
|
-
end
|
185
|
+
raise ValidationError, "Sentry DSN is required" unless @settings[:dsn]
|
186
|
+
|
187
|
+
validate_dsn!(@settings[:dsn], "Sentry DSN")
|
194
188
|
end
|
195
189
|
end
|
196
190
|
end
|