sentry-ruby 5.18.2 → 5.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/lib/sentry/attachment.rb +40 -0
  4. data/lib/sentry/backtrace.rb +1 -3
  5. data/lib/sentry/baggage.rb +6 -6
  6. data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
  7. data/lib/sentry/check_in_event.rb +4 -4
  8. data/lib/sentry/client.rb +9 -9
  9. data/lib/sentry/configuration.rb +30 -18
  10. data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
  11. data/lib/sentry/cron/monitor_check_ins.rb +1 -1
  12. data/lib/sentry/cron/monitor_config.rb +1 -1
  13. data/lib/sentry/dsn.rb +3 -3
  14. data/lib/sentry/envelope.rb +8 -8
  15. data/lib/sentry/event.rb +11 -7
  16. data/lib/sentry/faraday.rb +77 -0
  17. data/lib/sentry/graphql.rb +1 -1
  18. data/lib/sentry/hub.rb +8 -1
  19. data/lib/sentry/interfaces/mechanism.rb +1 -1
  20. data/lib/sentry/interfaces/request.rb +5 -5
  21. data/lib/sentry/interfaces/single_exception.rb +1 -1
  22. data/lib/sentry/interfaces/stacktrace.rb +3 -1
  23. data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
  24. data/lib/sentry/logger.rb +1 -1
  25. data/lib/sentry/metrics/aggregator.rb +12 -12
  26. data/lib/sentry/metrics/set_metric.rb +2 -2
  27. data/lib/sentry/metrics.rb +15 -15
  28. data/lib/sentry/net/http.rb +16 -38
  29. data/lib/sentry/profiler.rb +19 -20
  30. data/lib/sentry/propagation_context.rb +1 -1
  31. data/lib/sentry/rack/capture_exceptions.rb +1 -1
  32. data/lib/sentry/rack.rb +2 -2
  33. data/lib/sentry/rake.rb +2 -2
  34. data/lib/sentry/release_detector.rb +4 -4
  35. data/lib/sentry/scope.rb +15 -0
  36. data/lib/sentry/session_flusher.rb +1 -1
  37. data/lib/sentry/span.rb +8 -1
  38. data/lib/sentry/test_helper.rb +1 -1
  39. data/lib/sentry/transaction.rb +2 -2
  40. data/lib/sentry/transaction_event.rb +1 -1
  41. data/lib/sentry/transport/http_transport.rb +12 -12
  42. data/lib/sentry/transport.rb +10 -4
  43. data/lib/sentry/utils/env_helper.rb +21 -0
  44. data/lib/sentry/utils/http_tracing.rb +41 -0
  45. data/lib/sentry/utils/real_ip.rb +1 -1
  46. data/lib/sentry/version.rb +1 -1
  47. data/lib/sentry-ruby.rb +9 -1
  48. data/sentry-ruby.gemspec +1 -1
  49. metadata +12 -8
@@ -27,8 +27,9 @@ module Sentry
27
27
  attr_accessor :abs_path, :context_line, :function, :in_app, :filename,
28
28
  :lineno, :module, :pre_context, :post_context, :vars
29
29
 
30
- def initialize(project_root, line)
30
+ def initialize(project_root, line, strip_backtrace_load_path = true)
31
31
  @project_root = project_root
32
+ @strip_backtrace_load_path = strip_backtrace_load_path
32
33
 
33
34
  @abs_path = line.file
34
35
  @function = line.method if line.method
@@ -44,6 +45,7 @@ module Sentry
44
45
 
45
46
  def compute_filename
46
47
  return if abs_path.nil?
48
+ return abs_path unless @strip_backtrace_load_path
47
49
 
48
50
  prefix =
49
51
  if under_project_root? && in_app
@@ -17,22 +17,35 @@ module Sentry
17
17
  # @return [Proc, nil]
18
18
  attr_reader :backtrace_cleanup_callback
19
19
 
20
+ # @return [Boolean]
21
+ attr_reader :strip_backtrace_load_path
22
+
20
23
  # @param project_root [String]
21
24
  # @param app_dirs_pattern [Regexp, nil]
22
25
  # @param linecache [LineCache]
23
26
  # @param context_lines [Integer, nil]
24
27
  # @param backtrace_cleanup_callback [Proc, nil]
28
+ # @param strip_backtrace_load_path [Boolean]
25
29
  # @see Configuration#project_root
