sentry-ruby 5.19.0 → 5.21.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Rakefile +2 -0
  4. data/bin/console +1 -0
  5. data/lib/sentry/attachment.rb +3 -5
  6. data/lib/sentry/backtrace.rb +3 -5
  7. data/lib/sentry/baggage.rb +7 -7
  8. data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
  9. data/lib/sentry/check_in_event.rb +4 -4
  10. data/lib/sentry/client.rb +9 -9
  11. data/lib/sentry/configuration.rb +52 -19
  12. data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
  13. data/lib/sentry/cron/monitor_check_ins.rb +3 -1
  14. data/lib/sentry/cron/monitor_config.rb +1 -1
  15. data/lib/sentry/dsn.rb +3 -3
  16. data/lib/sentry/envelope/item.rb +88 -0
  17. data/lib/sentry/envelope.rb +2 -85
  18. data/lib/sentry/event.rb +7 -7
  19. data/lib/sentry/graphql.rb +1 -1
  20. data/lib/sentry/hub.rb +8 -1
  21. data/lib/sentry/interfaces/mechanism.rb +1 -1
  22. data/lib/sentry/interfaces/request.rb +5 -5
  23. data/lib/sentry/interfaces/single_exception.rb +3 -3
  24. data/lib/sentry/interfaces/stacktrace.rb +3 -1
  25. data/lib/sentry/interfaces/stacktrace_builder.rb +15 -2
  26. data/lib/sentry/logger.rb +1 -1
  27. data/lib/sentry/metrics/aggregator.rb +12 -12
  28. data/lib/sentry/metrics/set_metric.rb +2 -2
  29. data/lib/sentry/metrics.rb +15 -15
  30. data/lib/sentry/net/http.rb +1 -1
  31. data/lib/sentry/profiler/helpers.rb +46 -0
  32. data/lib/sentry/profiler.rb +25 -56
  33. data/lib/sentry/propagation_context.rb +1 -1
  34. data/lib/sentry/rack/capture_exceptions.rb +2 -2
  35. data/lib/sentry/rack.rb +2 -2
  36. data/lib/sentry/rake.rb +2 -2
  37. data/lib/sentry/release_detector.rb +4 -4
  38. data/lib/sentry/scope.rb +1 -0
  39. data/lib/sentry/session_flusher.rb +1 -1
  40. data/lib/sentry/span.rb +6 -0
  41. data/lib/sentry/test_helper.rb +3 -1
  42. data/lib/sentry/transaction.rb +4 -4
  43. data/lib/sentry/transaction_event.rb +1 -2
  44. data/lib/sentry/transport/http_transport.rb +12 -12
  45. data/lib/sentry/transport.rb +4 -4
  46. data/lib/sentry/utils/env_helper.rb +21 -0
  47. data/lib/sentry/utils/real_ip.rb +1 -1
  48. data/lib/sentry/vernier/output.rb +89 -0
  49. data/lib/sentry/vernier/profiler.rb +125 -0
  50. data/lib/sentry/version.rb +1 -1
  51. data/lib/sentry-ruby.rb +5 -4
  52. data/sentry-ruby-core.gemspec +3 -1
  53. data/sentry-ruby.gemspec +3 -1
  54. metadata +13 -8
data/lib/sentry/event.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'socket'
4
- require 'securerandom'
5
- require 'sentry/interface'
6
- require 'sentry/backtrace'
7
- require 'sentry/utils/real_ip'
8
- require 'sentry/utils/request_id'
9
- require 'sentry/utils/custom_inspection'
3
+ require "socket"
4
+ require "securerandom"
5
+ require "sentry/interface"
6
+ require "sentry/backtrace"
7
+ require "sentry/utils/real_ip"
8
+ require "sentry/utils/request_id"
9
+ require "sentry/utils/custom_inspection"
10
10
 
11
11
  module Sentry
12
12
  # This is an abstract class that defines the shared attributes of an event.
@@ -4,6 +4,6 @@ Sentry.register_patch(:graphql) do |config|
4
4
  if defined?(::GraphQL::Schema) && defined?(::GraphQL::Tracing::SentryTrace) && ::GraphQL::Schema.respond_to?(:trace_with)
5
5
  ::GraphQL::Schema.trace_with(::GraphQL::Tracing::SentryTrace, set_transaction_name: true)
6
6
  else
