sentry-ruby 5.10.0 → 5.26.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -1
  3. data/Gemfile +12 -13
  4. data/README.md +26 -11
  5. data/Rakefile +9 -11
  6. data/bin/console +2 -0
  7. data/lib/sentry/attachment.rb +40 -0
  8. data/lib/sentry/background_worker.rb +11 -5
  9. data/lib/sentry/backpressure_monitor.rb +45 -0
  10. data/lib/sentry/backtrace.rb +12 -9
  11. data/lib/sentry/baggage.rb +7 -7
  12. data/lib/sentry/breadcrumb/sentry_logger.rb +6 -6
  13. data/lib/sentry/breadcrumb.rb +13 -6
  14. data/lib/sentry/check_in_event.rb +61 -0
  15. data/lib/sentry/client.rb +214 -25
  16. data/lib/sentry/configuration.rb +221 -38
  17. data/lib/sentry/core_ext/object/deep_dup.rb +1 -1
  18. data/lib/sentry/cron/configuration.rb +23 -0
  19. data/lib/sentry/cron/monitor_check_ins.rb +77 -0
  20. data/lib/sentry/cron/monitor_config.rb +53 -0
  21. data/lib/sentry/cron/monitor_schedule.rb +42 -0
  22. data/lib/sentry/dsn.rb +4 -4
  23. data/lib/sentry/envelope/item.rb +88 -0
  24. data/lib/sentry/envelope.rb +2 -68
  25. data/lib/sentry/error_event.rb +2 -2
  26. data/lib/sentry/event.rb +28 -47
  27. data/lib/sentry/excon/middleware.rb +77 -0
  28. data/lib/sentry/excon.rb +10 -0
  29. data/lib/sentry/faraday.rb +77 -0
  30. data/lib/sentry/graphql.rb +9 -0
  31. data/lib/sentry/hub.rb +138 -6
  32. data/lib/sentry/integrable.rb +10 -0
  33. data/lib/sentry/interface.rb +1 -0
  34. data/lib/sentry/interfaces/exception.rb +5 -3
  35. data/lib/sentry/interfaces/mechanism.rb +20 -0
  36. data/lib/sentry/interfaces/request.rb +8 -8
  37. data/lib/sentry/interfaces/single_exception.rb +13 -9
  38. data/lib/sentry/interfaces/stacktrace.rb +3 -1
  39. data/lib/sentry/interfaces/stacktrace_builder.rb +23 -2
  40. data/lib/sentry/linecache.rb +3 -3
  41. data/lib/sentry/log_event.rb +206 -0
  42. data/lib/sentry/log_event_buffer.rb +75 -0
  43. data/lib/sentry/logger.rb +1 -1
  44. data/lib/sentry/metrics/aggregator.rb +248 -0
  45. data/lib/sentry/metrics/configuration.rb +47 -0
  46. data/lib/sentry/metrics/counter_metric.rb +25 -0
  47. data/lib/sentry/metrics/distribution_metric.rb +25 -0
  48. data/lib/sentry/metrics/gauge_metric.rb +35 -0
  49. data/lib/sentry/metrics/local_aggregator.rb +53 -0
  50. data/lib/sentry/metrics/metric.rb +19 -0
  51. data/lib/sentry/metrics/set_metric.rb +28 -0
  52. data/lib/sentry/metrics/timing.rb +51 -0
  53. data/lib/sentry/metrics.rb +56 -0
  54. data/lib/sentry/net/http.rb +27 -44
  55. data/lib/sentry/profiler/helpers.rb +46 -0
  56. data/lib/sentry/profiler.rb +41 -60
  57. data/lib/sentry/propagation_context.rb +135 -0
  58. data/lib/sentry/puma.rb +12 -5
  59. data/lib/sentry/rack/capture_exceptions.rb +17 -8
  60. data/lib/sentry/rack.rb +2 -2
  61. data/lib/sentry/rake.rb +4 -15
  62. data/lib/sentry/redis.rb +10 -4
  63. data/lib/sentry/release_detector.rb +5 -5
  64. data/lib/sentry/rspec.rb +91 -0
  65. data/lib/sentry/scope.rb +75 -39
  66. data/lib/sentry/session.rb +2 -2
  67. data/lib/sentry/session_flusher.rb +15 -43
  68. data/lib/sentry/span.rb +92 -8
  69. data/lib/sentry/std_lib_logger.rb +50 -0
  70. data/lib/sentry/structured_logger.rb +138 -0
  71. data/lib/sentry/test_helper.rb +42 -13
  72. data/lib/sentry/threaded_periodic_worker.rb +39 -0
  73. data/lib/sentry/transaction.rb +44 -43
  74. data/lib/sentry/transaction_event.rb +10 -6
  75. data/lib/sentry/transport/configuration.rb +73 -1
  76. data/lib/sentry/transport/http_transport.rb +71 -41
  77. data/lib/sentry/transport/spotlight_transport.rb +50 -0
  78. data/lib/sentry/transport.rb +53 -49
  79. data/lib/sentry/utils/argument_checking_helper.rb +12 -0
  80. data/lib/sentry/utils/env_helper.rb +21 -0
  81. data/lib/sentry/utils/http_tracing.rb +74 -0
  82. data/lib/sentry/utils/logging_helper.rb +10 -7
  83. data/lib/sentry/utils/real_ip.rb +2 -2
  84. data/lib/sentry/utils/request_id.rb +1 -1
  85. data/lib/sentry/utils/uuid.rb +13 -0
  86. data/lib/sentry/vernier/output.rb +89 -0
  87. data/lib/sentry/vernier/profiler.rb +132 -0
  88. data/lib/sentry/version.rb +1 -1
  89. data/lib/sentry-ruby.rb +206 -35
  90. data/sentry-ruby-core.gemspec +3 -1
  91. data/sentry-ruby.gemspec +15 -6
  92. metadata +61 -11
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "sentry/baggage"
5
+ require "sentry/utils/uuid"
6
+
7
+ module Sentry
8
+ class PropagationContext
9
+ SENTRY_TRACE_REGEXP = Regexp.new(
10
+ "^[ \t]*" + # whitespace
11
+ "([0-9a-f]{32})?" + # trace_id
12
+ "-?([0-9a-f]{16})?" + # span_id
13
+ "-?([01])?" + # sampled
14
+ "[ \t]*$" # whitespace
15
+ )
16
+
17
+ # An uuid that can be used to identify a trace.
18
+ # @return [String]
19
+ attr_reader :trace_id
20
+ # An uuid that can be used to identify the span.
21
+ # @return [String]
22
+ attr_reader :span_id
23
+ # Span parent's span_id.
24
+ # @return [String, nil]
25
+ attr_reader :parent_span_id
26
+ # The sampling decision of the parent transaction.
27
+ # @return [Boolean, nil]
28
+ attr_reader :parent_sampled
29
+ # Is there an incoming trace or not?
30
+ # @return [Boolean]
31
+ attr_reader :incoming_trace
32
+ # This is only for accessing the current baggage variable.
33
+ # Please use the #get_baggage method for interfacing outside this class.
34
+ # @return [Baggage, nil]
35
+ attr_reader :baggage
36
+
37
+ def initialize(scope, env = nil)
38
+ @scope = scope
39
+ @parent_span_id = nil
40
+ @parent_sampled = nil
41
+ @baggage = nil
42
+ @incoming_trace = false
43
+
44
+ if env
45
+ sentry_trace_header = env["HTTP_SENTRY_TRACE"] || env[SENTRY_TRACE_HEADER_NAME]
46
+ baggage_header = env["HTTP_BAGGAGE"] || env[BAGGAGE_HEADER_NAME]
47
+
48
+ if sentry_trace_header
49
+ sentry_trace_data = self.class.extract_sentry_trace(sentry_trace_header)
50
+
51
+ if sentry_trace_data
52
+ @trace_id, @parent_span_id, @parent_sampled = sentry_trace_data
53
+
54
+ @baggage =
55
+ if baggage_header && !baggage_header.empty?
56
+ Baggage.from_incoming_header(baggage_header)
57
+ else
58
+ # If there's an incoming sentry-trace but no incoming baggage header,
59
+ # for instance in traces coming from older SDKs,
60
+ # baggage will be empty and frozen and won't be populated as head SDK.
61
+ Baggage.new({})
62
+ end
63
+
64
+ @baggage.freeze!
65
+ @incoming_trace = true
66
+ end
67
+ end
68
+ end
69
+
70
+ @trace_id ||= Utils.uuid
71
+ @span_id = Utils.uuid.slice(0, 16)
72
+ end
73
+
74
+ # Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header.
75
+ #
76
+ # @param sentry_trace [String] the sentry-trace header value from the previous transaction.
77
+ # @return [Array, nil]
78
+ def self.extract_sentry_trace(sentry_trace)
79
+ match = SENTRY_TRACE_REGEXP.match(sentry_trace)
80
+ return nil if match.nil?
81
+
82
+ trace_id, parent_span_id, sampled_flag = match[1..3]
83
+ parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0"
84
+
85
+ [trace_id, parent_span_id, parent_sampled]
86
+ end
87
+
88
+ # Returns the trace context that can be used to embed in an Event.
89
+ # @return [Hash]
90
+ def get_trace_context
91
+ {
92
+ trace_id: trace_id,
93
+ span_id: span_id,
94
+ parent_span_id: parent_span_id
95
+ }
96
+ end
97
+
98
+ # Returns the sentry-trace header from the propagation context.
99
+ # @return [String]
100
+ def get_traceparent
101
+ "#{trace_id}-#{span_id}"
102
+ end
103
+
104
+ # Returns the Baggage from the propagation context or populates as head SDK if empty.
105
+ # @return [Baggage, nil]
106
+ def get_baggage
107
+ populate_head_baggage if @baggage.nil? || @baggage.mutable
108
+ @baggage
109
+ end
110
+
111
+ # Returns the Dynamic Sampling Context from the baggage.
112
+ # @return [Hash, nil]
113
+ def get_dynamic_sampling_context
114
+ get_baggage&.dynamic_sampling_context
115
+ end
116
+
117
+ private
118
+
119
+ def populate_head_baggage
120
+ return unless Sentry.initialized?
121
+
122
+ configuration = Sentry.configuration
123
+
124
+ items = {
125
+ "trace_id" => trace_id,
126
+ "environment" => configuration.environment,
127
+ "release" => configuration.release,
128
+ "public_key" => configuration.dsn&.public_key
129
+ }
130
+
131
+ items.compact!
132
+ @baggage = Baggage.new(items, mutable: false)
133
+ end
134
+ end
135
+ end
data/lib/sentry/puma.rb CHANGED
@@ -1,10 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ return unless defined?(Puma::Server)
4
+
3
5
  module Sentry