26
30
  # @see Configuration#app_dirs_pattern
27
31
  # @see Configuration#linecache
28
32
  # @see Configuration#context_lines
29
33
  # @see Configuration#backtrace_cleanup_callback
30
- def initialize(project_root:, app_dirs_pattern:, linecache:, context_lines:, backtrace_cleanup_callback: nil)
34
+ # @see Configuration#strip_backtrace_load_path
35
+ def initialize(
36
+ project_root:,
37
+ app_dirs_pattern:,
38
+ linecache:,
39
+ context_lines:,
40
+ backtrace_cleanup_callback: nil,
41
+ strip_backtrace_load_path: true
42
+ )
31
43
  @project_root = project_root
32
44
  @app_dirs_pattern = app_dirs_pattern
33
45
  @linecache = linecache
34
46
  @context_lines = context_lines
35
47
  @backtrace_cleanup_callback = backtrace_cleanup_callback
48
+ @strip_backtrace_load_path = strip_backtrace_load_path
36
49
  end
37
50
 
38
51
  # Generates a StacktraceInterface with the given backtrace.
@@ -73,7 +86,7 @@ module Sentry
73
86
  private
74
87
 
75
88
  def convert_parsed_line_into_frame(line)
76
- frame = StacktraceInterface::Frame.new(project_root, line)
89
+ frame = StacktraceInterface::Frame.new(project_root, line, strip_backtrace_load_path)
77
90
  frame.set_context(linecache, context_lines) if context_lines
78
91
  frame
79
92
  end
data/lib/sentry/logger.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
3
+ require "logger"
4
4
 
5
5
  module Sentry
6
6
  class Logger < ::Logger
@@ -41,8 +41,8 @@ module Sentry
41
41
  @stacktrace_builder = configuration.stacktrace_builder
42
42
 
43
43
  @default_tags = {}
44
- @default_tags['release'] = configuration.release if configuration.release
45
- @default_tags['environment'] = configuration.environment if configuration.environment
44
+ @default_tags["release"] = configuration.release if configuration.release
45
+ @default_tags["environment"] = configuration.environment if configuration.environment
46
46
 
47
47
  @mutex = Mutex.new
48
48
 
@@ -59,7 +59,7 @@ module Sentry
59
59
  def add(type,
60
60
  key,
61
61
  value,
62
- unit: 'none',
62
+ unit: "none",
63
63
  tags: {},
64
64
  timestamp: nil,
65
65
  stacklevel: nil)
@@ -98,7 +98,7 @@ module Sentry
98
98
  unless flushable_buckets.empty?
99
99
  payload = serialize_buckets(flushable_buckets)
100
100
  envelope.add_item(
101
- { type: 'statsd', length: payload.bytesize },
101
+ { type: "statsd", length: payload.bytesize },
102
102
  payload
103
103
  )
104
104
  end
@@ -107,7 +107,7 @@ module Sentry
107
107
  code_locations.each do |timestamp, locations|
108
108
  payload = serialize_locations(timestamp, locations)
109
109
  envelope.add_item(
110
- { type: 'metric_meta', content_type: 'application/json' },
110
+ { type: "metric_meta", content_type: "application/json" },
111
111
  payload
112
112
  )
113
113
  end
@@ -161,8 +161,8 @@ module Sentry
161
161
  buckets.map do |timestamp, timestamp_buckets|
162
162
  timestamp_buckets.map do |metric_key, metric|
163
163
  type, key, unit, tags = metric_key
164
- values = metric.serialize.join(':')
165
- sanitized_tags = tags.map { |k, v| "#{sanitize_tag_key(k)}:#{sanitize_tag_value(v)}" }.join(',')
164
+ values = metric.serialize.join(":")
165
+ sanitized_tags = tags.map { |k, v| "#{sanitize_tag_key(k)}:#{sanitize_tag_value(v)}" }.join(",")
166
166
 
167
167
  "#{sanitize_key(key)}@#{sanitize_unit(unit)}:#{values}|#{type}|\##{sanitized_tags}|T#{timestamp}"
168
168
  end
@@ -175,22 +175,22 @@ module Sentry
175
175
  mri = "#{type}:#{sanitize_key(key)}@#{sanitize_unit(unit)}"
176
176
 
177
177
  # note this needs to be an array but it really doesn't serve a purpose right now