7
- config.logger.warn(Sentry::LOGGER_PROGNAME) { 'You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile.' }
7
+ config.logger.warn(Sentry::LOGGER_PROGNAME) { "You tried to enable the GraphQL integration but no GraphQL gem was detected. Make sure you have the `graphql` gem (>= 2.2.6) in your Gemfile." }
8
8
  end
9
9
  end
data/lib/sentry/hub.rb CHANGED
@@ -73,7 +73,13 @@ module Sentry
73
73
  end
74
74
 
75
75
  def pop_scope
76
- @stack.pop
76
+ if @stack.size > 1
77
+ @stack.pop
78
+ else
79
+ # We never want to enter a situation where we have no scope and no client
80
+ client = current_client
81
+ @stack = [Layer.new(client, Scope.new)]
82
+ end
77
83
  end
78
84
 
79
85
  def start_transaction(transaction: nil, custom_sampling_context: {}, instrumenter: :sentry, **options)
@@ -214,6 +220,7 @@ module Sentry
214
220
  end
215
221
 
216
222
  def add_breadcrumb(breadcrumb, hint: {})
223
+ return unless current_client
217
224
  return unless configuration.enabled_in_current_env?
218
225
 
219
226
  if before_breadcrumb = current_client.configuration.before_breadcrumb
@@ -12,7 +12,7 @@ module Sentry
12
12
  # @return [Boolean]
13
13
  attr_accessor :handled
14
14
 
15
- def initialize(type: 'generic', handled: true)
15
+ def initialize(type: "generic", handled: true)
16
16
  @type = type
17
17
  @handled = handled
18
18
  end
@@ -59,7 +59,7 @@ module Sentry
59
59
  self.query_string = request.query_string
60
60
  end
61
61
 
62
- self.url = request.scheme && request.url.split('?').first
62
+ self.url = request.scheme && request.url.split("?").first
63
63
  self.method = request.request_method
64
64
 
65
65
  self.headers = filter_and_format_headers(env, send_default_pii)
@@ -85,14 +85,14 @@ module Sentry
85
85
  env.each_with_object({}) do |(key, value), memo|
86
86
  begin
87
87
  key = key.to_s # rack env can contain symbols
88
- next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
88
+ next memo["X-Request-Id"] ||= Utils::RequestId.read_from(env) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
89
89
  next if is_server_protocol?(key, value, env["SERVER_PROTOCOL"])
90
90
  next if is_skippable_header?(key)
91
91
  next if key == "HTTP_AUTHORIZATION" && !send_default_pii
92
92
 
93
93
  # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
94
94
  key = key.sub(/^HTTP_/, "")
95
- key = key.split('_').map(&:capitalize).join('-')
95
+ key = key.split("_").map(&:capitalize).join("-")
96
96
 
97
97
  memo[key] = Utils::EncodingHelper.encode_to_utf_8(value.to_s)
98
98
  rescue StandardError => e
@@ -108,7 +108,7 @@ module Sentry
108
108
  def is_skippable_header?(key)
109
109
  key.upcase != key || # lower-case envs aren't real http headers
110
110
  key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
111
- !(key.start_with?('HTTP_') || CONTENT_HEADERS.include?(key))
111
+ !(key.start_with?("HTTP_") || CONTENT_HEADERS.include?(key))
112
112
  end
113
113
 
114
114
  # In versions < 3, Rack adds in an incorrect HTTP_VERSION key, which causes downstream
@@ -120,7 +120,7 @@ module Sentry
120
120
  rack_version = Gem::Version.new(::Rack.release)
121
121
  return false if rack_version >= Gem::Version.new("3.0")
122
122
 
123
- key == 'HTTP_VERSION' && value == protocol_version
123
+ key == "HTTP_VERSION" && value == protocol_version
124
124
  end
125
125
 
126
126
  def filter_and_format_env(env, rack_env_whitelist)
@@ -7,8 +7,8 @@ module Sentry
7
7
  include CustomInspection
8
8
 
9
9
  SKIP_INSPECTION_ATTRIBUTES = [:@stacktrace]
10
- PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]".freeze
11
- OMISSION_MARK = "...".freeze
10
+ PROBLEMATIC_LOCAL_VALUE_REPLACEMENT = "[ignored due to error]"
11
+ OMISSION_MARK = "..."
12
12
  MAX_LOCAL_BYTES = 1024
13
13
 
14
14
  attr_reader :type, :module, :thread_id, :stacktrace, :mechanism
@@ -26,7 +26,7 @@ module Sentry
26
26
 
27
27
  @value = Utils::EncodingHelper.encode_to_utf_8(exception_message.byteslice(0..Event::MAX_MESSAGE_SIZE_IN_BYTES))
