sentry-ruby-core 4.8.1 → 5.1.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.
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sentry
4
+ # @api private
5
+ class Redis
6
+ OP_NAME ||= "db.redis.command"
7
+ LOGGER_NAME ||= :redis_logger
8
+
9
+ def initialize(commands, host, port, db)
10
+ @commands, @host, @port, @db = commands, host, port, db
11
+ end
12
+
13
+ def instrument
14
+ return yield unless Sentry.initialized?
15
+
16
+ record_span do
17
+ yield.tap do
18
+ record_breadcrumb
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :commands, :host, :port, :db
26
+
27
+ def record_span
28
+ return yield unless (transaction = Sentry.get_current_scope.get_transaction) && transaction.sampled
29
+
30
+ sentry_span = transaction.start_child(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f)
31
+
32
+ yield.tap do
33
+ sentry_span.set_description(commands_description)
34
+ sentry_span.set_data(:server, server_description)
35
+ sentry_span.set_timestamp(Sentry.utc_now.to_f)
36
+ end
37
+ end
38
+
39
+ def record_breadcrumb
40
+ return unless Sentry.configuration.breadcrumbs_logger.include?(LOGGER_NAME)
41
+
42
+ Sentry.add_breadcrumb(
43
+ Sentry::Breadcrumb.new(
44
+ level: :info,
45
+ category: OP_NAME,
46
+ type: :info,
47
+ data: {
48
+ commands: parsed_commands,
49
+ server: server_description
50
+ }
51
+ )
52
+ )
53
+ end
54
+
55
+ def commands_description
56
+ parsed_commands.map do |statement|
57
+ statement.values.join(" ").strip
58
+ end.join(", ")
59
+ end
60
+
61
+ def parsed_commands
62
+ commands.map do |statement|
63
+ command, key, *_values = statement
64
+
65
+ { command: command.to_s.upcase, key: key }
66
+ end
67
+ end
68
+
69
+ def server_description
70
+ "#{host}:#{port}/#{db}"
71
+ end
72
+
73
+ module Client
74
+ def logging(commands, &block)
75
+ Sentry::Redis.new(commands, host, port, db).instrument do
76
+ super
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ if defined?(::Redis::Client)
84
+ Sentry.register_patch do
85
+ patch = Sentry::Redis::Client
86
+ Redis::Client.prepend(patch) unless Redis::Client.ancestors.include?(patch)
87
+ end
88
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentry
4
+ # @api private
4
5
  class ReleaseDetector
5
6
  class << self
6
7
  def detect_release(project_root:, running_on_heroku:)
data/lib/sentry/scope.rb CHANGED
@@ -11,15 +11,22 @@ module Sentry
11
11
 
12
12
  attr_reader(*ATTRIBUTES)
13
13
 
14
+ # @param max_breadcrumbs [Integer] the maximum number of breadcrumbs to be stored in the scope.
14
15
  def initialize(max_breadcrumbs: nil)
15
16
  @max_breadcrumbs = max_breadcrumbs
16
17
  set_default_value
17
18
  end
18
19
 
20
+ # Resets the scope's attributes to defaults.
21
+ # @return [void]
19
22
  def clear
20
23
  set_default_value
21
24
  end
22
25
 
26
+ # Applies stored attributes and event processors to the given event.
27
+ # @param event [Event]
28
+ # @param hint [Hash] the hint data that'll be passed to event processors.
29
+ # @return [Event]
23
30
  def apply_to_event(event, hint = nil)
24
31
  event.tags = tags.merge(event.tags)
25
32
  event.user = user.merge(event.user)
@@ -45,14 +52,20 @@ module Sentry
45
52
  event
46
53
  end
47
54
 
55
+ # Adds the breadcrumb to the scope's breadcrumbs buffer.
56
+ # @param breadcrumb [Breadcrumb]
57
+ # @return [void]
48
58
  def add_breadcrumb(breadcrumb)
49
59
  breadcrumbs.record(breadcrumb)
50
60
  end
51
61
 
62
+ # Clears the scope's breadcrumbs buffer
63
+ # @return [void]
52
64
  def clear_breadcrumbs
53
65
  set_new_breadcrumb_buffer
54
66
  end
55
67
 
68
+ # @return [Scope]
56
69
  def dup
57
70
  copy = super
58
71
  copy.breadcrumbs = breadcrumbs.dup
@@ -66,6 +79,9 @@ module Sentry
66
79
  copy
67
80
  end
68
81
 