178
- [mri, [location.merge(type: 'location')]]
178
+ [mri, [location.merge(type: "location")]]
179
179
  end.to_h
180
180
 
181
181
  { timestamp: timestamp, mapping: mapping }
182
182
  end
183
183
 
184
184
  def sanitize_key(key)
185
- key.gsub(KEY_SANITIZATION_REGEX, '_')
185
+ key.gsub(KEY_SANITIZATION_REGEX, "_")
186
186
  end
187
187
 
188
188
  def sanitize_unit(unit)
189
- unit.gsub(UNIT_SANITIZATION_REGEX, '')
189
+ unit.gsub(UNIT_SANITIZATION_REGEX, "")
190
190
  end
191
191
 
192
192
  def sanitize_tag_key(key)
193
- key.gsub(TAG_KEY_SANITIZATION_REGEX, '')
193
+ key.gsub(TAG_KEY_SANITIZATION_REGEX, "")
194
194
  end
195
195
 
196
196
  def sanitize_tag_value(value)
@@ -209,7 +209,7 @@ module Sentry
209
209
  updated_tags = @default_tags.merge(tags)
210
210
 
211
211
  transaction_name = get_transaction_name
212
- updated_tags['transaction'] = transaction_name if transaction_name
212
+ updated_tags["transaction"] = transaction_name if transaction_name
213
213
 
214
214
  updated_tags
215
215
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require 'zlib'
3
+ require "set"
4
+ require "zlib"
5
5
 
6
6
  module Sentry
7
7
  module Metrics
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sentry/metrics/metric'
4
- require 'sentry/metrics/counter_metric'
5
- require 'sentry/metrics/distribution_metric'
6
- require 'sentry/metrics/gauge_metric'
7
- require 'sentry/metrics/set_metric'
8
- require 'sentry/metrics/timing'
9
- require 'sentry/metrics/aggregator'
3
+ require "sentry/metrics/metric"
4
+ require "sentry/metrics/counter_metric"
5
+ require "sentry/metrics/distribution_metric"
6
+ require "sentry/metrics/gauge_metric"
7
+ require "sentry/metrics/set_metric"
8
+ require "sentry/metrics/timing"
9
+ require "sentry/metrics/aggregator"
10
10
 
11
11
  module Sentry
12
12
  module Metrics
@@ -14,32 +14,32 @@ module Sentry
14
14
  INFORMATION_UNITS = %w[bit byte kilobyte kibibyte megabyte mebibyte gigabyte gibibyte terabyte tebibyte petabyte pebibyte exabyte exbibyte]
15
15
  FRACTIONAL_UNITS = %w[ratio percent]
16
16
 
17
- OP_NAME = 'metric.timing'
18
- SPAN_ORIGIN = 'auto.metric.timing'
17
+ OP_NAME = "metric.timing"
18
+ SPAN_ORIGIN = "auto.metric.timing"
19
19
 
20
20
  class << self
21
- def increment(key, value = 1.0, unit: 'none', tags: {}, timestamp: nil)
21
+ def increment(key, value = 1.0, unit: "none", tags: {}, timestamp: nil)
22
22
  Sentry.metrics_aggregator&.add(:c, key, value, unit: unit, tags: tags, timestamp: timestamp)
23
23
  end
24
24
 
25
- def distribution(key, value, unit: 'none', tags: {}, timestamp: nil)
25
+ def distribution(key, value, unit: "none", tags: {}, timestamp: nil)
26
26
  Sentry.metrics_aggregator&.add(:d, key, value, unit: unit, tags: tags, timestamp: timestamp)
27
27
  end
28
28
 
29
- def set(key, value, unit: 'none', tags: {}, timestamp: nil)
29
+ def set(key, value, unit: "none", tags: {}, timestamp: nil)
30
30
  Sentry.metrics_aggregator&.add(:s, key, value, unit: unit, tags: tags, timestamp: timestamp)
31
31
  end
32
32
 
33
- def gauge(key, value, unit: 'none', tags: {}, timestamp: nil)
33
+ def gauge(key, value, unit: "none", tags: {}, timestamp: nil)
34
34
  Sentry.metrics_aggregator&.add(:g, key, value, unit: unit, tags: tags, timestamp: timestamp)
35
35
  end
36
36
 
