sentry-ruby-core 4.2.0 → 4.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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