4
6
  module Puma
5
7
  module Server
6
- def lowlevel_error(e, env, status=500)
7
- result = super
8
+ PUMA_4_AND_PRIOR = Gem::Version.new(::Puma::Const::PUMA_VERSION) < Gem::Version.new("5.0.0")
9
+
10
+ def lowlevel_error(e, env, status = 500)
11
+ result =
12
+ if PUMA_4_AND_PRIOR
13
+ super(e, env)
14
+ else
15
+ super
16
+ end
8
17
 
9
18
  begin
10
19
  Sentry.capture_exception(e) do |scope|
@@ -20,6 +29,4 @@ module Sentry
20
29
  end
21
30
  end
22
31
 
23
- if defined?(Puma::Server)
24
- Sentry.register_patch(Sentry::Puma::Server, Puma::Server)
25
- end
32
+ Sentry.register_patch(:puma, Sentry::Puma::Server, Puma::Server)
@@ -4,6 +4,8 @@ module Sentry
4
4
  module Rack
5
5
  class CaptureExceptions
6
6
  ERROR_EVENT_ID_KEY = "sentry.error_event_id"
7
+ MECHANISM_TYPE = "rack"
8
+ SPAN_ORIGIN = "auto.http.rack"
7
9
 
8
10
  def initialize(app)
