lapsoss 0.4.6 → 0.4.9
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 +64 -13
- data/lib/lapsoss/adapters/sentry_adapter.rb +2 -1
- data/lib/lapsoss/adapters/telebugs_adapter.rb +42 -3
- data/lib/lapsoss/client.rb +37 -10
- data/lib/lapsoss/configuration.rb +7 -1
- data/lib/lapsoss/event.rb +4 -2
- data/lib/lapsoss/merged_scope.rb +22 -0
- data/lib/lapsoss/rails_controller_context.rb +36 -0
- data/lib/lapsoss/rails_controller_transaction.rb +33 -0
- data/lib/lapsoss/railtie.rb +13 -19
- data/lib/lapsoss/router.rb +6 -1
- data/lib/lapsoss/scope.rb +10 -0
- data/lib/lapsoss/version.rb +1 -1
- data/lib/lapsoss.rb +2 -0
- metadata +3 -2
- data/lib/lapsoss/rails_middleware.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e053057613e5678e160556abda63a224b056bb4b0b4b1d291282b2c1a8476ea
|
4
|
+
data.tar.gz: c6109d65cfb08dc095c45862565b6754a720e18d52f27814c0606a307964f328
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 944d8581a4dc544e14f2daf5094d9af5d59e5f387802212a2342f8b33f88790e218fe365df882cc6b56444edeb72addf1815ff4851c6eb9f5e50ecf388d22797
|
7
|
+
data.tar.gz: e4c712a550906d722d7a954d04560de2f4d6119a51d8c5271ca73cf4b7b8ca8a45ea238eddc3fb11366dbd35bc9f3dbc5cb3dc85544f4bbee8ccdd38d78858ec
|
data/README.md
CHANGED
@@ -23,10 +23,10 @@ Lapsoss.configure do |config|
|
|
23
23
|
# Monday: Using Bugsnag
|
24
24
|
config.use_bugsnag(api_key: ENV['BUGSNAG_KEY'])
|
25
25
|
|
26
|
-
# Tuesday: Add
|
27
|
-
config.
|
26
|
+
# Tuesday: Add Telebugs for comparison
|
27
|
+
config.use_telebugs(dsn: ENV['TELEBUGS_DSN'])
|
28
28
|
|
29
|
-
# Wednesday: Drop Bugsnag, keep
|
29
|
+
# Wednesday: Drop Bugsnag, keep Telebugs
|
30
30
|
# Just remove the line. Zero code changes.
|
31
31
|
end
|
32
32
|
```
|
@@ -64,7 +64,7 @@ That's it. No 500-line examples needed.
|
|
64
64
|
|
65
65
|
## Built for Rails, Not Around It
|
66
66
|
|
67
|
-
Lapsoss integrates with Rails' native error reporting API introduced in Rails 7. No monkey-patching, no
|
67
|
+
Lapsoss integrates with Rails' native error reporting API introduced in Rails 7. No monkey-patching, no global error handlers:
|
68
68
|
|
69
69
|
```ruby
|
70
70
|
# It just works with Rails.error:
|
@@ -74,6 +74,56 @@ end
|
|
74
74
|
# Automatically captured by whatever service you configured
|
75
75
|
```
|
76
76
|
|
77
|
+
### Rails Integration Options
|
78
|
+
|
79
|
+
**Option 1: Automatic Rails.error Integration (Recommended)**
|
80
|
+
```ruby
|
81
|
+
# config/initializers/lapsoss.rb
|
82
|
+
Lapsoss.configure do |config|
|
83
|
+
config.use_appsignal(push_api_key: ENV['APPSIGNAL_KEY'])
|
84
|
+
end
|
85
|
+
|
86
|
+
# That's it! All Rails errors are automatically captured
|
87
|
+
# Works with Rails.error.handle, Rails.error.record, Rails.error.report
|
88
|
+
# No code changes needed - just configure and go
|
89
|
+
```
|
90
|
+
|
91
|
+
**Option 2: Add Controller Context (Optional)**
|
92
|
+
```ruby
|
93
|
+
# app/controllers/application_controller.rb
|
94
|
+
class ApplicationController < ActionController::Base
|
95
|
+
include Lapsoss::RailsControllerContext
|
96
|
+
end
|
97
|
+
|
98
|
+
# Now all errors include controller/action context:
|
99
|
+
# { controller: "users", action: "show", controller_class: "UsersController" }
|
100
|
+
```
|
101
|
+
|
102
|
+
**Option 3: Manual Error Reporting (Your Choice)**
|
103
|
+
```ruby
|
104
|
+
# In controllers, jobs, or anywhere you want explicit control
|
105
|
+
begin
|
106
|
+
process_payment
|
107
|
+
rescue => e
|
108
|
+
Lapsoss.capture_exception(e, user_id: current_user.id)
|
109
|
+
# Handle gracefully...
|
110
|
+
end
|
111
|
+
|
112
|
+
# Or use Rails.error directly with your configured services
|
113
|
+
Rails.error.report(e, context: { user_id: current_user.id })
|
114
|
+
```
|
115
|
+
|
116
|
+
### No Global Patching Philosophy
|
117
|
+
|
118
|
+
Unlike other gems, Lapsoss **never** automatically captures all exceptions. You stay in control:
|
119
|
+
|
120
|
+
- ✅ **Rails.error integration only** - Uses Rails' official API
|
121
|
+
- ✅ **Explicit error handling** - You choose what to capture
|
122
|
+
- ✅ **No global hooks** - Your app behavior never changes
|
123
|
+
- ✅ **Optional controller context** - Include if you want it
|
124
|
+
|
125
|
+
This means your application behaves exactly the same with or without Lapsoss. No surprises, no changed behavior, no conflicts with other gems.
|
126
|
+
|
77
127
|
## Zero-Downtime Vendor Migration
|
78
128
|
|
79
129
|
```ruby
|
@@ -84,21 +134,22 @@ gem 'bugsnag' # Keep your existing gem for now
|
|
84
134
|
# Step 2: Configure dual reporting
|
85
135
|
Lapsoss.configure do |config|
|
86
136
|
config.use_bugsnag(api_key: ENV['BUGSNAG_KEY'])
|
87
|
-
config.
|
137
|
+
config.use_telebugs(dsn: ENV['TELEBUGS_DSN'])
|
88
138
|
end
|
89
139
|
|
90
140
|
# Step 3: Gradually replace Bugsnag calls
|
91
141
|
# Old: Bugsnag.notify(e)
|
92
142
|
# New: Lapsoss.capture_exception(e)
|
93
143
|
|
94
|
-
# Step 4: Remove bugsnag gem when ready
|
95
|
-
# Your app keeps running, now on
|
144
|
+
# Step 4: Remove bugsnag gem when ready
|
145
|
+
# Your app keeps running, now on Telebugs
|
96
146
|
```
|
97
147
|
|
98
148
|
## Why Not Just Use Vendor SDKs?
|
99
149
|
|
100
150
|
**Vendor SDKs monkey-patch your application:**
|
101
151
|
- Sentry patches Net::HTTP, Redis, and 20+ other gems
|
152
|
+
- Bugsnag patches ActionController, ActiveJob, and more
|
102
153
|
- Each vendor races to patch the same methods
|
103
154
|
- Multiple SDKs = multiple layers of patches
|
104
155
|
- Your app behavior changes based on load order
|
@@ -114,8 +165,8 @@ end
|
|
114
165
|
### GDPR Compliance
|
115
166
|
```ruby
|
116
167
|
# Route EU data to EU servers, US data to US servers
|
117
|
-
config.use_sentry(name: :us, dsn: ENV['
|
118
|
-
config.
|
168
|
+
config.use_sentry(name: :us, dsn: ENV['US_SENTRY_DSN'])
|
169
|
+
config.use_telebugs(name: :eu, dsn: ENV['EU_TELEBUGS_DSN'])
|
119
170
|
```
|
120
171
|
|
121
172
|
### A/B Testing Error Services
|
@@ -128,8 +179,8 @@ config.use_sentry(name: :candidate, dsn: ENV['SENTRY_DSN'])
|
|
128
179
|
### High Availability
|
129
180
|
```ruby
|
130
181
|
# Multiple providers for redundancy
|
131
|
-
config.
|
132
|
-
config.
|
182
|
+
config.use_telebugs(name: :primary, dsn: ENV['PRIMARY_DSN'])
|
183
|
+
config.use_appsignal(name: :backup, push_api_key: ENV['APPSIGNAL_KEY'])
|
133
184
|
```
|
134
185
|
|
135
186
|
## Yes, We Require ActiveSupport
|
@@ -172,7 +223,7 @@ All adapters are pure Ruby implementations with no external SDK dependencies:
|
|
172
223
|
```ruby
|
173
224
|
# config/initializers/lapsoss.rb
|
174
225
|
Lapsoss.configure do |config|
|
175
|
-
config.
|
226
|
+
config.use_telebugs(dsn: ENV["TELEBUGS_DSN"])
|
176
227
|
end
|
177
228
|
```
|
178
229
|
|
@@ -203,7 +254,7 @@ end
|
|
203
254
|
```ruby
|
204
255
|
Lapsoss.configure do |config|
|
205
256
|
# Adapter setup
|
206
|
-
config.
|
257
|
+
config.use_rollbar(access_token: ENV['ROLLBAR_TOKEN'])
|
207
258
|
|
208
259
|
# Data scrubbing (uses Rails filter_parameters automatically)
|
209
260
|
config.scrub_fields = %w[password credit_card ssn] # Or leave nil to use Rails defaults
|
@@ -110,8 +110,9 @@ module Lapsoss
|
|
110
110
|
user: event.user_context.presence,
|
111
111
|
extra: event.extra.presence,
|
112
112
|
breadcrumbs: format_breadcrumbs(event.breadcrumbs),
|
113
|
+
transaction: event.transaction,
|
113
114
|
sdk: {
|
114
|
-
name: "
|
115
|
+
name: "lapsoss.ruby",
|
115
116
|
version: Lapsoss::VERSION
|
116
117
|
}
|
117
118
|
}.compact_blank
|
@@ -8,20 +8,25 @@ module Lapsoss
|
|
8
8
|
# Telebugs is compatible with Sentry's API, so we inherit from SentryAdapter
|
9
9
|
class TelebugsAdapter < SentryAdapter
|
10
10
|
def initialize(name = :telebugs, settings = {})
|
11
|
+
debug_log "[TELEBUGS INIT] Initializing with settings: #{settings.inspect}"
|
11
12
|
super(name, settings)
|
13
|
+
debug_log "[TELEBUGS INIT] Initialization complete, enabled: #{@enabled}"
|
12
14
|
end
|
13
15
|
|
14
16
|
private
|
15
17
|
|
16
18
|
# Override to parse Telebugs DSN format
|
17
19
|
def parse_dsn(dsn_string)
|
20
|
+
debug_log "[TELEBUGS DSN] Parsing DSN: #{dsn_string}"
|
18
21
|
uri = URI.parse(dsn_string)
|
19
|
-
{
|
22
|
+
parsed = {
|
20
23
|
public_key: uri.user,
|
21
24
|
project_id: uri.path.split("/").last,
|
22
25
|
host: uri.host,
|
23
26
|
path: uri.path
|
24
27
|
}
|
28
|
+
debug_log "[TELEBUGS DSN] Parsed: #{parsed.inspect}"
|
29
|
+
parsed
|
25
30
|
end
|
26
31
|
|
27
32
|
# Override to build Telebugs-specific API path
|
@@ -42,8 +47,42 @@ module Lapsoss
|
|
42
47
|
uri = URI.parse(@settings[:dsn])
|
43
48
|
# For Telebug, we use the full URL without port (unless non-standard)
|
44
49
|
port = (uri.port == 443 || uri.port == 80) ? "" : ":#{uri.port}"
|
45
|
-
|
46
|
-
|
50
|
+
endpoint = "#{uri.scheme}://#{uri.host}#{port}"
|
51
|
+
api_path = build_api_path(uri)
|
52
|
+
|
53
|
+
debug_log "[TELEBUGS ENDPOINT] Setting endpoint: #{endpoint}"
|
54
|
+
debug_log "[TELEBUGS ENDPOINT] Setting API path: #{api_path}"
|
55
|
+
|
56
|
+
self.class.api_endpoint = endpoint
|
57
|
+
self.class.api_path = api_path
|
58
|
+
end
|
59
|
+
|
60
|
+
public
|
61
|
+
|
62
|
+
# Override capture to add debug logging
|
63
|
+
def capture(event)
|
64
|
+
debug_log "[TELEBUGS DEBUG] Capture called for event: #{event.type}"
|
65
|
+
debug_log "[TELEBUGS DEBUG] DSN configured: #{@dsn.inspect}"
|
66
|
+
debug_log "[TELEBUGS DEBUG] Endpoint: #{self.class.api_endpoint}"
|
67
|
+
debug_log "[TELEBUGS DEBUG] API Path: #{self.class.api_path}"
|
68
|
+
|
69
|
+
result = super(event)
|
70
|
+
debug_log "[TELEBUGS DEBUG] Event sent successfully, response: #{result.inspect}"
|
71
|
+
result
|
72
|
+
rescue => e
|
73
|
+
debug_log "[TELEBUGS ERROR] Failed to send: #{e.message}", :error
|
74
|
+
debug_log "[TELEBUGS ERROR] Backtrace: #{e.backtrace.first(5).join("\n")}", :error
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
|
78
|
+
def debug_log(message, level = :info)
|
79
|
+
return unless @debug
|
80
|
+
|
81
|
+
if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
82
|
+
Rails.logger.public_send(level, message)
|
83
|
+
elsif @logger
|
84
|
+
@logger.public_send(level, message)
|
85
|
+
end
|
47
86
|
end
|
48
87
|
|
49
88
|
# Override headers builder to add Telebugs-specific headers
|
data/lib/lapsoss/client.rb
CHANGED
@@ -6,32 +6,35 @@ module Lapsoss
|
|
6
6
|
class Client
|
7
7
|
def initialize(configuration)
|
8
8
|
@configuration = configuration
|
9
|
-
|
9
|
+
# Note: We're using Thread.new directly for async mode instead of a thread pool
|
10
|
+
# The Concurrent::FixedThreadPool had issues in Rails development mode
|
10
11
|
end
|
11
12
|
|
12
13
|
def capture_exception(exception, **context)
|
13
|
-
return unless @configuration.enabled
|
14
|
+
return nil unless @configuration.enabled
|
14
15
|
|
15
16
|
with_scope(context) do |scope|
|
16
17
|
event = Event.build(
|
17
18
|
type: :exception,
|
18
19
|
level: :error,
|
19
20
|
exception: exception,
|
20
|
-
context: scope_to_context(scope)
|
21
|
+
context: scope_to_context(scope),
|
22
|
+
transaction: scope.transaction_name
|
21
23
|
)
|
22
24
|
capture_event(event)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
def capture_message(message, level: :info, **context)
|
27
|
-
return unless @configuration.enabled
|
29
|
+
return nil unless @configuration.enabled
|
28
30
|
|
29
31
|
with_scope(context) do |scope|
|
30
32
|
event = Event.build(
|
31
33
|
type: :message,
|
32
34
|
level: level,
|
33
35
|
message: message,
|
34
|
-
context: scope_to_context(scope)
|
36
|
+
context: scope_to_context(scope),
|
37
|
+
transaction: scope.transaction_name
|
35
38
|
)
|
36
39
|
capture_event(event)
|
37
40
|
end
|
@@ -58,30 +61,54 @@ module Lapsoss
|
|
58
61
|
end
|
59
62
|
|
60
63
|
def flush(timeout: 2)
|
61
|
-
|
64
|
+
@configuration.logger.debug("[LAPSOSS] Flush called with timeout: #{timeout}")
|
65
|
+
# Give threads a moment to complete
|
66
|
+
sleep(0.5) if @configuration.async
|
67
|
+
|
68
|
+
# Flush individual adapters if they support it
|
69
|
+
Registry.instance.active.each do |adapter|
|
70
|
+
adapter.flush(timeout: timeout) if adapter.respond_to?(:flush)
|
71
|
+
end
|
62
72
|
end
|
63
73
|
|
64
74
|
def shutdown
|
65
|
-
@executor&.shutdown
|
66
75
|
Registry.instance.shutdown
|
67
76
|
end
|
68
77
|
|
69
78
|
private
|
70
79
|
|
71
80
|
def capture_event(event)
|
81
|
+
@configuration.logger.debug("[LAPSOSS] capture_event called, async: #{@configuration.async}, executor: #{@executor.inspect}")
|
82
|
+
|
72
83
|
# Apply pipeline processing if enabled
|
73
84
|
if @configuration.enable_pipeline && @configuration.pipeline
|
74
85
|
event = @configuration.pipeline.call(event)
|
75
|
-
return unless event
|
86
|
+
return nil unless event
|
76
87
|
end
|
77
88
|
|
78
89
|
event = run_before_send(event)
|
79
|
-
return unless event
|
90
|
+
return nil unless event
|
80
91
|
|
81
92
|
if @configuration.async
|
82
|
-
@
|
93
|
+
@configuration.logger.debug("[LAPSOSS ASYNC] About to process event asynchronously")
|
94
|
+
|
95
|
+
# Use Thread.new for now - the executor pool seems to have issues in Rails dev mode
|
96
|
+
thread = Thread.new do
|
97
|
+
begin
|
98
|
+
@configuration.logger.debug("[LAPSOSS ASYNC] Background thread started")
|
99
|
+
Router.process_event(event)
|
100
|
+
@configuration.logger.debug("[LAPSOSS ASYNC] Background thread completed")
|
101
|
+
rescue => e
|
102
|
+
@configuration.logger.error("[LAPSOSS ASYNC ERROR] Failed in background: #{e.message}")
|
103
|
+
@configuration.logger.error(e.backtrace.join("\n")) if @configuration.debug
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
thread
|
83
108
|
else
|
109
|
+
@configuration.logger.debug("[LAPSOSS SYNC] Processing event synchronously")
|
84
110
|
Router.process_event(event)
|
111
|
+
nil
|
85
112
|
end
|
86
113
|
rescue StandardError => e
|
87
114
|
handle_capture_error(e)
|
@@ -59,6 +59,7 @@ module Lapsoss
|
|
59
59
|
# Pipeline settings
|
60
60
|
@enable_pipeline = true
|
61
61
|
@pipeline_builder = nil
|
62
|
+
@pipeline = nil
|
62
63
|
@sampling_strategy = nil
|
63
64
|
# Rails error filtering
|
64
65
|
@skip_rails_cache_errors = true
|
@@ -185,7 +186,12 @@ module Lapsoss
|
|
185
186
|
end
|
186
187
|
|
187
188
|
def pipeline
|
188
|
-
@pipeline_builder&.pipeline
|
189
|
+
@pipeline || @pipeline_builder&.pipeline
|
190
|
+
end
|
191
|
+
|
192
|
+
def pipeline=(value)
|
193
|
+
validate_callable!(value, "pipeline") if value
|
194
|
+
@pipeline = value
|
189
195
|
end
|
190
196
|
|
191
197
|
# Sampling configuration
|
data/lib/lapsoss/event.rb
CHANGED
@@ -16,7 +16,8 @@ module Lapsoss
|
|
16
16
|
:context,
|
17
17
|
:environment,
|
18
18
|
:fingerprint,
|
19
|
-
:backtrace_frames
|
19
|
+
:backtrace_frames,
|
20
|
+
:transaction # Controller#action or task name where event occurred
|
20
21
|
) do
|
21
22
|
# Factory method with smart defaults
|
22
23
|
def self.build(type:, level: :info, **attributes)
|
@@ -43,7 +44,8 @@ module Lapsoss
|
|
43
44
|
context: context,
|
44
45
|
environment: environment,
|
45
46
|
fingerprint: fingerprint,
|
46
|
-
backtrace_frames: backtrace_frames
|
47
|
+
backtrace_frames: backtrace_frames,
|
48
|
+
transaction: attributes[:transaction]
|
47
49
|
)
|
48
50
|
end
|
49
51
|
|
data/lib/lapsoss/merged_scope.rb
CHANGED
@@ -25,6 +25,24 @@ module Lapsoss
|
|
25
25
|
@breadcrumbs ||= merge_breadcrumbs
|
26
26
|
end
|
27
27
|
|
28
|
+
def transaction_name
|
29
|
+
# Check scope stack first (most recent wins)
|
30
|
+
@scope_stack.reverse_each do |context|
|
31
|
+
return context[:transaction_name] if context[:transaction_name]
|
32
|
+
end
|
33
|
+
# Fall back to base scope
|
34
|
+
@base_scope.transaction_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def transaction_source
|
38
|
+
# Check scope stack first (most recent wins)
|
39
|
+
@scope_stack.reverse_each do |context|
|
40
|
+
return context[:transaction_source] if context[:transaction_source]
|
41
|
+
end
|
42
|
+
# Fall back to base scope
|
43
|
+
@base_scope.transaction_source
|
44
|
+
end
|
45
|
+
|
28
46
|
def add_breadcrumb(message, type: :default, **metadata)
|
29
47
|
breadcrumb = Breadcrumb.build(message, type: type, metadata: metadata)
|
30
48
|
@own_breadcrumbs << breadcrumb
|
@@ -34,6 +52,10 @@ module Lapsoss
|
|
34
52
|
@breadcrumbs = nil
|
35
53
|
end
|
36
54
|
|
55
|
+
def set_transaction_name(name, source: nil)
|
56
|
+
@base_scope.set_transaction_name(name, source: source)
|
57
|
+
end
|
58
|
+
|
37
59
|
private
|
38
60
|
|
39
61
|
def merge_hash_contexts(key)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lapsoss
|
4
|
+
# Optional concern to add controller and action context to Rails.error
|
5
|
+
# Include this in ApplicationController or specific controllers to get more detailed context
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# class ApplicationController < ActionController::Base
|
9
|
+
# include Lapsoss::RailsControllerContext
|
10
|
+
# end
|
11
|
+
module RailsControllerContext
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
prepend_before_action :set_lapsoss_controller_context
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_lapsoss_controller_context
|
21
|
+
# Set context in Lapsoss scope if available
|
22
|
+
Lapsoss::Current.scope&.set_context("controller", {
|
23
|
+
controller: controller_name,
|
24
|
+
action: action_name,
|
25
|
+
controller_class: self.class.name
|
26
|
+
})
|
27
|
+
|
28
|
+
# Set context in Rails.error for ecosystem-wide availability
|
29
|
+
Rails.error.set_context(
|
30
|
+
controller: controller_name,
|
31
|
+
action: action_name,
|
32
|
+
controller_class: self.class.name
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lapsoss
|
4
|
+
module RailsControllerTransaction
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
around_action :lapsoss_capture_transaction
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def lapsoss_capture_transaction
|
14
|
+
if Lapsoss.client
|
15
|
+
transaction_name = "#{self.class.name}##{action_name}"
|
16
|
+
|
17
|
+
# Set the transaction name in the current scope
|
18
|
+
Lapsoss::Current.scope.set_transaction_name(transaction_name, source: :view)
|
19
|
+
|
20
|
+
# Add breadcrumb for the action
|
21
|
+
Lapsoss::Current.scope.add_breadcrumb(
|
22
|
+
"Processing #{transaction_name}",
|
23
|
+
type: :navigation,
|
24
|
+
controller: self.class.name,
|
25
|
+
action: action_name,
|
26
|
+
params: request.filtered_parameters
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/lapsoss/railtie.rb
CHANGED
@@ -2,13 +2,7 @@
|
|
2
2
|
|
3
3
|
module Lapsoss
|
4
4
|
class Railtie < Rails::Railtie
|
5
|
-
|
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
|
5
|
+
# Debug logging removed - will be handled by the configured logger
|
12
6
|
config.lapsoss = ActiveSupport::OrderedOptions.new
|
13
7
|
|
14
8
|
initializer "lapsoss.configure" do |_app|
|
@@ -20,12 +14,11 @@ module Lapsoss
|
|
20
14
|
Rails.env
|
21
15
|
end
|
22
16
|
|
23
|
-
# Use
|
24
|
-
config.logger ||=
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
17
|
+
# Use Rails logger if available
|
18
|
+
config.logger ||= Rails.logger
|
19
|
+
|
20
|
+
# Set debug level in development
|
21
|
+
config.debug = Rails.env.development?
|
29
22
|
|
30
23
|
config.release ||= if Rails.application.respond_to?(:version)
|
31
24
|
Rails.application.version.to_s
|
@@ -41,15 +34,16 @@ module Lapsoss
|
|
41
34
|
end
|
42
35
|
end
|
43
36
|
|
44
|
-
initializer "lapsoss.add_middleware" do |app|
|
45
|
-
require "lapsoss/rails_middleware"
|
46
37
|
|
47
|
-
|
48
|
-
|
38
|
+
initializer "lapsoss.rails_error_subscriber" do |app|
|
39
|
+
Rails.error.subscribe(Lapsoss::RailsErrorSubscriber.new)
|
49
40
|
end
|
50
41
|
|
51
|
-
initializer "lapsoss.
|
52
|
-
|
42
|
+
initializer "lapsoss.controller_transaction" do
|
43
|
+
ActiveSupport.on_load(:action_controller) do
|
44
|
+
require "lapsoss/rails_controller_transaction"
|
45
|
+
include Lapsoss::RailsControllerTransaction
|
46
|
+
end
|
53
47
|
end
|
54
48
|
end
|
55
49
|
end
|
data/lib/lapsoss/router.rb
CHANGED
@@ -8,8 +8,13 @@ module Lapsoss
|
|
8
8
|
#
|
9
9
|
# @param event [Lapsoss::Event] The event to process.
|
10
10
|
def process_event(event)
|
11
|
-
Registry.instance.active
|
11
|
+
adapters = Registry.instance.active
|
12
|
+
Lapsoss.configuration.logger.debug("[LAPSOSS ROUTER] Processing event to #{adapters.length} adapters: #{adapters.map(&:name).join(', ')}")
|
13
|
+
|
14
|
+
adapters.each do |adapter|
|
15
|
+
Lapsoss.configuration.logger.info("[LAPSOSS ROUTER] About to call #{adapter.name}.capture")
|
12
16
|
adapter.capture(event)
|
17
|
+
Lapsoss.configuration.logger.info("[LAPSOSS ROUTER] Adapter #{adapter.name} completed")
|
13
18
|
rescue StandardError => e
|
14
19
|
handle_adapter_error(adapter, event, e)
|
15
20
|
end
|
data/lib/lapsoss/scope.rb
CHANGED
@@ -3,12 +3,15 @@
|
|
3
3
|
module Lapsoss
|
4
4
|
class Scope
|
5
5
|
attr_reader :breadcrumbs, :tags, :user, :extra
|
6
|
+
attr_accessor :transaction_name, :transaction_source
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@breadcrumbs = []
|
9
10
|
@tags = {}
|
10
11
|
@user = {}
|
11
12
|
@extra = {}
|
13
|
+
@transaction_name = nil
|
14
|
+
@transaction_source = nil
|
12
15
|
end
|
13
16
|
|
14
17
|
def add_breadcrumb(message, type: :default, **metadata)
|
@@ -36,6 +39,8 @@ module Lapsoss
|
|
36
39
|
@tags.clear
|
37
40
|
@user.clear
|
38
41
|
@extra.clear
|
42
|
+
@transaction_name = nil
|
43
|
+
@transaction_source = nil
|
39
44
|
end
|
40
45
|
|
41
46
|
def set_context(key, value)
|
@@ -61,5 +66,10 @@ module Lapsoss
|
|
61
66
|
def set_extras(extras)
|
62
67
|
@extra.merge!(extras)
|
63
68
|
end
|
69
|
+
|
70
|
+
def set_transaction_name(name, source: nil)
|
71
|
+
@transaction_name = name
|
72
|
+
@transaction_source = source if source
|
73
|
+
end
|
64
74
|
end
|
65
75
|
end
|
data/lib/lapsoss/version.rb
CHANGED
data/lib/lapsoss.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lapsoss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdelkader Boudih
|
@@ -249,8 +249,9 @@ files:
|
|
249
249
|
- lib/lapsoss/middleware/release_tracker.rb
|
250
250
|
- lib/lapsoss/pipeline.rb
|
251
251
|
- lib/lapsoss/pipeline_builder.rb
|
252
|
+
- lib/lapsoss/rails_controller_context.rb
|
253
|
+
- lib/lapsoss/rails_controller_transaction.rb
|
252
254
|
- lib/lapsoss/rails_error_subscriber.rb
|
253
|
-
- lib/lapsoss/rails_middleware.rb
|
254
255
|
- lib/lapsoss/railtie.rb
|
255
256
|
- lib/lapsoss/registry.rb
|
256
257
|
- lib/lapsoss/release_tracker.rb
|
@@ -1,78 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Lapsoss
|
4
|
-
class RailsMiddleware
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
Lapsoss::Current.with_clean_scope do
|
11
|
-
# Add request context to current scope
|
12
|
-
if Lapsoss.configuration.capture_request_context
|
13
|
-
Rails.logger.tagged("Lapsoss") { Rails.logger.debug "Adding request context" } if Rails.env.test?
|
14
|
-
add_request_context(env)
|
15
|
-
end
|
16
|
-
|
17
|
-
begin
|
18
|
-
@app.call(env)
|
19
|
-
rescue Exception => e
|
20
|
-
Rails.logger.tagged("Lapsoss") { Rails.logger.debug "Capturing exception: #{e.class} - #{e.message}" } if Rails.env.test?
|
21
|
-
# Capture the exception
|
22
|
-
Lapsoss.capture_exception(e)
|
23
|
-
# Re-raise the exception to maintain Rails error handling
|
24
|
-
raise
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def add_request_context(env)
|
32
|
-
request = Rack::Request.new(env)
|
33
|
-
|
34
|
-
return unless Lapsoss::Current.scope
|
35
|
-
|
36
|
-
Lapsoss::Current.scope.set_context("request", {
|
37
|
-
method: request.request_method,
|
38
|
-
url: request.url,
|
39
|
-
path: request.path,
|
40
|
-
query_string: request.query_string,
|
41
|
-
headers: extract_headers(env),
|
42
|
-
ip: request.ip,
|
43
|
-
user_agent: request.user_agent,
|
44
|
-
referer: request.referer,
|
45
|
-
request_id: env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
|
46
|
-
})
|
47
|
-
|
48
|
-
# Add user context if available
|
49
|
-
return unless env["warden"]&.user
|
50
|
-
|
51
|
-
user = env["warden"].user
|
52
|
-
Lapsoss::Current.scope.set_user(
|
53
|
-
id: user.id,
|
54
|
-
email: user.respond_to?(:email) ? user.email : nil
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
def extract_headers(env)
|
59
|
-
headers = {}
|
60
|
-
|
61
|
-
env.each do |key, value|
|
62
|
-
if key.start_with?("HTTP_") && FILTERED_HEADERS.exclude?(key)
|
63
|
-
header_name = key.sub(/^HTTP_/, "").split("_").map(&:capitalize).join("-")
|
64
|
-
headers[header_name] = value
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
headers
|
69
|
-
end
|
70
|
-
|
71
|
-
FILTERED_HEADERS = %w[
|
72
|
-
HTTP_AUTHORIZATION
|
73
|
-
HTTP_COOKIE
|
74
|
-
HTTP_X_API_KEY
|
75
|
-
HTTP_X_AUTH_TOKEN
|
76
|
-
].freeze
|
77
|
-
end
|
78
|
-
end
|