37
- def timing(key, unit: 'second', tags: {}, timestamp: nil, &block)
37
+ def timing(key, unit: "second", tags: {}, timestamp: nil, &block)
38
38
  return unless block_given?
39
39
  return yield unless DURATION_UNITS.include?(unit)
40
40
 
41
41
  result, value = Sentry.with_child_span(op: OP_NAME, description: key, origin: SPAN_ORIGIN) do |span|
42
- tags.each { |k, v| span.set_tag(k, v.is_a?(Array) ? v.join(', ') : v.to_s) } if span
42
+ tags.each { |k, v| span.set_tag(k, v.is_a?(Array) ? v.join(", ") : v.to_s) } if span
43
43
 
44
44
  start = Timing.send(unit.to_sym)
45
45
  result = yield
@@ -2,11 +2,14 @@
2
2
 
3
3
  require "net/http"
4
4
  require "resolv"
5
+ require "sentry/utils/http_tracing"
5
6
 
6
7
  module Sentry
7
8
  # @api private
8
9
  module Net
9
10
  module HTTP
11
+ include Utils::HttpTracing
12
+
10
13
  OP_NAME = "http.client"
11
14
  SPAN_ORIGIN = "auto.http.net_http"
12
15
  BREADCRUMB_CATEGORY = "net.http"
@@ -21,8 +24,7 @@ module Sentry
21
24
  # req['connection'] ||= 'close'
22
25
  # return request(req, body, &block) # <- request will be called for the second time from the first call
23
26
  # }
24
- # end
25
- # # .....
27
+ # end # .....
26
28
  # end
27
29
  # ```
28
30
  #
@@ -34,44 +36,26 @@ module Sentry
34
36
  Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |sentry_span|
35
37
  request_info = extract_request_info(req)
36
38
 
37
- if propagate_trace?(request_info[:url], Sentry.configuration)
39
+ if propagate_trace?(request_info[:url])
38
40
  set_propagation_headers(req)
39
41
  end
40
42
 
41
- super.tap do |res|
42
- record_sentry_breadcrumb(request_info, res)
43
+ res = super
44
+ response_status = res.code.to_i
43
45
 
44
- if sentry_span
45
- sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
46
- sentry_span.set_data(Span::DataConventions::URL, request_info[:url])
47
- sentry_span.set_data(Span::DataConventions::HTTP_METHOD, request_info[:method])
48
- sentry_span.set_data(Span::DataConventions::HTTP_QUERY, request_info[:query]) if request_info[:query]
49
- sentry_span.set_data(Span::DataConventions::HTTP_STATUS_CODE, res.code.to_i)
50
- end
46
+ if record_sentry_breadcrumb?
47
+ record_sentry_breadcrumb(request_info, response_status)
51
48
  end
52
- end
53
- end
54
49
 
55
- private
50
+ if sentry_span
51
+ set_span_info(sentry_span, request_info, response_status)
52
+ end
56
53
 
57
- def set_propagation_headers(req)
58
- Sentry.get_trace_propagation_headers&.each { |k, v| req[k] = v }
54
+ res
55
+ end
59
56
  end
60
57
 
61
- def record_sentry_breadcrumb(request_info, res)
62
- return unless Sentry.initialized? && Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
63
-
64
- crumb = Sentry::Breadcrumb.new(
65
- level: :info,
66
- category: BREADCRUMB_CATEGORY,
67
- type: :info,
68
- data: {
69
- status: res.code.to_i,
70
- **request_info
71
- }
72
- )
73
- Sentry.add_breadcrumb(crumb)
74
- end
58
+ private
75
59
 
76
60
  def from_sentry_sdk?
77
61
  dsn = Sentry.configuration.dsn
@@ -82,7 +66,7 @@ module Sentry
82
66
  # IPv6 url could look like '::1/path', and that won't parse without
83
67
  # wrapping it in square brackets.
84
68
  hostname = address =~ Resolv::IPv6::Regex ? "[#{address}]" : address
85
- uri = req.uri || URI.parse("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}")
69
+ uri = req.uri || URI.parse(URI::DEFAULT_PARSER.escape("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}"))
86
70
  url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
87
71
 
88
72
  result = { method: req.method, url: url }
@@ -94,12 +78,6 @@ module Sentry
94
78
 
95
79
  result
96
80
  end