28
28
 
29
- @module = exception.class.to_s.split('::')[0...-1].join('::')
29
+ @module = exception.class.to_s.split("::")[0...-1].join("::")
30
30
  @thread_id = Thread.current.object_id
31
31
  @stacktrace = stacktrace
32
32
  @mechanism = mechanism
@@ -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
@@ -66,7 +66,7 @@ module Sentry
66
66
  # IPv6 url could look like '::1/path', and that won't parse without
67
67
  # wrapping it in square brackets.
68
68
  hostname = address =~ Resolv::IPv6::Regex ? "[#{address}]" : address
69
- 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}"))
70
70
  url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s
71
71
 
72
72
  result = { method: req.method, url: url }
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Sentry
6
+ class Profiler
7
+ module Helpers
8
+ def in_app?(abs_path)
9
+ abs_path.match?(@in_app_pattern)
10
+ end
11
+
12
+ # copied from stacktrace.rb since I don't want to touch existing code
13
+ # TODO-neel-profiler try to fetch this from stackprof once we patch
14
+ # the native extension
15
+ def compute_filename(abs_path, in_app)
16
+ return nil if abs_path.nil?
17
+
18
+ under_project_root = @project_root && abs_path.start_with?(@project_root)
19
+
20
+ prefix =
21
+ if under_project_root && in_app
22
+ @project_root
23
+ else
24
+ longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
25
+
26
+ if under_project_root
27
+ longest_load_path || @project_root
28
+ else
29
+ longest_load_path
30
+ end
31
+ end
32
+
33
+ prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
34
+ end
35
+
36
+ def split_module(name)
37
+ # last module plus class/instance method
38
+ i = name.rindex("::")
39
+ function = i ? name[(i + 2)..-1] : name
40
+ mod = i ? name[0...i] : nil
41
+
42
+ [function, mod]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
3
+ require "securerandom"
4
+ require_relative "profiler/helpers"
4
5
 
5
6
  module Sentry
6
7
  class Profiler
7
- VERSION = '1'
8
- PLATFORM = 'ruby'
8
+ include Profiler::Helpers
9
+
10
+ VERSION = "1"
11
+ PLATFORM = "ruby"
9
12
  # 101 Hz in microseconds
10
13
  DEFAULT_INTERVAL = 1e6 / 101
11
14
  MICRO_TO_NANO_SECONDS = 1e3
@@ -14,14 +17,14 @@ module Sentry
14
17
  attr_reader :sampled, :started, :event_id
15
18
 
16
19
  def initialize(configuration)
17
- @event_id = SecureRandom.uuid.delete('-')
20
+ @event_id = SecureRandom.uuid.delete("-")
18
21
  @started = false
19
22
  @sampled = nil
20
23
 
21
24
  @profiling_enabled = defined?(StackProf) && configuration.profiling_enabled?
22
25
  @profiles_sample_rate = configuration.profiles_sample_rate
23
26
  @project_root = configuration.project_root
24
- @app_dirs_pattern = configuration.app_dirs_pattern || Backtrace::APP_DIRS_PATTERN
27
+ @app_dirs_pattern = configuration.app_dirs_pattern
25
28
  @in_app_pattern = Regexp.new("^(#{@project_root}/)?#{@app_dirs_pattern}")
26
29
  end
27
30
 
@@ -33,7 +36,7 @@ module Sentry
33
36
  raw: true,
34
37
  aggregate: false)
35
38
 
36
- @started ? log('Started') : log('Not started since running elsewhere')
39
+ @started ? log("Started") : log("Not started since running elsewhere")
37
40
  end
38
41
 
39
42
  def stop
@@ -41,7 +44,11 @@ module Sentry
41
44
  return unless @started
42
45
 
43
46
  StackProf.stop
44
- log('Stopped')
47
+ log("Stopped")
48
+ end
49
+
50
+ def active_thread_id
51
+ "0"
45
52
  end
46
53
 
47
54
  # Sets initial sampling decision of the profile.
@@ -54,14 +61,14 @@ module Sentry
54
61
 
55
62
  unless transaction_sampled
56
63
  @sampled = false
57
- log('Discarding profile because transaction not sampled')
64
+ log("Discarding profile because transaction not sampled")
58
65
  return
59
66
  end
60
67
 
61
68
  case @profiles_sample_rate
62
69
  when 0.0
63
70
  @sampled = false
64
- log('Discarding profile because sample_rate is 0')
71
+ log("Discarding profile because sample_rate is 0")
65
72
  return
