sentry-ruby-core 4.2.0 → 4.3.2

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.
@@ -16,30 +16,24 @@ module Sentry
16
16
  scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
17
17
  scope.set_rack_env(env)
18
18
 
19
- span =
20
- if sentry_trace = env["HTTP_SENTRY_TRACE"]
21
- Sentry::Transaction.from_sentry_trace(sentry_trace, name: scope.transaction_name, op: transaction_op)
22
- else
23
- Sentry.start_transaction(name: scope.transaction_name, op: transaction_op)
24
- end
25
-
26
- scope.set_span(span)
19
+ transaction = start_transaction(env, scope)
20
+ scope.set_span(transaction) if transaction
27
21
 
28
22
  begin
29
23
  response = @app.call(env)
30
24
  rescue Sentry::Error
31
- finish_span(span, 500)
25
+ finish_transaction(transaction, 500)
32
26
  raise # Don't capture Sentry errors
33
27
  rescue Exception => e
34
28
  capture_exception(e)
35
- finish_span(span, 500)
29
+ finish_transaction(transaction, 500)
36
30
  raise
37
31
  end
38
32
 
39
33
  exception = collect_exception(env)
40
34
  capture_exception(exception) if exception
41
35
 
42
- finish_span(span, response[0])
36
+ finish_transaction(transaction, response[0])
43
37
 
44
38
  response
45
39
  end
@@ -59,9 +53,19 @@ module Sentry
59
53
  Sentry.capture_exception(exception)
60
54
  end
61
55
 
62
- def finish_span(span, status_code)
63
- span.set_http_status(status_code)
64
- span.finish
56
+ def start_transaction(env, scope)
57
+ sentry_trace = env["HTTP_SENTRY_TRACE"]
58
+ options = { name: scope.transaction_name, op: transaction_op }
59
+ transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
60
+ Sentry.start_transaction(transaction: transaction, **options)
61
+ end
62
+
63
+
64
+ def finish_transaction(transaction, status_code)
65
+ return unless transaction
66
+
67
+ transaction.set_http_status(status_code)
68
+ transaction.finish
65
69
  end
66
70
  end
67
71
  end
data/lib/sentry/scope.rb CHANGED
@@ -9,7 +9,8 @@ module Sentry
9
9
 
10
10
  attr_reader(*ATTRIBUTES)
11
11
 
12
- def initialize
12
+ def initialize(max_breadcrumbs: nil)
13
+ @max_breadcrumbs = max_breadcrumbs
13
14
  set_default_value
14
15
  end
15
16
 
@@ -47,7 +48,7 @@ module Sentry
47
48
  end
48
49
 
49
50
  def clear_breadcrumbs
50
- @breadcrumbs = BreadcrumbBuffer.new
51
+ set_new_breadcrumb_buffer
51
52
  end
52
53
 
53
54
  def dup
@@ -125,7 +126,7 @@ module Sentry
125
126
 
126
127
  def set_contexts(contexts_hash)
127
128
  check_argument_type!(contexts_hash, Hash)
128
- @contexts = contexts_hash
129
+ @contexts.merge!(contexts_hash)
129
130
  end
130
131
 
131
132
  def set_context(key, value)
@@ -145,8 +146,7 @@ module Sentry
145
146
  end
146
147
 
147
148
  def get_transaction
148
- # transaction will always be the first in the span_recorder
149
- span.span_recorder.spans.first if span
149
+ span.transaction if span
150
150
  end
151
151
 
152
152
  def get_span
@@ -171,7 +171,6 @@ module Sentry
171
171
  private
172
172
 
173
173
  def set_default_value
174
- @breadcrumbs = BreadcrumbBuffer.new
175
174
  @contexts = { :os => self.class.os_context, :runtime => self.class.runtime_context }
176
175
  @extra = {}
177
176
  @tags = {}
@@ -182,8 +181,14 @@ module Sentry
182
181
  @event_processors = []
183
182
  @rack_env = {}
184
183
  @span = nil
184
+ set_new_breadcrumb_buffer
185
185
  end
186
186
 
187
+ def set_new_breadcrumb_buffer
188
+ @breadcrumbs = BreadcrumbBuffer.new(@max_breadcrumbs)
189
+ end
190
+
191
+
187
192
  class << self
188
193
  def os_context
189
194
  @os_context ||=