97
-
98
- def propagate_trace?(url, configuration)
99
- url &&
100
- configuration.propagate_traces &&
101
- configuration.trace_propagation_targets.any? { |target| url.match?(target) }
102
- end
103
81
  end
104
82
  end
105
83
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
3
+ require "securerandom"
4
4
 
5
5
  module Sentry
6
6
  class Profiler
7
- VERSION = '1'
8
- PLATFORM = 'ruby'
7
+ VERSION = "1"
8
+ PLATFORM = "ruby"
9
9
  # 101 Hz in microseconds
10
10
  DEFAULT_INTERVAL = 1e6 / 101
11
11
  MICRO_TO_NANO_SECONDS = 1e3
@@ -14,14 +14,14 @@ module Sentry
14
14
  attr_reader :sampled, :started, :event_id
15
15
 
16
16
  def initialize(configuration)
17
- @event_id = SecureRandom.uuid.delete('-')
17
+ @event_id = SecureRandom.uuid.delete("-")
18
18
  @started = false
19
19
  @sampled = nil
20
20
 
21
21
  @profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
22
22
  @profiles_sample_rate = configuration.profiles_sample_rate
23
23
  @project_root = configuration.project_root
24
- @app_dirs_pattern = configuration.app_dirs_pattern || Backtrace::APP_DIRS_PATTERN
24
+ @app_dirs_pattern = configuration.app_dirs_pattern
25
25
  @in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
26
26
  end
27
27
 
@@ -33,7 +33,7 @@ module Sentry
33
33
  raw: true,
34
34
  aggregate: false)
35
35
 
36
- @started ? log('Started') : log('Not started since running elsewhere')
36
+ @started ? log("Started") : log("Not started since running elsewhere")
37
37
  end
38
38
 
39
39
  def stop
@@ -41,7 +41,7 @@ module Sentry
41
41
  return unless @started
42
42
 
43
43
  StackProf.stop
44
- log('Stopped')
44
+ log("Stopped")
45
45
  end
46
46
 
47
47
  # Sets initial sampling decision of the profile.
@@ -54,14 +54,14 @@ module Sentry
54
54
 
55
55
  unless transaction_sampled
56
56
  @sampled = false
57
- log('Discarding profile because transaction not sampled')
57
+ log("Discarding profile because transaction not sampled")
58
58
  return
59
59
  end
60
60
 
61
61
  case @profiles_sample_rate
62
62
  when 0.0
63
63
  @sampled = false
64
- log('Discarding profile because sample_rate is 0')
64
+ log("Discarding profile because sample_rate is 0")
65
65
  return
66
66
  when 1.0
67
67
  @sampled = true
@@ -70,7 +70,7 @@ module Sentry
70
70
  @sampled = Random.rand < @profiles_sample_rate
71
71
  end
72
72
 
73
- log('Discarding profile due to sampling decision') unless @sampled
73
+ log("Discarding profile due to sampling decision") unless @sampled
74
74
  end
75
75
 
76
76
  def to_hash
@@ -90,13 +90,12 @@ module Sentry
90
90
 
91
91
  frame_map = {}
92
92
 
93
- frames = results[:frames].to_enum.with_index.map do |frame, idx|
94
- frame_id, frame_data = frame
95
-
93
+ frames = results[:frames].map.with_index do |(frame_id, frame_data), idx|
96
94
  # need to map over stackprof frame ids to ours
97
95
  frame_map[frame_id] = idx
98
96
 
99
97
  file_path = frame_data[:file]
98
+ lineno = frame_data[:line]
100
99
  in_app = in_app?(file_path)
101
100
  filename = compute_filename(file_path, in_app)
102
101
  function, mod = split_module(frame_data[:name])
@@ -109,7 +108,7 @@ module Sentry
109
108
  }
110
109
 
111
110
  frame_hash[:module] = mod if mod
112
- frame_hash[:lineno] = frame_data[:line] if frame_data[:line] && frame_data[:line] >= 0
111
+ frame_hash[:lineno] = lineno if lineno && lineno >= 0
113
112
 
114
113
  frame_hash
115
114
  end
@@ -130,7 +129,7 @@ module Sentry
130
129
  num_seen << results[:raw][idx + len]
131
130
  idx += len + 1
132
131
 
133
- log('Unknown frame in stack') if stack.size != len
132
+ log("Unknown frame in stack") if stack.size != len
134
133
  end
135
134
 
