sentry-rails 5.18.2 → 5.28.1
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/Gemfile +43 -26
- data/Rakefile +13 -10
- data/app/jobs/sentry/send_event_job.rb +2 -0
- data/bin/console +1 -0
- data/bin/test +389 -0
- data/lib/generators/sentry_generator.rb +13 -1
- data/lib/sentry/rails/action_cable.rb +3 -1
- data/lib/sentry/rails/active_job.rb +65 -10
- data/lib/sentry/rails/background_worker.rb +13 -7
- data/lib/sentry/rails/backtrace_cleaner.rb +7 -7
- data/lib/sentry/rails/breadcrumb/active_support_logger.rb +2 -0
- data/lib/sentry/rails/breadcrumb/monotonic_active_support_logger.rb +2 -0
- data/lib/sentry/rails/capture_exceptions.rb +4 -2
- data/lib/sentry/rails/configuration.rb +64 -18
- data/lib/sentry/rails/controller_methods.rb +2 -0
- data/lib/sentry/rails/controller_transaction.rb +7 -3
- data/lib/sentry/rails/engine.rb +2 -0
- data/lib/sentry/rails/error_subscriber.rb +2 -0
- data/lib/sentry/rails/instrument_payload_cleanup_helper.rb +2 -0
- data/lib/sentry/rails/log_subscriber.rb +73 -0
- data/lib/sentry/rails/log_subscribers/action_controller_subscriber.rb +116 -0
- data/lib/sentry/rails/log_subscribers/action_mailer_subscriber.rb +88 -0
- data/lib/sentry/rails/log_subscribers/active_job_subscriber.rb +155 -0
- data/lib/sentry/rails/log_subscribers/active_record_subscriber.rb +134 -0
- data/lib/sentry/rails/log_subscribers/parameter_filter.rb +52 -0
- data/lib/sentry/rails/overrides/streaming_reporter.rb +2 -0
- data/lib/sentry/rails/railtie.rb +16 -2
- data/lib/sentry/rails/rescued_exception_interceptor.rb +12 -1
- data/lib/sentry/rails/structured_logging.rb +32 -0
- data/lib/sentry/rails/tracing/abstract_subscriber.rb +2 -0
- data/lib/sentry/rails/tracing/action_controller_subscriber.rb +5 -3
- data/lib/sentry/rails/tracing/action_view_subscriber.rb +4 -2
- data/lib/sentry/rails/tracing/active_record_subscriber.rb +1 -0
- data/lib/sentry/rails/tracing/active_storage_subscriber.rb +8 -3
- data/lib/sentry/rails/tracing/active_support_subscriber.rb +63 -0
- data/lib/sentry/rails/tracing.rb +2 -0
- data/lib/sentry/rails/version.rb +3 -1
- data/lib/sentry/rails.rb +3 -0
- data/lib/sentry-rails.rb +2 -0
- data/sentry-rails.gemspec +4 -2
- metadata +20 -14
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/rails/log_subscriber"
|
|
4
|
+
require "sentry/rails/log_subscribers/parameter_filter"
|
|
5
|
+
|
|
6
|
+
module Sentry
|
|
7
|
+
module Rails
|
|
8
|
+
module LogSubscribers
|
|
9
|
+
# LogSubscriber for ActiveJob events that captures background job execution
|
|
10
|
+
# and logs them using Sentry's structured logging system.
|
|
11
|
+
#
|
|
12
|
+
# This subscriber captures various ActiveJob events including job execution,
|
|
13
|
+
# enqueueing, retries, and failures with relevant job information.
|
|
14
|
+
#
|
|
15
|
+
# @example Usage
|
|
16
|
+
# # Enable structured logging for ActiveJob
|
|
17
|
+
# Sentry.init do |config|
|
|
18
|
+
# config.enable_logs = true
|
|
19
|
+
# config.rails.structured_logging = true
|
|
20
|
+
# config.rails.structured_logging.subscribers = { active_job: Sentry::Rails::LogSubscribers::ActiveJobSubscriber }
|
|
21
|
+
# end
|
|
22
|
+
class ActiveJobSubscriber < Sentry::Rails::LogSubscriber
|
|
23
|
+
include ParameterFilter
|
|
24
|
+
|
|
25
|
+
# Handle perform.active_job events
|
|
26
|
+
#
|
|
27
|
+
# @param event [ActiveSupport::Notifications::Event] The job performance event
|
|
28
|
+
def perform(event)
|
|
29
|
+
job = event.payload[:job]
|
|
30
|
+
duration = duration_ms(event)
|
|
31
|
+
|
|
32
|
+
attributes = {
|
|
33
|
+
job_class: job.class.name,
|
|
34
|
+
job_id: job.job_id,
|
|
35
|
+
queue_name: job.queue_name,
|
|
36
|
+
duration_ms: duration,
|
|
37
|
+
executions: job.executions,
|
|
38
|
+
priority: job.priority
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
attributes[:adapter] = job.class.queue_adapter.class.name
|
|
42
|
+
|
|
43
|
+
if job.scheduled_at
|
|
44
|
+
attributes[:scheduled_at] = job.scheduled_at.iso8601
|
|
45
|
+
attributes[:delay_ms] = ((Time.current - job.scheduled_at) * 1000).round(2)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if Sentry.configuration.send_default_pii && job.arguments.present?
|
|
49
|
+
filtered_args = filter_sensitive_arguments(job.arguments)
|
|
50
|
+
attributes[:arguments] = filtered_args unless filtered_args.empty?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
message = "Job performed: #{job.class.name}"
|
|
54
|
+
|
|
55
|
+
log_structured_event(
|
|
56
|
+
message: message,
|
|
57
|
+
level: :info,
|
|
58
|
+
attributes: attributes
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Handle enqueue.active_job events
|
|
63
|
+
#
|
|
64
|
+
# @param event [ActiveSupport::Notifications::Event] The job enqueue event
|
|
65
|
+
def enqueue(event)
|
|
66
|
+
job = event.payload[:job]
|
|
67
|
+
|
|
68
|
+
attributes = {
|
|
69
|
+
job_class: job.class.name,
|
|
70
|
+
job_id: job.job_id,
|
|
71
|
+
queue_name: job.queue_name,
|
|
72
|
+
priority: job.priority
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
attributes[:adapter] = job.class.queue_adapter.class.name if job.class.respond_to?(:queue_adapter)
|
|
76
|
+
|
|
77
|
+
if job.scheduled_at
|
|
78
|
+
attributes[:scheduled_at] = job.scheduled_at.iso8601
|
|
79
|
+
attributes[:delay_seconds] = (job.scheduled_at - Time.current).round(2)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
message = "Job enqueued: #{job.class.name}"
|
|
83
|
+
|
|
84
|
+
log_structured_event(
|
|
85
|
+
message: message,
|
|
86
|
+
level: :info,
|
|
87
|
+
attributes: attributes
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def retry_stopped(event)
|
|
92
|
+
job = event.payload[:job]
|
|
93
|
+
error = event.payload[:error]
|
|
94
|
+
|
|
95
|
+
attributes = {
|
|
96
|
+
job_class: job.class.name,
|
|
97
|
+
job_id: job.job_id,
|
|
98
|
+
queue_name: job.queue_name,
|
|
99
|
+
executions: job.executions,
|
|
100
|
+
error_class: error.class.name,
|
|
101
|
+
error_message: error.message
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
message = "Job retry stopped: #{job.class.name}"
|
|
105
|
+
|
|
106
|
+
log_structured_event(
|
|
107
|
+
message: message,
|
|
108
|
+
level: :error,
|
|
109
|
+
attributes: attributes
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def discard(event)
|
|
114
|
+
job = event.payload[:job]
|
|
115
|
+
error = event.payload[:error]
|
|
116
|
+
|
|
117
|
+
attributes = {
|
|
118
|
+
job_class: job.class.name,
|
|
119
|
+
job_id: job.job_id,
|
|
120
|
+
queue_name: job.queue_name,
|
|
121
|
+
executions: job.executions
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
attributes[:error_class] = error.class.name if error
|
|
125
|
+
attributes[:error_message] = error.message if error
|
|
126
|
+
|
|
127
|
+
message = "Job discarded: #{job.class.name}"
|
|
128
|
+
|
|
129
|
+
log_structured_event(
|
|
130
|
+
message: message,
|
|
131
|
+
level: :warn,
|
|
132
|
+
attributes: attributes
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def filter_sensitive_arguments(arguments)
|
|
139
|
+
return [] unless arguments.is_a?(Array)
|
|
140
|
+
|
|
141
|
+
arguments.map do |arg|
|
|
142
|
+
case arg
|
|
143
|
+
when Hash
|
|
144
|
+
filter_sensitive_params(arg)
|
|
145
|
+
when String
|
|
146
|
+
arg.length > 100 ? "[FILTERED: #{arg.length} chars]" : arg
|
|
147
|
+
else
|
|
148
|
+
arg
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/rails/log_subscriber"
|
|
4
|
+
require "sentry/rails/log_subscribers/parameter_filter"
|
|
5
|
+
|
|
6
|
+
module Sentry
|
|
7
|
+
module Rails
|
|
8
|
+
module LogSubscribers
|
|
9
|
+
# LogSubscriber for ActiveRecord events that captures database queries
|
|
10
|
+
# and logs them using Sentry's structured logging system.
|
|
11
|
+
#
|
|
12
|
+
# This subscriber captures sql.active_record events and formats them
|
|
13
|
+
# with relevant database information including SQL queries, duration,
|
|
14
|
+
# database configuration, and caching information.
|
|
15
|
+
#
|
|
16
|
+
# @example Usage
|
|
17
|
+
# # Automatically attached when structured logging is enabled for :active_record
|
|
18
|
+
# Sentry.init do |config|
|
|
19
|
+
# config.enable_logs = true
|
|
20
|
+
# config.rails.structured_logging = true
|
|
21
|
+
# config.rails.structured_logging.subscribers = { active_record: Sentry::Rails::LogSubscribers::ActiveRecordSubscriber }
|
|
22
|
+
# end
|
|
23
|
+
class ActiveRecordSubscriber < Sentry::Rails::LogSubscriber
|
|
24
|
+
include ParameterFilter
|
|
25
|
+
|
|
26
|
+
EXCLUDED_NAMES = ["SCHEMA", "TRANSACTION"].freeze
|
|
27
|
+
|
|
28
|
+
# Handle sql.active_record events
|
|
29
|
+
#
|
|
30
|
+
# @param event [ActiveSupport::Notifications::Event] The SQL event
|
|
31
|
+
def sql(event)
|
|
32
|
+
return if EXCLUDED_NAMES.include?(event.payload[:name])
|
|
33
|
+
|
|
34
|
+
sql = event.payload[:sql]
|
|
35
|
+
statement_name = event.payload[:name]
|
|
36
|
+
|
|
37
|
+
# Rails 5.0.0 doesn't include :cached in the payload, it was added in Rails 5.1
|
|
38
|
+
cached = event.payload.fetch(:cached, false)
|
|
39
|
+
connection_id = event.payload[:connection_id]
|
|
40
|
+
|
|
41
|
+
db_config = extract_db_config(event.payload)
|
|
42
|
+
|
|
43
|
+
attributes = {
|
|
44
|
+
sql: sql,
|
|
45
|
+
duration_ms: duration_ms(event),
|
|
46
|
+
cached: cached
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
attributes[:statement_name] = statement_name if statement_name && statement_name != "SQL"
|
|
50
|
+
attributes[:connection_id] = connection_id if connection_id
|
|
51
|
+
|
|
52
|
+
add_db_config_attributes(attributes, db_config)
|
|
53
|
+
|
|
54
|
+
message = build_log_message(statement_name)
|
|
55
|
+
|
|
56
|
+
log_structured_event(
|
|
57
|
+
message: message,
|
|
58
|
+
level: :info,
|
|
59
|
+
attributes: attributes
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def build_log_message(statement_name)
|
|
66
|
+
if statement_name && statement_name != "SQL"
|
|
67
|
+
"Database query: #{statement_name}"
|
|
68
|
+
else
|
|
69
|
+
"Database query"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def extract_db_config(payload)
|
|
74
|
+
connection = payload[:connection]
|
|
75
|
+
|
|
76
|
+
return unless connection
|
|
77
|
+
|
|
78
|
+
extract_db_config_from_connection(connection)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def add_db_config_attributes(attributes, db_config)
|
|
82
|
+
return unless db_config
|
|
83
|
+
|
|
84
|
+
attributes[:db_system] = db_config[:adapter] if db_config[:adapter]
|
|
85
|
+
|
|
86
|
+
if db_config[:database]
|
|
87
|
+
db_name = db_config[:database]
|
|
88
|
+
|
|
89
|
+
if db_config[:adapter] == "sqlite3" && db_name.include?("/")
|
|
90
|
+
db_name = File.basename(db_name)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
attributes[:db_name] = db_name
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
attributes[:server_address] = db_config[:host] if db_config[:host]
|
|
97
|
+
attributes[:server_port] = db_config[:port] if db_config[:port]
|
|
98
|
+
attributes[:server_socket_address] = db_config[:socket] if db_config[:socket]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if ::Rails.version.to_f >= 6.1
|
|
102
|
+
def extract_db_config_from_connection(connection)
|
|
103
|
+
if connection.pool.respond_to?(:db_config)
|
|
104
|
+
db_config = connection.pool.db_config
|
|
105
|
+
if db_config.respond_to?(:configuration_hash)
|
|
106
|
+
return db_config.configuration_hash
|
|
107
|
+
elsif db_config.respond_to?(:config)
|
|
108
|
+
return db_config.config
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
extract_db_config_fallback(connection)
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
# Rails 6.0 and earlier use spec API
|
|
116
|
+
def extract_db_config_from_connection(connection)
|
|
117
|
+
if connection.pool.respond_to?(:spec)
|
|
118
|
+
spec = connection.pool.spec
|
|
119
|
+
if spec.respond_to?(:config)
|
|
120
|
+
return spec.config
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
extract_db_config_fallback(connection)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def extract_db_config_fallback(connection)
|
|
129
|
+
connection.config if connection.respond_to?(:config)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentry
|
|
4
|
+
module Rails
|
|
5
|
+
module LogSubscribers
|
|
6
|
+
# Shared utility module for filtering sensitive parameters in log subscribers.
|
|
7
|
+
#
|
|
8
|
+
# This module provides consistent parameter filtering across all Sentry Rails
|
|
9
|
+
# log subscribers, leveraging Rails' built-in parameter filtering when available.
|
|
10
|
+
# It automatically detects the correct Rails parameter filtering API based on
|
|
11
|
+
# the Rails version and includes the appropriate implementation module.
|
|
12
|
+
#
|
|
13
|
+
# @example Usage in a log subscriber
|
|
14
|
+
# class MySubscriber < Sentry::Rails::LogSubscriber
|
|
15
|
+
# include Sentry::Rails::LogSubscribers::ParameterFilter
|
|
16
|
+
#
|
|
17
|
+
# def my_event(event)
|
|
18
|
+
# if Sentry.configuration.send_default_pii && event.payload[:params]
|
|
19
|
+
# filtered_params = filter_sensitive_params(event.payload[:params])
|
|
20
|
+
# attributes[:params] = filtered_params unless filtered_params.empty?
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
module ParameterFilter
|
|
25
|
+
EMPTY_HASH = {}.freeze
|
|
26
|
+
|
|
27
|
+
if ::Rails.version.to_f >= 6.0
|
|
28
|
+
def self.backend
|
|
29
|
+
ActiveSupport::ParameterFilter
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
def self.backend
|
|
33
|
+
ActionDispatch::Http::ParameterFilter
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Filter sensitive parameters from a hash, respecting Rails configuration.
|
|
38
|
+
#
|
|
39
|
+
# @param params [Hash] The parameters to filter
|
|
40
|
+
# @return [Hash] Filtered parameters with sensitive data removed
|
|
41
|
+
def filter_sensitive_params(params)
|
|
42
|
+
return EMPTY_HASH unless params.is_a?(Hash)
|
|
43
|
+
|
|
44
|
+
filter_parameters = ::Rails.application.config.filter_parameters
|
|
45
|
+
parameter_filter = ParameterFilter.backend.new(filter_parameters)
|
|
46
|
+
|
|
47
|
+
parameter_filter.filter(params)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/sentry/rails/railtie.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "sentry/rails/capture_exceptions"
|
|
2
4
|
require "sentry/rails/rescued_exception_interceptor"
|
|
3
5
|
require "sentry/rails/backtrace_cleaner"
|
|
@@ -47,8 +49,14 @@ module Sentry
|
|
|
47
49
|
setup_backtrace_cleanup_callback
|
|
48
50
|
inject_breadcrumbs_logger
|
|
49
51
|
activate_tracing
|
|
52
|
+
activate_structured_logging
|
|
50
53
|
|
|
51
54
|
register_error_subscriber(app) if ::Rails.version.to_f >= 7.0 && Sentry.configuration.rails.register_error_subscriber
|
|
55
|
+
|
|
56
|
+
# Presence of ActiveJob is no longer a reliable cue
|
|
57
|
+
if defined?(Sentry::Rails::ActiveJobExtensions)
|
|
58
|
+
Sentry::Rails::ActiveJobExtensions::SentryReporter.register_event_handlers
|
|
59
|
+
end
|
|
52
60
|
end
|
|
53
61
|
|
|
54
62
|
runner do
|
|
@@ -94,14 +102,14 @@ module Sentry
|
|
|
94
102
|
|
|
95
103
|
def inject_breadcrumbs_logger
|
|
96
104
|
if Sentry.configuration.breadcrumbs_logger.include?(:active_support_logger)
|
|
97
|
-
require
|
|
105
|
+
require "sentry/rails/breadcrumb/active_support_logger"
|
|
98
106
|
Sentry::Rails::Breadcrumb::ActiveSupportLogger.inject(Sentry.configuration.rails.active_support_logger_subscription_items)
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
if Sentry.configuration.breadcrumbs_logger.include?(:monotonic_active_support_logger)
|
|
102
110
|
return warn "Usage of `monotonic_active_support_logger` require a version of Rails >= 6.1, please upgrade your Rails version or use another logger" if ::Rails.version.to_f < 6.1
|
|
103
111
|
|
|
104
|
-
require
|
|
112
|
+
require "sentry/rails/breadcrumb/monotonic_active_support_logger"
|
|
105
113
|
Sentry::Rails::Breadcrumb::MonotonicActiveSupportLogger.inject
|
|
106
114
|
end
|
|
107
115
|
end
|
|
@@ -131,6 +139,12 @@ module Sentry
|
|
|
131
139
|
end
|
|
132
140
|
end
|
|
133
141
|
|
|
142
|
+
def activate_structured_logging
|
|
143
|
+
if Sentry.configuration.rails.structured_logging.enabled? && Sentry.configuration.enable_logs
|
|
144
|
+
Sentry::Rails::StructuredLogging.attach(Sentry.configuration.rails.structured_logging)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
134
148
|
def register_error_subscriber(app)
|
|
135
149
|
require "sentry/rails/error_subscriber"
|
|
136
150
|
app.executor.error_reporter.subscribe(Sentry::Rails::ErrorSubscriber.new)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sentry
|
|
2
4
|
module Rails
|
|
3
5
|
class RescuedExceptionInterceptor
|
|
@@ -17,7 +19,16 @@ module Sentry
|
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def report_rescued_exceptions?
|
|
20
|
-
Sentry.configuration.
|
|
22
|
+
# In rare edge cases, `Sentry.configuration` might be `nil` here.
|
|
23
|
+
# Hence, we use a safe navigation and fallback to a reasonable default
|
|
24
|
+
# of `true` in case the configuration couldn't be loaded.
|
|
25
|
+
# See https://github.com/getsentry/sentry-ruby/issues/2386
|
|
26
|
+
report_rescued_exceptions = Sentry.configuration&.rails&.report_rescued_exceptions
|
|
27
|
+
return report_rescued_exceptions unless report_rescued_exceptions.nil?
|
|
28
|
+
|
|
29
|
+
# `true` is the default for `report_rescued_exceptions`, as specified in
|
|
30
|
+
# `sentry-rails/lib/sentry/rails/configuration.rb`.
|
|
31
|
+
true
|
|
21
32
|
end
|
|
22
33
|
end
|
|
23
34
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/rails/log_subscriber"
|
|
4
|
+
require "sentry/rails/log_subscribers/action_controller_subscriber"
|
|
5
|
+
require "sentry/rails/log_subscribers/active_record_subscriber"
|
|
6
|
+
require "sentry/rails/log_subscribers/active_job_subscriber"
|
|
7
|
+
require "sentry/rails/log_subscribers/action_mailer_subscriber"
|
|
8
|
+
|
|
9
|
+
module Sentry
|
|
10
|
+
module Rails
|
|
11
|
+
module StructuredLogging
|
|
12
|
+
class << self
|
|
13
|
+
def attach(config)
|
|
14
|
+
config.subscribers.each do |component, subscriber_class|
|
|
15
|
+
subscriber_class.attach_to component
|
|
16
|
+
end
|
|
17
|
+
rescue => e
|
|
18
|
+
Sentry.configuration.sdk_logger.error("Failed to attach structured loggers: #{e.message}")
|
|
19
|
+
Sentry.configuration.sdk_logger.error(e.backtrace.join("\n"))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def detach(config)
|
|
23
|
+
config.subscribers.each do |component, subscriber_class|
|
|
24
|
+
subscriber_class.detach_from component
|
|
25
|
+
end
|
|
26
|
+
rescue => e
|
|
27
|
+
Sentry.configuration.sdk_logger.debug("Error during detaching loggers: #{e.message}")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "sentry/rails/tracing/abstract_subscriber"
|
|
2
4
|
require "sentry/rails/instrument_payload_cleanup_helper"
|
|
3
5
|
|
|
@@ -8,11 +10,11 @@ module Sentry
|
|
|
8
10
|
extend InstrumentPayloadCleanupHelper
|
|
9
11
|
|
|
10
12
|
EVENT_NAMES = ["process_action.action_controller"].freeze
|
|
11
|
-
OP_NAME = "view.process_action.action_controller"
|
|
12
|
-
SPAN_ORIGIN = "auto.view.rails"
|
|
13
|
+
OP_NAME = "view.process_action.action_controller"
|
|
14
|
+
SPAN_ORIGIN = "auto.view.rails"
|
|
13
15
|
|
|
14
16
|
def self.subscribe!
|
|
15
|
-
Sentry.
|
|
17
|
+
Sentry.sdk_logger.warn <<~MSG
|
|
16
18
|
DEPRECATION WARNING: sentry-rails has changed its approach on controller span recording and #{self.name} is now depreacted.
|
|
17
19
|
Please stop using or referencing #{self.name} as it will be removed in the next major release.
|
|
18
20
|
MSG
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "sentry/rails/tracing/abstract_subscriber"
|
|
2
4
|
|
|
3
5
|
module Sentry
|
|
@@ -5,8 +7,8 @@ module Sentry
|
|
|
5
7
|
module Tracing
|
|
6
8
|
class ActionViewSubscriber < AbstractSubscriber
|
|
7
9
|
EVENT_NAMES = ["render_template.action_view"].freeze
|
|
8
|
-
SPAN_PREFIX = "template."
|
|
9
|
-
SPAN_ORIGIN = "auto.template.rails"
|
|
10
|
+
SPAN_PREFIX = "template."
|
|
11
|
+
SPAN_ORIGIN = "auto.template.rails"
|
|
10
12
|
|
|
11
13
|
def self.subscribe!
|
|
12
14
|
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
|
@@ -71,6 +71,7 @@ module Sentry
|
|
|
71
71
|
|
|
72
72
|
if source_location
|
|
73
73
|
backtrace_line = Sentry::Backtrace::Line.parse(source_location)
|
|
74
|
+
|
|
74
75
|
span.set_data(Span::DataConventions::FILEPATH, backtrace_line.file) if backtrace_line.file
|
|
75
76
|
span.set_data(Span::DataConventions::LINENO, backtrace_line.number) if backtrace_line.number
|
|
76
77
|
span.set_data(Span::DataConventions::FUNCTION, backtrace_line.method) if backtrace_line.method
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "sentry/rails/tracing/abstract_subscriber"
|
|
2
4
|
|
|
3
5
|
module Sentry
|
|
@@ -19,19 +21,22 @@ module Sentry
|
|
|
19
21
|
analyze.active_storage
|
|
20
22
|
].freeze
|
|
21
23
|
|
|
22
|
-
SPAN_ORIGIN = "auto.file.rails"
|
|
24
|
+
SPAN_ORIGIN = "auto.file.rails"
|
|
23
25
|
|
|
24
26
|
def self.subscribe!
|
|
25
27
|
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
|
26
28
|
record_on_current_span(
|
|
27
|
-
op: "file.#{event_name}"
|
|
29
|
+
op: "file.#{event_name}",
|
|
28
30
|
origin: SPAN_ORIGIN,
|
|
29
31
|
start_timestamp: payload[START_TIMESTAMP_NAME],
|
|
30
32
|
description: payload[:service],
|
|
31
33
|
duration: duration
|
|
32
34
|
) do |span|
|
|
33
35
|
payload.each do |key, value|
|
|
34
|
-
|
|
36
|
+
next if key == START_TIMESTAMP_NAME
|
|
37
|
+
next if key == :key && !Sentry.configuration.send_default_pii
|
|
38
|
+
|
|
39
|
+
span.set_data(key, value)
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sentry/rails/tracing/abstract_subscriber"
|
|
4
|
+
|
|
5
|
+
module Sentry
|
|
6
|
+
module Rails
|
|
7
|
+
module Tracing
|
|
8
|
+
class ActiveSupportSubscriber < AbstractSubscriber
|
|
9
|
+
READ_EVENT_NAMES = %w[
|
|
10
|
+
cache_read.active_support
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
WRITE_EVENT_NAMES = %w[
|
|
14
|
+
cache_write.active_support
|
|
15
|
+
cache_increment.active_support
|
|
16
|
+
cache_decrement.active_support
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
REMOVE_EVENT_NAMES = %w[
|
|
20
|
+
cache_delete.active_support
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
FLUSH_EVENT_NAMES = %w[
|
|
24
|
+
cache_prune.active_support
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
EVENT_NAMES = READ_EVENT_NAMES + WRITE_EVENT_NAMES + REMOVE_EVENT_NAMES + FLUSH_EVENT_NAMES
|
|
28
|
+
|
|
29
|
+
SPAN_ORIGIN = "auto.cache.rails"
|
|
30
|
+
|
|
31
|
+
def self.subscribe!
|
|
32
|
+
subscribe_to_event(EVENT_NAMES) do |event_name, duration, payload|
|
|
33
|
+
record_on_current_span(
|
|
34
|
+
op: operation_name(event_name),
|
|
35
|
+
origin: SPAN_ORIGIN,
|
|
36
|
+
start_timestamp: payload[START_TIMESTAMP_NAME],
|
|
37
|
+
description: payload[:store],
|
|
38
|
+
duration: duration
|
|
39
|
+
) do |span|
|
|
40
|
+
span.set_data("cache.key", [*payload[:key]].select { |key| Utils::EncodingHelper.valid_utf_8?(key) })
|
|
41
|
+
span.set_data("cache.hit", payload[:hit] == true) # Handle nil case
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.operation_name(event_name)
|
|
47
|
+
case
|
|
48
|
+
when READ_EVENT_NAMES.include?(event_name)
|
|
49
|
+
"cache.get"
|
|
50
|
+
when WRITE_EVENT_NAMES.include?(event_name)
|
|
51
|
+
"cache.put"
|
|
52
|
+
when REMOVE_EVENT_NAMES.include?(event_name)
|
|
53
|
+
"cache.remove"
|
|
54
|
+
when FLUSH_EVENT_NAMES.include?(event_name)
|
|
55
|
+
"cache.flush"
|
|
56
|
+
else
|
|
57
|
+
"other"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/sentry/rails/tracing.rb
CHANGED
data/lib/sentry/rails/version.rb
CHANGED
data/lib/sentry/rails.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rails"
|
|
2
4
|
require "sentry-ruby"
|
|
3
5
|
require "sentry/integrable"
|
|
4
6
|
require "sentry/rails/tracing"
|
|
5
7
|
require "sentry/rails/configuration"
|
|
8
|
+
require "sentry/rails/structured_logging"
|
|
6
9
|
require "sentry/rails/engine"
|
|
7
10
|
require "sentry/rails/railtie"
|
|
8
11
|
|
data/lib/sentry-rails.rb
CHANGED
data/sentry-rails.gemspec
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative "lib/sentry/rails/version"
|
|
2
4
|
|
|
3
5
|
Gem::Specification.new do |spec|
|
|
@@ -11,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
11
13
|
spec.platform = Gem::Platform::RUBY
|
|
12
14
|
spec.required_ruby_version = '>= 2.4'
|
|
13
15
|
spec.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
|
14
|
-
spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples)'`.split("\n")
|
|
16
|
+
spec.files = `git ls-files | grep -Ev '^(spec|benchmarks|examples|\.rubocop\.yml)'`.split("\n")
|
|
15
17
|
|
|
16
18
|
github_root_uri = 'https://github.com/getsentry/sentry-ruby'
|
|
17
19
|
spec.homepage = "#{github_root_uri}/tree/#{spec.version}/#{spec.name}"
|
|
@@ -29,5 +31,5 @@ Gem::Specification.new do |spec|
|
|
|
29
31
|
spec.require_paths = ["lib"]
|
|
30
32
|
|
|
31
33
|
spec.add_dependency "railties", ">= 5.0"
|
|
32
|
-
spec.add_dependency "sentry-ruby", "~> 5.
|
|
34
|
+
spec.add_dependency "sentry-ruby", "~> 5.28.1"
|
|
33
35
|
end
|