9
11
  @app = app
@@ -48,25 +50,28 @@ module Sentry
48
50
  private
49
51
 
50
52
  def collect_exception(env)
51
- env['rack.exception'] || env['sinatra.error']
53
+ env["rack.exception"] || env["sinatra.error"]
52
54
  end
53
55
 
54
56
  def transaction_op
55
- "http.server".freeze
57
+ "http.server"
56
58
  end
57
59
 
58
60
  def capture_exception(exception, env)
59
- Sentry.capture_exception(exception).tap do |event|
61
+ Sentry.capture_exception(exception, hint: { mechanism: mechanism }).tap do |event|
60
62
  env[ERROR_EVENT_ID_KEY] = event.event_id if event
61
63
  end
62
64
  end
63
65
 
64
66
  def start_transaction(env, scope)
65
- sentry_trace = env["HTTP_SENTRY_TRACE"]
66
- baggage = env["HTTP_BAGGAGE"]
67
-
68
- options = { name: scope.transaction_name, source: scope.transaction_source, op: transaction_op }
69
- transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, baggage: baggage, **options) if sentry_trace
67
+ options = {
68
+ name: scope.transaction_name,
69
+ source: scope.transaction_source,
70
+ op: transaction_op,
71
+ origin: SPAN_ORIGIN
72
+ }
73
+
74
+ transaction = Sentry.continue_trace(env, **options)
70
75
  Sentry.start_transaction(transaction: transaction, custom_sampling_context: { env: env }, **options)