136
135
  idx = 0
@@ -155,16 +154,16 @@ module Sentry
155
154
  # Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
156
155
  # we're profiling is idle/sleeping/waiting for IO etc.
157
156
  # https://bugs.ruby-lang.org/issues/10602
158
- thread_id: '0',
157
+ thread_id: "0",
159
158
  elapsed_since_start_ns: elapsed_since_start_ns.to_s
160
159
  }
161
160
  end
162
161
  end
163
162
 
164
- log('Some samples thrown away') if samples.size != results[:samples]
163
+ log("Some samples thrown away") if samples.size != results[:samples]
165
164
 
166
165
  if samples.size <= MIN_SAMPLES_REQUIRED
167
- log('Not enough samples, discarding profiler')
166
+ log("Not enough samples, discarding profiler")
168
167
  record_lost_event(:insufficient_data)
169
168
  return {}
170
169
  end
@@ -219,7 +218,7 @@ module Sentry
219
218
 
220
219
  def split_module(name)
221
220
  # last module plus class/instance method
222
- i = name.rindex('::')
221
+ i = name.rindex("::")
223
222
  function = i ? name[(i + 2)..-1] : name
224
223
  mod = i ? name[0...i] : nil
225
224
 
@@ -227,7 +226,7 @@ module Sentry
227
226
  end
228
227
 
229
228
  def record_lost_event(reason)
230
- Sentry.get_current_client&.transport&.record_lost_event(reason, 'profile')
229
+ Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
231
230
  end
232
231
  end
233
232
  end
@@ -108,7 +108,7 @@ module Sentry
108
108
  end
109
109
 
110
110
  # Returns the Dynamic Sampling Context from the baggage.
111
- # @return [String, nil]
111
+ # @return [Hash, nil]
112
112
  def get_dynamic_sampling_context
113
113
  get_baggage&.dynamic_sampling_context
114
114
  end
@@ -50,7 +50,7 @@ module Sentry
50
50
  private
51
51
 
52
52
  def collect_exception(env)
53
- env['rack.exception'] || env['sinatra.error']
53
+ env["rack.exception"] || env["sinatra.error"]
54
54
  end
55
55
 
56
56
  def transaction_op
data/lib/sentry/rack.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack'
3
+ require "rack"
4
4
 
5
- require 'sentry/rack/capture_exceptions'
5
+ require "sentry/rack/capture_exceptions"
data/lib/sentry/rake.rb CHANGED
@@ -8,10 +8,10 @@ module Sentry
8
8
  module Application
9
9
  # @api private
10
10
  def display_error_message(ex)
11
- mechanism = Sentry::Mechanism.new(type: 'rake', handled: false)
11
+ mechanism = Sentry::Mechanism.new(type: "rake", handled: false)
12
12
 
13
13
  Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
14
- task_name = top_level_tasks.join(' ')
14
+ task_name = top_level_tasks.join(" ")
15
15
  scope.set_transaction_name(task_name, source: :task)
16
16
  scope.set_tag("rake_task", task_name)
17
17
  end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
@@ -13,12 +13,12 @@ module Sentry
13
13
 
14
14
  def detect_release_from_heroku(running_on_heroku)
15
15
  return unless running_on_heroku
16
- ENV['HEROKU_SLUG_COMMIT']
16
+ ENV["HEROKU_SLUG_COMMIT"]
17
17
  end
18
18
 
19
19
  def detect_release_from_capistrano(project_root)
20
- revision_file = File.join(project_root, 'REVISION')
21
- revision_log = File.join(project_root, '..', 'revisions.log')
20
+ revision_file = File.join(project_root, "REVISION")
21
+ revision_log = File.join(project_root, "..", "revisions.log")
22
22
 
23
23
  if File.exist?(revision_file)
24
24
  File.read(revision_file).strip
@@ -32,7 +32,7 @@ module Sentry
32
32
  end
33
33
 
34
34
  def detect_release_from_env
35
- ENV['SENTRY_RELEASE']
35
+ ENV["SENTRY_RELEASE"]
36
36
  end
37
37
  end
38
38
  end
data/lib/sentry/scope.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "sentry/breadcrumb_buffer"
4
4
  require "sentry/propagation_context"
5
+ require "sentry/attachment"
5
6
  require "etc"
6
7
 
7
8
  module Sentry