data/lib/sentry/span.rb CHANGED
@@ -19,7 +19,7 @@ module Sentry
19
19
 
20
20
 
21
21
  attr_reader :trace_id, :span_id, :parent_span_id, :sampled, :start_timestamp, :timestamp, :description, :op, :status, :tags, :data
22
- attr_accessor :span_recorder
22
+ attr_accessor :span_recorder, :transaction
23
23
 
24
24
  def initialize(description: nil, op: nil, status: nil, trace_id: nil, parent_span_id: nil, sampled: nil, start_timestamp: nil, timestamp: nil)
25
25
  @trace_id = trace_id || SecureRandom.uuid.delete("-")
@@ -78,7 +78,15 @@ module Sentry
78
78
 
79
79
  def start_child(**options)
80
80
  options = options.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
81
- Span.new(**options)
81
+ new_span = Span.new(**options)
82
+ new_span.transaction = transaction
83
+ new_span.span_recorder = span_recorder
84
+
85
+ if span_recorder
86
+ span_recorder.add(new_span)
87
+ end
88
+
89
+ new_span
82
90
  end
83
91
 
84
92
  def with_child_span(**options, &block)
@@ -17,23 +17,26 @@ module Sentry
17
17
 
18
18
  @name = name
19
19
  @parent_sampled = parent_sampled
20
- set_span_recorder
20
+ @transaction = self
21
+ init_span_recorder
21
22
  end
22
23
 
23
- def set_span_recorder
24
- @span_recorder = SpanRecorder.new(1000)
25
- @span_recorder.add(self)
26
- end
27
-
28
- def self.from_sentry_trace(sentry_trace, **options)
24
+ def self.from_sentry_trace(sentry_trace, configuration: Sentry.configuration, **options)
25
+ return unless configuration.tracing_enabled?
29
26
  return unless sentry_trace
30
27
 
31
28
  match = SENTRY_TRACE_REGEXP.match(sentry_trace)
29
+ return if match.nil?
32
30
  trace_id, parent_span_id, sampled_flag = match[1..3]
33
31
 
34
- sampled = sampled_flag != "0"
32
+ parent_sampled =
33
+ if sampled_flag.nil?
34
+ nil
35
+ else
36
+ sampled_flag != "0"
37
+ end
35
38
 
36
- new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: sampled, **options)
39
+ new(trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, **options)
37
40
  end
38
41
 
39
42
  def to_hash
@@ -42,20 +45,9 @@ module Sentry
42
45
  hash
43
46
  end
44
47
 
45
- def start_child(**options)
46
- child_span = super
47
- child_span.span_recorder = @span_recorder
48
-
49
- if @sampled
50
- @span_recorder.add(child_span)
51
- end
52
-
53
- child_span
54
- end
55
-
56
48
  def deep_dup
57
49
  copy = super
58
- copy.set_span_recorder
50
+ copy.init_span_recorder(@span_recorder.max_length)
59
51
 
60
52
  @span_recorder.spans.each do |span|
61
53
  # span_recorder's first span is the current span, which should not be added to the copy's spans
@@ -66,30 +58,29 @@ module Sentry
66
58
  copy
67
59
  end
68
60
 
69
- def set_initial_sample_desicion(sampling_context = {})
70
- unless Sentry.configuration.tracing_enabled?
61
+ def set_initial_sample_decision(sampling_context:, configuration: Sentry.configuration)
62
+ unless configuration.tracing_enabled?
71
63
  @sampled = false
72
64
  return
73
65
  end
74
66
 
75
67
  return unless @sampled.nil?
76
68
 
77
- transaction_description = generate_transaction_description
78
-
79
- logger = Sentry.configuration.logger
80
- sample_rate = Sentry.configuration.traces_sample_rate
81
- traces_sampler = Sentry.configuration.traces_sampler
69
+ traces_sampler = configuration.traces_sampler
82
70
 
83
- if traces_sampler.is_a?(Proc)
84
- sampling_context = sampling_context.merge(
85
- parent_sampled: @parent_sampled,
86
- transaction_context: self.to_hash
87
- )
71
+ sample_rate =
72
+ if traces_sampler.is_a?(Proc)
73
+ traces_sampler.call(sampling_context)
74
+ elsif !sampling_context[:parent_sampled].nil?
75
+ sampling_context[:parent_sampled]
76
+ else
77
+ configuration.traces_sample_rate
78
+ end
88
79
 