66
73
  when 1.0
67
74
  @sampled = true
@@ -70,7 +77,7 @@ module Sentry
70
77
  @sampled = Random.rand < @profiles_sample_rate
71
78
  end
72
79
 
73
- log('Discarding profile due to sampling decision') unless @sampled
80
+ log("Discarding profile due to sampling decision") unless @sampled
74
81
  end
75
82
 
76
83
  def to_hash
@@ -90,13 +97,12 @@ module Sentry
90
97
 
91
98
  frame_map = {}
92
99
 
93
- frames = results[:frames].to_enum.with_index.map do |frame, idx|
94
- frame_id, frame_data = frame
95
-
100
+ frames = results[:frames].map.with_index do |(frame_id, frame_data), idx|
96
101
  # need to map over stackprof frame ids to ours
97
102
  frame_map[frame_id] = idx
98
103
 
99
104
  file_path = frame_data[:file]
105
+ lineno = frame_data[:line]
100
106
  in_app = in_app?(file_path)
101
107
  filename = compute_filename(file_path, in_app)
102
108
  function, mod = split_module(frame_data[:name])
@@ -109,7 +115,7 @@ module Sentry
109
115
  }
110
116
 
111
117
  frame_hash[:module] = mod if mod
112
- frame_hash[:lineno] = frame_data[:line] if frame_data[:line] && frame_data[:line] >= 0
118
+ frame_hash[:lineno] = lineno if lineno && lineno >= 0
113
119
 
114
120
  frame_hash
115
121
  end
@@ -130,7 +136,7 @@ module Sentry
130
136
  num_seen << results[:raw][idx + len]
131
137
  idx += len + 1
132
138
 
133
- log('Unknown frame in stack') if stack.size != len
139
+ log("Unknown frame in stack") if stack.size != len
134
140
  end
135
141
 
136
142
  idx = 0
@@ -155,16 +161,16 @@ module Sentry
155
161
  # Till then, on multi-threaded servers like puma, we will get frames from other active threads when the one
156
162
  # we're profiling is idle/sleeping/waiting for IO etc.
157
163
  # https://bugs.ruby-lang.org/issues/10602
158
- thread_id: '0',
164
+ thread_id: "0",
159
165
  elapsed_since_start_ns: elapsed_since_start_ns.to_s
160
166
  }
161
167
  end
162
168
  end
163
169
 
164
- log('Some samples thrown away') if samples.size != results[:samples]
170
+ log("Some samples thrown away") if samples.size != results[:samples]
165
171
 
166
172
  if samples.size <= MIN_SAMPLES_REQUIRED
167
- log('Not enough samples, discarding profiler')
173
+ log("Not enough samples, discarding profiler")
168
174
  record_lost_event(:insufficient_data)
169
175
  return {}
170
176
  end
@@ -189,45 +195,8 @@ module Sentry
189
195
  Sentry.logger.debug(LOGGER_PROGNAME) { "[Profiler] #{message}" }
190
196
  end
191
197
 
192
- def in_app?(abs_path)
193
- abs_path.match?(@in_app_pattern)
194
- end
195
-
196
- # copied from stacktrace.rb since I don't want to touch existing code
197
- # TODO-neel-profiler try to fetch this from stackprof once we patch
198
- # the native extension
199
- def compute_filename(abs_path, in_app)
200
- return nil if abs_path.nil?
201
-
202
- under_project_root = @project_root && abs_path.start_with?(@project_root)
203
-
204
- prefix =
205
- if under_project_root && in_app
206
- @project_root
207
- else
208
- longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size)
209
-
210
- if under_project_root
211
- longest_load_path || @project_root
212
- else
213
- longest_load_path
214
- end
215
- end
216
-
217
- prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path
218
- end
219
-
220
- def split_module(name)
221
- # last module plus class/instance method
222
- i = name.rindex('::')
223
- function = i ? name[(i + 2)..-1] : name
224
- mod = i ? name[0...i] : nil
225
-
226
- [function, mod]
227
- end
228
-
229
198
  def record_lost_event(reason)
230
- Sentry.get_current_client&.transport&.record_lost_event(reason, 'profile')
199
+ Sentry.get_current_client&.transport&.record_lost_event(reason, "profile")
231
200
  end
232
201
  end
233
202
  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,11 +50,11 @@ 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
57
- "http.server".freeze
57
+ "http.server"
58
58
  end
59
59
 
60
60
  def capture_exception(exception, env)
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