71
76
  end
72
77
 
@@ -77,6 +82,10 @@ module Sentry
77
82
  transaction.set_http_status(status_code)
78
83
  transaction.finish
79
84
  end
85
+
86
+ def mechanism
87
+ Sentry::Mechanism.new(type: MECHANISM_TYPE, handled: false)
88
+ end
80
89
  end
81
90
  end
82
91
  end
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,8 +8,10 @@ module Sentry
8
8
  module Application
9
9
  # @api private
10
10
  def display_error_message(ex)
11
- Sentry.capture_exception(ex) do |scope|
12
- task_name = top_level_tasks.join(' ')
11
+ mechanism = Sentry::Mechanism.new(type: "rake", handled: false)
12
+
13
+ Sentry.capture_exception(ex, hint: { mechanism: mechanism }) do |scope|
14
+ task_name = top_level_tasks.join(" ")
13
15
  scope.set_transaction_name(task_name, source: :task)
14
16
  scope.set_tag("rake_task", task_name)
15
17
  end if Sentry.initialized? && !Sentry.configuration.skip_rake_integration
@@ -17,15 +19,6 @@ module Sentry
17
19
  super
18
20
  end
19
21
  end
20
-
21
- module Task
22
- # @api private
23
- def execute(args=nil)
24
- return super unless Sentry.initialized? && Sentry.get_current_hub
25
-
26
- super
27
- end
28
- end
29
22
  end
30
23
  end
31
24
 
@@ -34,8 +27,4 @@ module Rake
34
27
  class Application
35
28
  prepend(Sentry::Rake::Application)
36
29
  end
37
-
38
- class Task
39
- prepend(Sentry::Rake::Task)
40
- end
41
30
  end
data/lib/sentry/redis.rb CHANGED
@@ -4,6 +4,7 @@ module Sentry
4
4
  # @api private
5
5
  class Redis
6
6
  OP_NAME = "db.redis"
7
+ SPAN_ORIGIN = "auto.db.redis"
7
8
  LOGGER_NAME = :redis_logger
8
9
 
9
10
  def initialize(commands, host, port, db)
@@ -13,13 +14,16 @@ module Sentry
13
14
  def instrument
14
15
  return yield unless Sentry.initialized?
15
16
 
16
- Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f) do |span|
17
+ Sentry.with_child_span(op: OP_NAME, start_timestamp: Sentry.utc_now.to_f, origin: SPAN_ORIGIN) do |span|
17
18
  yield.tap do
18
19
  record_breadcrumb
19
20
 
20
21
  if span
21
22
  span.set_description(commands_description)
22
- span.set_data(:server, server_description)
23
+ span.set_data(Span::DataConventions::DB_SYSTEM, "redis")
24
+ span.set_data(Span::DataConventions::DB_NAME, db)
25
+ span.set_data(Span::DataConventions::SERVER_ADDRESS, host)
26
+ span.set_data(Span::DataConventions::SERVER_PORT, port)
23
27
  end
24
28
  end
25
29
  end
@@ -96,8 +100,10 @@ end
96
100
 
97
101
  if defined?(::Redis::Client)
98
102
  if Gem::Version.new(::Redis::VERSION) < Gem::Version.new("5.0")