89
- sample_rate = traces_sampler.call(sampling_context)
90
- end
80
+ transaction_description = generate_transaction_description
81
+ logger = configuration.logger
91
82
 
92
- unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Float) && sample_rate >= 0.0 && sample_rate <= 1.0)
83
+ unless [true, false].include?(sample_rate) || (sample_rate.is_a?(Numeric) && sample_rate >= 0.0 && sample_rate <= 1.0)
93
84
  @sampled = false
94
85
  logger.warn("#{MESSAGE_PREFIX} Discarding #{transaction_description} because of invalid sample_rate: #{sample_rate}")
95
86
  return
@@ -130,6 +121,13 @@ module Sentry
130
121
  hub.capture_event(event)
131
122
  end
132
123
 
124
+ protected
125
+
126
+ def init_span_recorder(limit = 1000)
127
+ @span_recorder = SpanRecorder.new(limit)
128
+ @span_recorder.add(self)
129
+ end
130
+
133
131
  private
134
132
 
135
133
  def generate_transaction_description
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Sentry
4
4
  class TransactionEvent < Event
5
+ TYPE = "transaction"
6
+
5
7
  ATTRIBUTES = %i(
6
8
  event_id level timestamp start_timestamp
7
9
  release environment server_name modules
@@ -17,7 +19,7 @@ module Sentry
17
19
  end
18
20
 
19
21
  def type
20
- "transaction"
22
+ TYPE
21
23
  end
22
24
 
23
25
  def to_hash
@@ -24,16 +24,13 @@ module Sentry
24
24
  return
25
25
  end
26
26
 
27
- encoded_data = prepare_encoded_event(event)
27
+ encoded_data = encode(event)
28
28
 
29
29
  return nil unless encoded_data
30
30
 
31
31
  send_data(encoded_data)
32
32
 
33
33
  event
34
- rescue => e
35
- failed_for_exception(e, event)
36
- nil
37
34
  end
38
35
 
39
36
  def generate_auth_header
@@ -48,38 +45,22 @@ module Sentry
48
45
  'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ')
49
46
  end
50
47
 
51
- def encode(event_hash)
52
- event_id = event_hash[:event_id] || event_hash['event_id']
53
- event_type = event_hash[:type] || event_hash['type']
48
+ def encode(event)
49
+ # Convert to hash
50
+ event_hash = event.to_hash
51
+
52
+ event_id = event_hash[:event_id] || event_hash["event_id"]
53
+ item_type = event_hash[:type] || event_hash["type"] || "event"
54
54
 
55
55
  envelope = <<~ENVELOPE