@@ -22,6 +23,7 @@ module Sentry
22
23
  :rack_env,
23
24
  :span,
24
25
  :session,
26
+ :attachments,
25
27
  :propagation_context
26
28
  ]
27
29
 
@@ -55,10 +57,12 @@ module Sentry
55
57
  event.level = level
56
58
  event.breadcrumbs = breadcrumbs
57
59
  event.rack_env = rack_env if rack_env
60
+ event.attachments = attachments
58
61
  end
59
62
 
60
63
  if span
61
64
  event.contexts[:trace] ||= span.get_trace_context
65
+ event.dynamic_sampling_context ||= span.get_dynamic_sampling_context
62
66
  else
63
67
  event.contexts[:trace] ||= propagation_context.get_trace_context
64
68
  event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
@@ -102,6 +106,7 @@ module Sentry
102
106
  copy.span = span.deep_dup
103
107
  copy.session = session.deep_dup
104
108
  copy.propagation_context = propagation_context.deep_dup
109
+ copy.attachments = attachments.dup
105
110
  copy
106
111
  end
107
112
 
@@ -119,6 +124,7 @@ module Sentry
119
124
  self.fingerprint = scope.fingerprint
120
125
  self.span = scope.span
121
126
  self.propagation_context = scope.propagation_context
127
+ self.attachments = scope.attachments
122
128
  end
123
129
 
124
130
  # Updates the scope's data from the given options.
@@ -128,6 +134,7 @@ module Sentry
128
134
  # @param user [Hash]
129
135
  # @param level [String, Symbol]
130
136
  # @param fingerprint [Array]
137
+ # @param attachments [Array<Attachment>]
131
138
  # @return [Array]
132
139
  def update_from_options(
133
140
  contexts: nil,
@@ -136,6 +143,7 @@ module Sentry
136
143
  user: nil,
137
144
  level: nil,
138
145
  fingerprint: nil,
146
+ attachments: nil,
139
147
  **options
140
148
  )
141
149
  self.contexts.merge!(contexts) if contexts
@@ -283,6 +291,12 @@ module Sentry
283
291
  @propagation_context = PropagationContext.new(self, env)
284
292
  end
285
293
 
294
+ # Add a new attachment to the scope.
295
+ def add_attachment(**opts)
296
+ attachments << (attachment = Attachment.new(**opts))
297
+ attachment
298
+ end
299
+
286
300
  protected
287
301
 
288
302
  # for duplicating scopes internally
@@ -303,6 +317,7 @@ module Sentry
303
317
  @rack_env = {}
304
318
  @span = nil
305
319
  @session = nil
320
+ @attachments = []
306
321
  generate_propagation_context
307
322
  set_new_breadcrumb_buffer
308
323
  end
@@ -44,7 +44,7 @@ module Sentry
44
44
  def pending_envelope
45
45
  envelope = Envelope.new
46
46
 
47
- header = { type: 'sessions' }
47
+ header = { type: "sessions" }
48
48
  payload = { attrs: attrs, aggregates: @pending_aggregates.values }
49
49
 
50
50
  envelope.add_item(header, payload)
data/lib/sentry/span.rb CHANGED
@@ -160,6 +160,12 @@ module Sentry
160
160
  transaction.get_baggage&.serialize
161
161
  end
162
162
 
163
+ # Returns the Dynamic Sampling Context from the transaction baggage.
164
+ # @return [Hash, nil]
165
+ def get_dynamic_sampling_context
166
+ transaction.get_baggage&.dynamic_sampling_context
167
+ end
168
+
163
169
  # @return [Hash]
164
170
  def to_hash
165
171
  hash = {
@@ -192,7 +198,8 @@ module Sentry
192
198
  description: @description,
193
199
  op: @op,
194
200
  status: @status,
195
- origin: @origin
201
+ origin: @origin,
202
+ data: @data
196
203
  }
197
204
  end
198
205
 
@@ -1,6 +1,6 @@
1
1
  module Sentry
2
2
  module TestHelper
3
- DUMMY_DSN = 'http://12345:67890@sentry.localdomain/sentry/42'
3
+ DUMMY_DSN = "http://12345:67890@sentry.localdomain/sentry/42"
4
4
 
5
5
  # Alters the existing SDK configuration with test-suitable options. Mainly:
6
6
  # - Sets a dummy DSN instead of `nil` or an actual DSN.