99
- Sentry.register_patch(Sentry::Redis::OldClientPatch, ::Redis::Client)
103
+ Sentry.register_patch(:redis, Sentry::Redis::OldClientPatch, ::Redis::Client)
100
104
  elsif defined?(RedisClient)
101
- RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
105
+ Sentry.register_patch(:redis) do
106
+ RedisClient.register(Sentry::Redis::GlobalRedisInstrumentation)
107
+ end
102
108
  end
103
109
  end
@@ -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
@@ -28,11 +28,11 @@ module Sentry
28
28
  end
29
29
 
30
30
  def detect_release_from_git
31
- Sentry.sys_command("git rev-parse --short HEAD") if File.directory?(".git")
31
+ Sentry.sys_command("git rev-parse HEAD") if File.directory?(".git")
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
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :include_sentry_event do |event_message = "", **opts|
4
+ match do |sentry_events|
5
+ @expected_exception = expected_exception(**opts)
6
+ @context = context(**opts)
7
+ @tags = tags(**opts)
8
+
9
+ @expected_event = expected_event(event_message)
10
+ @matched_event = find_matched_event(event_message, sentry_events)
11
+
12
+ return false unless @matched_event
13
+
14
+ [verify_context(), verify_tags()].all?
15
+ end
16
+
17
+ chain :with_context do |context|
18
+ @context = context
19
+ end
20
+
21
+ chain :with_tags do |tags|
22
+ @tags = tags
23
+ end
24
+
25
+ failure_message do |sentry_events|
26
+ info = ["Failed to find event matching:\n"]
27
+ info << " message: #{@expected_event.message.inspect}"
28
+ info << " exception: #{@expected_exception.inspect}"
29
+ info << " context: #{@context.inspect}"
30
+ info << " tags: #{@tags.inspect}"
31
+ info << "\n"
32
+ info << "Captured events:\n"
33
+ info << dump_events(sentry_events)
34
+ info.join("\n")
35
+ end
36
+
37
+ def expected_event(event_message)
38
+ if @expected_exception
39
+ Sentry.get_current_client.event_from_exception(@expected_exception)
40
+ else
41
+ Sentry.get_current_client.event_from_message(event_message)
42
+ end
43
+ end
44
+
45
+ def expected_exception(**opts)
46
+ opts[:exception].new(opts[:message]) if opts[:exception]
47
+ end
48
+
49
+ def context(**opts)
50
+ opts.fetch(:context, @context || {})
51
+ end
52
+
53
+ def tags(**opts)
54
+ opts.fetch(:tags, @tags || {})
55
+ end
56
+
57
+ def find_matched_event(event_message, sentry_events)
58
+ @matched_event ||= sentry_events
59
+ .find { |event|
60
+ if @expected_exception
61
+ # Is it OK that we only compare the first exception?
62
+ event_exception = event.exception.values.first
63
+ expected_event_exception = @expected_event.exception.values.first
64
+
65
+ event_exception.type == expected_event_exception.type && event_exception.value == expected_event_exception.value
66
+ else
67
+ event.message == @expected_event.message
68
+ end
69
+ }
70
+ end
71
+
72
+ def dump_events(sentry_events)
73
+ sentry_events.map(&Kernel.method(:Hash)).map do |hash|
74
+ hash.select { |k, _| [:message, :contexts, :tags, :exception].include?(k) }
75
+ end.map do |hash|
76
+ JSON.pretty_generate(hash)
77
+ end.join("\n\n")
78
+ end
79
+
80
+ def verify_context
81
+ return true if @context.empty?
82
+
83
+ @matched_event.contexts.any? { |key, value| value == @context[key] }
84
+ end
85
+
86
+ def verify_tags
87
+ return true if @tags.empty?
88
+
89
+ @tags.all? { |key, value| @matched_event.tags.include?(key) && @matched_event.tags[key] == value }
90
+ end
91
+ end
data/lib/sentry/scope.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sentry/breadcrumb_buffer"
4
+ require "sentry/propagation_context"
5
+ require "sentry/attachment"
4
6
  require "etc"
5
7
 
6
8
  module Sentry
@@ -8,8 +10,8 @@ module Sentry
8
10
  include ArgumentCheckingHelper
9
11
 