82
+ # Updates the scope's data from a given scope.
83
+ # @param scope [Scope]
84
+ # @return [void]
69
85
  def update_from_scope(scope)
70
86
  self.breadcrumbs = scope.breadcrumbs
71
87
  self.contexts = scope.contexts
@@ -77,6 +93,14 @@ module Sentry
77
93
  self.span = scope.span
78
94
  end
79
95
 
96
+ # Updates the scope's data from the given options.
97
+ # @param contexts [Hash]
98
+ # @param extras [Hash]
99
+ # @param tags [Hash]
100
+ # @param user [Hash]
101
+ # @param level [String, Symbol]
102
+ # @param fingerprint [Array]
103
+ # @return [void]
80
104
  def update_from_options(
81
105
  contexts: nil,
82
106
  extra: nil,
@@ -93,39 +117,59 @@ module Sentry
93
117
  self.fingerprint = fingerprint if fingerprint
94
118
  end
95
119
 
120
+ # Sets the scope's rack_env attribute.
121
+ # @param env [Hash]
122
+ # @return [Hash]
96
123
  def set_rack_env(env)
97
124
  env = env || {}
98
125
  @rack_env = env
99
126
  end
100
127
 
128
+ # Sets the scope's span attribute.
129
+ # @param span [Span]
130
+ # @return [Span]
101
131
  def set_span(span)
102
132
  check_argument_type!(span, Span)
103
133
  @span = span
104
134
  end
105
135
 
136
+ # @!macro set_user
106
137
  def set_user(user_hash)
107
138
  check_argument_type!(user_hash, Hash)
108
139
  @user = user_hash
109
140
  end
110
141
 
142
+ # @!macro set_extras
111
143
  def set_extras(extras_hash)
112
144
  check_argument_type!(extras_hash, Hash)
113
145
  @extra.merge!(extras_hash)
114
146
  end
115
147
 
148
+ # Adds a new key-value pair to current extras.
149
+ # @param key [String, Symbol]
150
+ # @param value [Object]
151
+ # @return [Hash]
116
152
  def set_extra(key, value)
117
153
  set_extras(key => value)
118
154
  end
119
155
 
156
+ # @!macro set_tags
120
157
  def set_tags(tags_hash)
121
158
  check_argument_type!(tags_hash, Hash)
122
159
  @tags.merge!(tags_hash)
123
160
  end
124
161
 
162
+ # Adds a new key-value pair to current tags.
163
+ # @param key [String, Symbol]
164
+ # @param value [Object]
165
+ # @return [Hash]
125
166
  def set_tag(key, value)
126
167
  set_tags(key => value)
127
168
  end
128
169
 
170
+ # Updates the scope's contexts attribute by merging with the old value.
171
+ # @param contexts [Hash]
172
+ # @return [Hash]
129
173
  def set_contexts(contexts_hash)
130
174
  check_argument_type!(contexts_hash, Hash)
131
175
  @contexts.merge!(contexts_hash) do |key, old, new|
@@ -133,37 +177,58 @@ module Sentry
133
177
  end
134
178
  end
135
179
 
180
+ # @!macro set_context
136
181
  def set_context(key, value)
137
182
  check_argument_type!(value, Hash)
138
183
  set_contexts(key => value)
139
184
  end
140
185
 
186
+ # Sets the scope's level attribute.
187
+ # @param level [String, Symbol]
188
+ # @return [void]
141
189
  def set_level(level)
142
190
  @level = level
143
191
  end
144
192
 
193
+ # Appends a new transaction name to the scope.
194
+ # The "transaction" here does not refer to `Transaction` objects.
195
+ # @param transaction_name [String]
196
+ # @return [void]
145
197
  def set_transaction_name(transaction_name)
146
198
  @transaction_names << transaction_name
147
199
  end
148
200
 
201
+ # Returns current transaction name.
202
+ # The "transaction" here does not refer to `Transaction` objects.
203
+ # @return [String, nil]
149
204
  def transaction_name
150
205
  @transaction_names.last
151
206
  end
152
207
 
208
+ # Returns the associated Transaction object.
209
+ # @return [Transaction, nil]
153
210
  def get_transaction
154
211
  span.transaction if span
155
212
  end
156
213
 
214
+ # Returns the associated Span object.
215
+ # @return [Span, nil]
157
216
  def get_span
158
217
  span
159
218
  end
160
219
 
220
+ # Sets the scope's fingerprint attribute.
221
+ # @param fingerprint [Array]
222
+ # @return [Array]
161
223
  def set_fingerprint(fingerprint)
162
224
  check_argument_type!(fingerprint, Array)
163
225
 
164
226
  @fingerprint = fingerprint
165
227
  end
166
228
 
229
+ # Adds a new event processor [Proc] to the scope.
230
+ # @param block [Proc]
231
+ # @return [void]
167
232
  def add_event_processor(&block)
168
233
  @event_processors << block
169
234
  end
@@ -193,8 +258,8 @@ module Sentry
193
258
  @breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
194
259
  end
195
260
 
196
-
197
261
  class << self
262
+ # @return [Hash]
198
263
  def os_context
199
264
  @os_context ||=
200
265
  begin
@@ -208,6 +273,7 @@ module Sentry
208
273
  end
209
274
  end
210
275
 
276
+ # @return [Hash]
211
277
  def runtime_context
212
278
  @runtime_context ||= {
213
279
  name: RbConfig::CONFIG["ruby_install_name"],
data/lib/sentry/span.rb CHANGED
@@ -18,9 +18,49 @@ module Sentry
18
18
  504 => "deadline_exceeded"
19
19
  }
20
20
 
21
-
22
- attr_reader :trace_id, :span_id, :parent_span_id, :sampled, :start_timestamp, :timestamp, :description, :op, :status, :tags, :data
23
- attr_accessor :span_recorder, :transaction
21
+ # An uuid that can be used to identify a trace.
22
+ # @return [String]
23
+ attr_reader :trace_id
24
+ # An uuid that can be used to identify the span.
25
+ # @return [String]
26
+ attr_reader :span_id
27
+ # Span parent's span_id.
28
+ # @return [String]
29
+ attr_reader :parent_span_id
30
+ # Sampling result of the span.
31
+ # @return [Boolean, nil]
32
+ attr_reader :sampled
33
+ # Starting timestamp of the span.
34
+ # @return [Float]
35
+ attr_reader :start_timestamp
36
+ # Finishing timestamp of the span.
37
+ # @return [Float]
38
+ attr_reader :timestamp
39
+ # Span description
40
+ # @return [String]
41
+ attr_reader :description
42
+ # Span operation
43
+ # @return [String]
44
+ attr_reader :op
45
+ # Span status
46
+ # @return [String]
47
+ attr_reader :status
48
+ # Span tags
49
+ # @return [Hash]
50
+ attr_reader :tags
51
+ # Span data
52
+ # @return [Hash]
53
+ attr_reader :data
54
+
55
+ # The SpanRecorder the current span belongs to.
56
+ # SpanRecorder holds all spans under the same Transaction object (including the Transaction itself).
57
+ # @return [SpanRecorder]
58
+ attr_accessor :span_recorder
59
+
60
+ # The Transaction object the Span belongs to.
61
+ # Every span needs to be attached to a Transaction and their child spans will also inherit the same transaction.
62
+ # @return [Transaction]
63
+ attr_accessor :transaction
24
64
 
25
65
  def initialize(
26
66
  description: nil,
@@ -45,6 +85,8 @@ module Sentry
45
85
  @tags = {}
46
86
  end
47
87
 
88
+ # Finishes the span by adding a timestamp.
89
+ # @return [self]
48
90
  def finish
49
91
  # already finished
50
92
  return if @timestamp
@@ -53,6 +95,8 @@ module Sentry
53
95
  self
54
96
  end
55
97
 
98
+ # Generates a trace string that can be used to connect other transactions.
99
+ # @return [String]
56
100
  def to_sentry_trace
57
101
  sampled_flag = ""
58
102
  sampled_flag = @sampled ? 1 : 0 unless @sampled.nil?
@@ -60,6 +104,7 @@ module Sentry
60
104
  "#{@trace_id}-#{@span_id}-#{sampled_flag}"
61
105
  end
62
106
 
107
+ # @return [Hash]
63
108
  def to_hash
64
109
  {
65
110
  trace_id: @trace_id,
@@ -75,6 +120,8 @@ module Sentry
75
120
  }
76
121
  end
77
122
 
123
+ # Returns the span's context that can be used to embed in an Event.
124
+ # @return [Hash]
78
125
  def get_trace_context
79
126
  {
80
127
  trace_id: @trace_id,
@@ -86,9 +133,11 @@ module Sentry
86
133
  }
87
134
  end
88
135
 
89
- def start_child(**options)
90
- options = options.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
91
- new_span = Span.new(**options)
136
+ # Starts a child span with given attributes.
137
+ # @param attributes [Hash] the attributes for the child span.
138
+ def start_child(**attributes)
139
+ attributes = attributes.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
140
+ new_span = Span.new(**attributes)
92
141
  new_span.transaction = transaction
93
142
  new_span.span_recorder = span_recorder
94
143
 
@@ -99,8 +148,17 @@ module Sentry
99
148
  new_span
100
149
  end
101
150
 
102
- def with_child_span(**options, &block)
103
- child_span = start_child(**options)
151
+ # Starts a child span, yield it to the given block, and then finish the span after the block is executed.
152
+ # @example
153
+ # span.with_child_span do |child_span|
154
+ # # things happen here will be recorded in a child span
155
+ # end
156
+ #
157
+ # @param attributes [Hash] the attributes for the child span.
158
+ # @param block [Proc] the action to be recorded in the child span.
159
+ # @yieldparam child_span [Span]
160
+ def with_child_span(**attributes, &block)
161
+ child_span = start_child(**attributes)
104
162
 
105
163
  yield(child_span)
106
164
 
@@ -111,22 +169,33 @@ module Sentry
111
169
  dup
112
170
  end
113
171
 
172
+ # Sets the span's operation.
173
+ # @param op [String] operation of the span.
114
174
  def set_op(op)
115
175
  @op = op
116
176
  end
117
177
 
178
+ # Sets the span's description.
179
+ # @param description [String] description of the span.
118
180
  def set_description(description)
119
181
  @description = description
120
182
  end
121
183
 
184
+
185
+ # Sets the span's status.
186
+ # @param satus [String] status of the span.
122
187
  def set_status(status)
123
188
  @status = status
124
189
  end
125
190
 
191
+ # Sets the span's finish timestamp.
192
+ # @param timestamp [Float] finished time in float format (most precise).
126
193
  def set_timestamp(timestamp)
127
194
  @timestamp = timestamp
128
195
  end
129
196
 
197
+ # Sets the span's status with given http status code.
198
+ # @param status_code [String] example: "500".
130
199
  def set_http_status(status_code)
131
200
  status_code = status_code.to_i
132
201
  set_data("status_code", status_code)
@@ -140,10 +209,16 @@ module Sentry
140
209
  set_status(status)
141
210
  end
142
211
 
212
+ # Inserts a key-value pair to the span's data payload.
213
+ # @param key [String, Symbol]
214
+ # @param value [Object]
143
215
  def set_data(key, value)
144
216
  @data[key] = value
145
217
  end
146
218
 
219
+ # Sets a tag to the span.
220
+ # @param key [String, Symbol]
221
+ # @param value [String]
147
222
  def set_tag(key, value)
148
223
  @tags[key] = value
149
224
  end
@@ -14,7 +14,22 @@ module Sentry
14
14
 
15
15
  include LoggingHelper
16
16
 
17
- attr_reader :name, :parent_sampled, :hub, :configuration, :logger
17
+ # The name of the transaction.
18
+ # @return [String]
19
+ attr_reader :name
20
+
21
+ # The sampling decision of the parent transaction, which will be considered when making the current transaction's sampling decision.
22
+ # @return [String]
23
+ attr_reader :parent_sampled
24
+
25
+ # @deprecated Use Sentry.get_current_hub instead.
26
+ attr_reader :hub
27
+
28
+ # @deprecated Use Sentry.configuration instead.
29
+ attr_reader :configuration
30
+
31
+ # @deprecated Use Sentry.logger instead.
32
+ attr_reader :logger
18
33
 
19
34
  def initialize(name: nil, parent_sampled: nil, hub:, **options)
20
35
  super(**options)
@@ -23,11 +38,23 @@ module Sentry
23
38
  @parent_sampled = parent_sampled
24
39
  @transaction = self
25
40
  @hub = hub
26
- @configuration = hub.configuration
27
- @logger = configuration.logger
41
+ @configuration = hub.configuration # to be removed
42
+ @tracing_enabled = hub.configuration.tracing_enabled?
43
+ @traces_sampler = hub.configuration.traces_sampler
44
+ @traces_sample_rate = hub.configuration.traces_sample_rate
45
+ @logger = hub.configuration.logger
28
46
  init_span_recorder
29
47
  end
30
48
 
49
+ # Initalizes a Transaction instance with a Sentry trace string from another transaction (usually from an external request).
50
+ #
51
+ # The original transaction will become the parent of the new Transaction instance. And they will share the same `trace_id`.
52
+ #
53
+ # The child transaction will also store the parent's sampling decision in its `parent_sampled` attribute.
54
+ # @param sentry_trace [String] the trace string from the previous transaction.
55
+ # @param hub [Hub] the hub that'll be responsible for sending this transaction when it's finished.
56
+ # @param options [Hash] the options you want to use to initialize a Transaction instance.
57
+ # @return [Transaction, nil]
31
58
  def self.from_sentry_trace(sentry_trace, hub: Sentry.get_current_hub, **options)
32
59
  return unless hub.configuration.tracing_enabled?
33
60
  return unless sentry_trace
@@ -46,12 +73,14 @@ module Sentry
46
73
  new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, hub: hub, **options)
47
74
  end
48
75
 
76
+ # @return [Hash]
49
77
  def to_hash
50
78
  hash = super
51
79
  hash.merge!(name: @name, sampled: @sampled, parent_sampled: @parent_sampled)
52
80
  hash
53
81
  end
54
82
 
83
+ # @return [Transaction]
55
84
  def deep_dup
56
85
  copy = super
57
86
  copy.init_span_recorder(@span_recorder.max_length)
@@ -65,23 +94,24 @@ module Sentry
65
94
  copy
66
95
  end
67
96
 
97
+ # Sets initial sampling decision of the transaction.
98
+ # @param sampling_context [Hash] a context Hash that'll be passed to `traces_sampler` (if provided).
99
+ # @return [void]
68
100
  def set_initial_sample_decision(sampling_context:)
69
- unless configuration.tracing_enabled?
101
+ unless @tracing_enabled
70
102
  @sampled = false
71
103
  return
72
104
  end
73
105
 
74
106
  return unless @sampled.nil?
75
107
 
76
- traces_sampler = configuration.traces_sampler
77
-
78
108
  sample_rate =
79
- if traces_sampler.is_a?(Proc)
80
- traces_sampler.call(sampling_context)
109
+ if @traces_sampler.is_a?(Proc)
110
+ @traces_sampler.call(sampling_context)
81
111
  elsif !sampling_context[:parent_sampled].nil?
82
112
  sampling_context[:parent_sampled]
83
113
  else
84
- configuration.traces_sample_rate
114
+ @traces_sample_rate
85
115
  end
86
116
 
87
117
  transaction_description = generate_transaction_description
@@ -113,6 +143,9 @@ module Sentry
113
143
  end
114
144
  end
115
145
 
146
+ # Finishes the transaction's recording and send it to Sentry.
147
+ # @param hub [Hub] the hub that'll send this transaction. (Deprecated)
148
+ # @return [TransactionEvent]
116
149
  def finish(hub: nil)
117
150
  if hub
118
151
  log_warn(
@@ -131,13 +164,12 @@ module Sentry
131
164
  @name = UNLABELD_NAME
132
165
  end
133
166
 
134
- unless @sampled || @parent_sampled
167
+ if @sampled
168
+ event = hub.current_client.event_from_transaction(self)
169
+ hub.capture_event(event)
170
+ else
135
171
  hub.current_client.transport.record_lost_event(:sample_rate, 'transaction')
136
- return
137
172
  end
138
-
139
- event = hub.current_client.event_from_transaction(self)
140
- hub.capture_event(event)
141
173
  end
142
174
 
143
175
  protected
@@ -16,17 +16,25 @@ module Sentry
16
16
  attr_writer(*WRITER_ATTRIBUTES)
17
17
  attr_reader(*SERIALIZEABLE_ATTRIBUTES)
18
18
 
19
+ # @return [<Array[Span]>]
19
20
  attr_accessor :spans
20
21
 
22
+ # @param configuration [Configuration]
23
+ # @param integration_meta [Hash, nil]
24
+ # @param message [String, nil]
21
25
  def initialize(configuration:, integration_meta: nil, message: nil)
22
26
  super
23
27
  @type = TYPE
24
28
  end
25
29
 
30
+ # Sets the event's start_timestamp.
31
+ # @param time [Time, Float]
32
+ # @return [void]
26
33
  def start_timestamp=(time)
27
34
  @start_timestamp = time.is_a?(Time) ? time.to_f : time
28
35
  end
29
36
 
37
+ # @return [Hash]
30
38
  def to_hash
31
39
  data = super
32
40
  data[:spans] = @spans.map(&:to_hash) if @spans
@@ -3,8 +3,7 @@
3
3
  module Sentry
4
4
  class Transport
5
5
  class Configuration
6
- attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder,
7
- :encoding
6
+ attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :encoding
8
7
  attr_reader :transport_class
9
8
 
10
9
  def initialize