56
56
  {"event_id":"#{event_id}","dsn":"#{configuration.dsn.to_s}","sdk":#{Sentry.sdk_meta.to_json},"sent_at":"#{Sentry.utc_now.iso8601}"}
57
- {"type":"#{event_type}","content_type":"application/json"}
57
+ {"type":"#{item_type}","content_type":"application/json"}
58
58
  #{JSON.generate(event_hash)}
59
59
  ENVELOPE
60
60
 
61
- envelope
62
- end
63
-
64
- private
65
-
66
- def prepare_encoded_event(event)
67
- # Convert to hash
68
- event_hash = event.to_hash
69
-
70
- event_id = event_hash[:event_id] || event_hash["event_id"]
71
- event_type = event_hash[:type] || event_hash["type"]
72
- configuration.logger.info(LOGGER_PROGNAME) { "Sending #{event_type} #{event_id} to Sentry" }
73
- encode(event_hash)
74
- end
75
-
76
- def failed_for_exception(e, event)
77
- configuration.logger.warn(LOGGER_PROGNAME) { "Unable to record event with remote Sentry server (#{e.class} - #{e.message}):\n#{e.backtrace[0..10].join("\n")}" }
78
- log_not_sending(event)
79
- end
61
+ configuration.logger.info(LOGGER_PROGNAME) { "Sending envelope [#{item_type}] #{event_id} to Sentry" }
80
62
 
81
- def log_not_sending(event)
82
- configuration.logger.warn(LOGGER_PROGNAME) { "Failed to submit event. Unreported Event: #{Event.get_log_message(event.to_hash)}" }
63
+ envelope
83
64
  end
84
65
  end
85
66
  end
@@ -1,12 +1,14 @@
1
1
  module Sentry
2
2
  class Transport
3
3
  class Configuration
4
- attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder, :transport_class
4
+ attr_accessor :timeout, :open_timeout, :proxy, :ssl, :ssl_ca_file, :ssl_verification, :http_adapter, :faraday_builder,
5
+ :transport_class, :encoding
5
6
 
6
7
  def initialize
7
8
  @ssl_verification = true
8
9
  @open_timeout = 1
9
10
  @timeout = 2
11
+ @encoding = HTTPTransport::GZIP_ENCODING
10
12
  end
11
13
 
12
14
  def transport_class=(klass)
@@ -1,8 +1,12 @@
1
1
  require 'faraday'
2
+ require 'zlib'
2
3
 
3
4
  module Sentry
4
5
  class HTTPTransport < Transport
5
- CONTENT_TYPE = 'application/json'
6
+ GZIP_ENCODING = "gzip"
7
+ GZIP_THRESHOLD = 1024 * 30
8
+ CONTENT_TYPE = 'application/x-sentry-envelope'
9
+
6
10
  attr_reader :conn, :adapter
7
11
 
8
12
  def initialize(*args)
@@ -13,8 +17,16 @@ module Sentry
13
17
  end
14
18
 
15
19
  def send_data(data)
20
+ encoding = ""
21
+
22
+ if should_compress?(data)
23
+ data = Zlib.gzip(data)
24
+ encoding = GZIP_ENCODING
25
+ end
26
+
16
27
  conn.post @endpoint do |req|
17
28
  req.headers['Content-Type'] = CONTENT_TYPE
29
+ req.headers['Content-Encoding'] = encoding
18
30
  req.headers['X-Sentry-Auth'] = generate_auth_header
19
31
  req.body = data
20
32
  end
@@ -26,11 +38,15 @@ module Sentry
26
38
  error_info += " Error in headers is: #{e.response[:headers]['x-sentry-error']}" if e.response[:headers]['x-sentry-error']
27
39
  end
28
40
 
29
- raise Sentry::Error, error_info
41
+ raise Sentry::ExternalError, error_info
30
42
  end
31
43
 
32
44
  private
33
45
 
46
+ def should_compress?(data)
47
+ @transport_configuration.encoding == GZIP_ENCODING && data.bytesize >= GZIP_THRESHOLD
48
+ end
49
+
34
50
  def set_conn
35
51
  server = @dsn.server
36
52
 
@@ -29,7 +29,7 @@ module Sentry
29
29
  @client_ip = client_ip
30
30
  @real_ip = real_ip
31
31
  @forwarded_for = forwarded_for
32
- @trusted_proxies = (LOCAL_ADDRESSES + Array(trusted_proxies)).map { |proxy| IPAddr.new(proxy) }.uniq
32
+ @trusted_proxies = (LOCAL_ADDRESSES + Array(trusted_proxies)).map { |proxy| IPAddr.new(proxy.to_s) }.uniq
33
33
  end
34
34
 
35
35
  def calculate_ip
@@ -1,3 +1,3 @@
1
1
  module Sentry
2
- VERSION = "4.2.0"
2
+ VERSION = "4.3.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-ruby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sentry Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
11
+ date: 2021-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -71,6 +71,7 @@ files:
71
71
  - lib/sentry/core_ext/object/duplicable.rb
72
72
  - lib/sentry/dsn.rb
73
73
  - lib/sentry/event.rb
74
+ - lib/sentry/exceptions.rb
74
75
  - lib/sentry/hub.rb
75
76
  - lib/sentry/integrable.rb
76
77
  - lib/sentry/interface.rb
@@ -78,6 +79,7 @@ files:
78
79
  - lib/sentry/interfaces/request.rb
79
80
  - lib/sentry/interfaces/single_exception.rb
80
81
  - lib/sentry/interfaces/stacktrace.rb
82
+ - lib/sentry/interfaces/stacktrace_builder.rb
81
83
  - lib/sentry/interfaces/threads.rb
82
84
  - lib/sentry/linecache.rb
83
85
  - lib/sentry/logger.rb
@@ -122,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  - !ruby/object:Gem::Version
123
125
  version: '0'
124
126
  requirements: []
125
- rubygems_version: 3.0.3
127
+ rubygems_version: 3.0.3.1
126
128
  signing_key:
127
129
  specification_version: 4
128
130
  summary: A gem that provides a client interface for the Sentry error logger