10
12
  ATTRIBUTES = [
11
- :transaction_names,
12
- :transaction_sources,
13
+ :transaction_name,
14
+ :transaction_source,
13
15
  :contexts,
14
16
  :extra,
15
17
  :tags,
@@ -20,7 +22,9 @@ module Sentry
20
22
  :event_processors,
21
23
  :rack_env,
22
24
  :span,
23
- :session
25
+ :session,
26
+ :attachments,
27
+ :propagation_context
24
28
  ]
25
29
 
26
30
  attr_reader(*ATTRIBUTES)
@@ -42,21 +46,37 @@ module Sentry
42
46
  # @param hint [Hash] the hint data that'll be passed to event processors.
43
47
  # @return [Event]
44
48
  def apply_to_event(event, hint = nil)
45
- event.tags = tags.merge(event.tags)
46
- event.user = user.merge(event.user)
47
- event.extra = extra.merge(event.extra)
48
- event.contexts = contexts.merge(event.contexts)
49
- event.transaction = transaction_name if transaction_name
50
- event.transaction_info = { source: transaction_source } if transaction_source
49
+ unless event.is_a?(CheckInEvent) || event.is_a?(LogEvent)
50
+ event.tags = tags.merge(event.tags)
51
+ event.user = user.merge(event.user)
52
+ event.extra = extra.merge(event.extra)
53
+ event.contexts = contexts.merge(event.contexts)
54
+ event.transaction = transaction_name if transaction_name
55
+ event.transaction_info = { source: transaction_source } if transaction_source
56
+ event.fingerprint = fingerprint
57
+ event.level = level
58
+ event.breadcrumbs = breadcrumbs
59
+ event.rack_env = rack_env if rack_env
60
+ event.attachments = attachments
61
+ end
51
62
 
52
- if span
53
- event.contexts[:trace] = span.get_trace_context
63
+ if event.is_a?(LogEvent)
64
+ event.user = user.merge(event.user)
54
65
  end
55
66
 
56
- event.fingerprint = fingerprint
57
- event.level = level
58
- event.breadcrumbs = breadcrumbs
59
- event.rack_env = rack_env if rack_env
67
+ if span
68
+ event.contexts[:trace] ||= span.get_trace_context
69
+
70
+ if event.respond_to?(:dynamic_sampling_context)
71
+ event.dynamic_sampling_context ||= span.get_dynamic_sampling_context
72
+ end
73
+ else
74
+ event.contexts[:trace] ||= propagation_context.get_trace_context
75
+
76
+ if event.respond_to?(:dynamic_sampling_context)
77
+ event.dynamic_sampling_context ||= propagation_context.get_dynamic_sampling_context
78
+ end
79
+ end
60
80
 
61
81
  all_event_processors = self.class.global_event_processors + @event_processors
62
82
 
@@ -90,11 +110,13 @@ module Sentry
90
110
  copy.extra = extra.deep_dup
91
111
  copy.tags = tags.deep_dup
92
112
  copy.user = user.deep_dup
93
- copy.transaction_names = transaction_names.dup
94
- copy.transaction_sources = transaction_sources.dup
113
+ copy.transaction_name = transaction_name.dup
114
+ copy.transaction_source = transaction_source.dup
95
115
  copy.fingerprint = fingerprint.deep_dup
96
116
  copy.span = span.deep_dup
97
117
  copy.session = session.deep_dup
118
+ copy.propagation_context = propagation_context.deep_dup
119
+ copy.attachments = attachments.dup
98
120
  copy
99
121
  end
100
122
 
@@ -107,10 +129,12 @@ module Sentry
107
129
  self.extra = scope.extra
108
130
  self.tags = scope.tags
109
131
  self.user = scope.user
110
- self.transaction_names = scope.transaction_names
111
- self.transaction_sources = scope.transaction_sources
132
+ self.transaction_name = scope.transaction_name
133
+ self.transaction_source = scope.transaction_source
112
134
  self.fingerprint = scope.fingerprint
113
135
  self.span = scope.span
136
+ self.propagation_context = scope.propagation_context
137
+ self.attachments = scope.attachments
114
138
  end
115
139
 
116
140
  # Updates the scope's data from the given options.
@@ -120,14 +144,17 @@ module Sentry
120
144
  # @param user [Hash]
121
145
  # @param level [String, Symbol]
122
146
  # @param fingerprint [Array]
123
- # @return [void]
147
+ # @param attachments [Array<Attachment>]
148
+ # @return [Array]
124
149
  def update_from_options(
125
150
  contexts: nil,
126
151
  extra: nil,
127
152
  tags: nil,
128
153
  user: nil,
129
154
  level: nil,
130
- fingerprint: nil
155
+ fingerprint: nil,
156
+ attachments: nil,
157
+ **options
131
158
  )
132
159
  self.contexts.merge!(contexts) if contexts
133
160
  self.extra.merge!(extra) if extra
@@ -135,6 +162,9 @@ module Sentry
135
162
  self.user = user if user
136
163
  self.level = level if level
137
164
  self.fingerprint = fingerprint if fingerprint
165
+
166
+ # Returns unsupported option keys so we can notify users.
167
+ options.keys
138
168
  end
139
169
 
140
170
  # Sets the scope's rack_env attribute.
@@ -219,8 +249,8 @@ module Sentry
219
249
  # @param transaction_name [String]
220
250
  # @return [void]
221
251
  def set_transaction_name(transaction_name, source: :custom)
222
- @transaction_names << transaction_name
223
- @transaction_sources << source
252
+ @transaction_name = transaction_name
253
+ @transaction_source = source
224
254
  end
225
255
 
226
256
  # Sets the currently active session on the scope.
@@ -230,18 +260,10 @@ module Sentry
230
260
  @session = session
231
261
  end
232
262
 
233
- # Returns current transaction name.
234
- # The "transaction" here does not refer to `Transaction` objects.
235
- # @return [String, nil]
236
- def transaction_name
237
- @transaction_names.last
238
- end
239
-
240
- # Returns current transaction source.
241
- # The "transaction" here does not refer to `Transaction` objects.
242
- # @return [String, nil]
243
- def transaction_source
244
- @transaction_sources.last
263
+ # These are high cardinality and thus bad.
264
+ # @return [Boolean]
265
+ def transaction_source_low_quality?
266
+ transaction_source == :url
245
267
  end
246
268
 
247
269
  # Returns the associated Transaction object.
@@ -272,6 +294,19 @@ module Sentry
272
294
  @event_processors << block
273
295
  end
274
296
 
297
+ # Generate a new propagation context either from the incoming env headers or from scratch.
298
+ # @param env [Hash, nil]
299
+ # @return [void]
300
+ def generate_propagation_context(env = nil)
301
+ @propagation_context = PropagationContext.new(self, env)
302
+ end
303
+
304
+ # Add a new attachment to the scope.
305
+ def add_attachment(**opts)
306
+ attachments << (attachment = Attachment.new(**opts))
307
+ attachment
308
+ end
309
+
275
310
  protected
276
311
 
277
312
  # for duplicating scopes internally
@@ -280,18 +315,20 @@ module Sentry
280
315
  private
281
316
 
282
317
  def set_default_value
283
- @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
318
+ @contexts = { os: self.class.os_context, runtime: self.class.runtime_context }
284
319
  @extra = {}
285
320
  @tags = {}
286
321
  @user = {}
287
322
  @level = :error
288
323
  @fingerprint = []
289
- @transaction_names = []
290
- @transaction_sources = []
324
+ @transaction_name = nil
325
+ @transaction_source = nil
291
326
  @event_processors = []
292
327
  @rack_env = {}
293
328
  @span = nil
294
329
  @session = nil
330
+ @attachments = []
331
+ generate_propagation_context
295
332
  set_new_breadcrumb_buffer
296
333
  end
297
334
 
@@ -339,6 +376,5 @@ module Sentry
339
376
  global_event_processors << block
340
377
  end
341
378
  end
342
-
343
379
  end
344
380
  end
@@ -5,8 +5,8 @@ module Sentry
5
5
  attr_reader :started, :status, :aggregation_key
6
6
 
7
7
  # TODO-neel add :crashed after adding handled mechanism
8
- STATUSES = %i(ok errored exited)
9
- AGGREGATE_STATUSES = %i(errored exited)
8
+ STATUSES = %i[ok errored exited]
9
+ AGGREGATE_STATUSES = %i[errored exited]
10
10
 
11
11
  def initialize
12
12
  @started = Sentry.utc_now