sentry-ruby 0.1.2 → 4.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile +5 -0
- data/README.md +53 -7
- data/Rakefile +3 -1
- data/lib/{sentry.rb → sentry-ruby.rb} +28 -2
- data/lib/sentry/benchmarks/benchmark_transport.rb +14 -0
- data/lib/sentry/breadcrumb.rb +7 -7
- data/lib/sentry/breadcrumb/sentry_logger.rb +10 -26
- data/lib/sentry/breadcrumb_buffer.rb +2 -5
- data/lib/sentry/client.rb +17 -5
- data/lib/sentry/configuration.rb +76 -87
- data/lib/sentry/dsn.rb +6 -3
- data/lib/sentry/event.rb +32 -26
- data/lib/sentry/hub.rb +13 -2
- data/lib/sentry/interfaces/request.rb +1 -10
- data/lib/sentry/rack.rb +1 -0
- data/lib/sentry/rack/tracing.rb +39 -0
- data/lib/sentry/rake.rb +17 -0
- data/lib/sentry/scope.rb +26 -4
- data/lib/sentry/span.rb +155 -0
- data/lib/sentry/transaction.rb +113 -0
- data/lib/sentry/transaction_event.rb +29 -0
- data/lib/sentry/transport.rb +7 -5
- data/lib/sentry/transport/configuration.rb +1 -8
- data/lib/sentry/transport/http_transport.rb +5 -2
- data/lib/sentry/transport/state.rb +2 -2
- data/lib/sentry/utils/request_id.rb +16 -0
- data/lib/sentry/version.rb +1 -1
- metadata +10 -4
- data/lib/sentry/ruby.rb +0 -1
data/lib/sentry/dsn.rb
CHANGED
@@ -2,7 +2,10 @@ require "uri"
|
|
2
2
|
|
3
3
|
module Sentry
|
4
4
|
class DSN
|
5
|
-
|
5
|
+
PORT_MAP = { 'http' => 80, 'https' => 443 }.freeze
|
6
|
+
REQUIRED_ATTRIBUTES = %w(host path public_key project_id).freeze
|
7
|
+
|
8
|
+
attr_reader :scheme, :secret_key, :port, *REQUIRED_ATTRIBUTES
|
6
9
|
|
7
10
|
def initialize(dsn_string)
|
8
11
|
@raw_value = dsn_string
|
@@ -24,7 +27,7 @@ module Sentry
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def valid?
|
27
|
-
|
30
|
+
REQUIRED_ATTRIBUTES.all? { |k| public_send(k) }
|
28
31
|
end
|
29
32
|
|
30
33
|
def to_s
|
@@ -33,7 +36,7 @@ module Sentry
|
|
33
36
|
|
34
37
|
def server
|
35
38
|
server = "#{scheme}://#{host}"
|
36
|
-
server += ":#{port}" unless port ==
|
39
|
+
server += ":#{port}" unless port == PORT_MAP[scheme]
|
37
40
|
server += path
|
38
41
|
server
|
39
42
|
end
|
data/lib/sentry/event.rb
CHANGED
@@ -5,6 +5,7 @@ require 'securerandom'
|
|
5
5
|
require 'sentry/interface'
|
6
6
|
require 'sentry/backtrace'
|
7
7
|
require 'sentry/utils/real_ip'
|
8
|
+
require 'sentry/utils/request_id'
|
8
9
|
|
9
10
|
module Sentry
|
10
11
|
class Event
|
@@ -13,21 +14,19 @@ module Sentry
|
|
13
14
|
release environment server_name modules
|
14
15
|
message user tags contexts extra
|
15
16
|
fingerprint breadcrumbs backtrace transaction
|
16
|
-
platform sdk
|
17
|
+
platform sdk type
|
17
18
|
)
|
18
19
|
|
19
20
|
attr_accessor(*ATTRIBUTES)
|
20
|
-
attr_reader :
|
21
|
-
|
22
|
-
alias event_id id
|
21
|
+
attr_reader :configuration, :request, :exception, :stacktrace
|
23
22
|
|
24
23
|
def initialize(configuration:, message: nil)
|
25
24
|
# this needs to go first because some setters rely on configuration
|
26
25
|
@configuration = configuration
|
27
26
|
|
28
27
|
# Set some simple default values
|
29
|
-
@
|
30
|
-
@timestamp =
|
28
|
+
@event_id = SecureRandom.uuid.delete("-")
|
29
|
+
@timestamp = Sentry.utc_now.iso8601
|
31
30
|
@platform = :ruby
|
32
31
|
@sdk = Sentry.sdk_meta
|
33
32
|
|
@@ -39,9 +38,9 @@ module Sentry
|
|
39
38
|
@fingerprint = []
|
40
39
|
|
41
40
|
@server_name = configuration.server_name
|
42
|
-
@environment = configuration.
|
41
|
+
@environment = configuration.environment
|
43
42
|
@release = configuration.release
|
44
|
-
@modules =
|
43
|
+
@modules = configuration.gem_specs if configuration.send_modules
|
45
44
|
|
46
45
|
@message = message || ""
|
47
46
|
|
@@ -51,8 +50,8 @@ module Sentry
|
|
51
50
|
class << self
|
52
51
|
def get_log_message(event_hash)
|
53
52
|
message = event_hash[:message] || event_hash['message']
|
54
|
-
message = get_message_from_exception(event_hash) if message.empty?
|
55
|
-
message = '<no message value>' if message.empty?
|
53
|
+
message = get_message_from_exception(event_hash) if message.nil? || message.empty?
|
54
|
+
message = '<no message value>' if message.nil? || message.empty?
|
56
55
|
message
|
57
56
|
end
|
58
57
|
|
@@ -68,7 +67,7 @@ module Sentry
|
|
68
67
|
end
|
69
68
|
|
70
69
|
def timestamp=(time)
|
71
|
-
@timestamp = time.is_a?(Time) ? time.
|
70
|
+
@timestamp = time.is_a?(Time) ? time.to_f : time
|
72
71
|
end
|
73
72
|
|
74
73
|
def level=(new_level) # needed to meet the Sentry spec
|
@@ -76,7 +75,7 @@ module Sentry
|
|
76
75
|
end
|
77
76
|
|
78
77
|
def rack_env=(env)
|
79
|
-
unless
|
78
|
+
unless request || env.empty?
|
80
79
|
@request = Sentry::RequestInterface.new.tap do |int|
|
81
80
|
int.from_rack(env)
|
82
81
|
end
|
@@ -84,18 +83,22 @@ module Sentry
|
|
84
83
|
if configuration.send_default_pii && ip = calculate_real_ip_from_rack(env.dup)
|
85
84
|
user[:ip_address] = ip
|
86
85
|
end
|
86
|
+
if request_id = Utils::RequestId.read_from(env)
|
87
|
+
tags[:request_id] = request_id
|
88
|
+
end
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
end
|
92
|
+
def type
|
93
|
+
"event"
|
94
|
+
end
|
94
95
|
|
96
|
+
def to_hash
|
97
|
+
data = serialize_attributes
|
95
98
|
data[:breadcrumbs] = breadcrumbs.to_hash if breadcrumbs
|
96
|
-
data[:stacktrace] =
|
97
|
-
data[:request] =
|
98
|
-
data[:exception] =
|
99
|
+
data[:stacktrace] = stacktrace.to_hash if stacktrace
|
100
|
+
data[:request] = request.to_hash if request
|
101
|
+
data[:exception] = exception.to_hash if exception
|
99
102
|
|
100
103
|
data
|
101
104
|
end
|
@@ -141,9 +144,9 @@ module Sentry
|
|
141
144
|
frame.in_app = line.in_app
|
142
145
|
frame.module = line.module_name if line.module_name
|
143
146
|
|
144
|
-
if configuration
|
147
|
+
if configuration.context_lines && frame.abs_path
|
145
148
|
frame.pre_context, frame.context_line, frame.post_context = \
|
146
|
-
configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration
|
149
|
+
configuration.linecache.get_file_context(frame.abs_path, frame.lineno, configuration.context_lines)
|
147
150
|
end
|
148
151
|
|
149
152
|
memo << frame if frame.filename
|
@@ -152,6 +155,14 @@ module Sentry
|
|
152
155
|
|
153
156
|
private
|
154
157
|
|
158
|
+
def serialize_attributes
|
159
|
+
self.class::ATTRIBUTES.each_with_object({}) do |att, memo|
|
160
|
+
if value = public_send(att)
|
161
|
+
memo[att] = value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
155
166
|
# When behind a proxy (or if the user is using a proxy), we can't use
|
156
167
|
# REMOTE_ADDR to determine the Event IP, and must use other headers instead.
|
157
168
|
def calculate_real_ip_from_rack(env)
|
@@ -162,10 +173,5 @@ module Sentry
|
|
162
173
|
:forwarded_for => env["HTTP_X_FORWARDED_FOR"]
|
163
174
|
).calculate_ip
|
164
175
|
end
|
165
|
-
|
166
|
-
def list_gem_specs
|
167
|
-
# Older versions of Rubygems don't support iterating over all specs
|
168
|
-
Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map)
|
169
|
-
end
|
170
176
|
end
|
171
177
|
end
|
data/lib/sentry/hub.rb
CHANGED
@@ -67,6 +67,12 @@ module Sentry
|
|
67
67
|
@stack.pop
|
68
68
|
end
|
69
69
|
|
70
|
+
def start_transaction(transaction: nil, **options)
|
71
|
+
transaction ||= Transaction.new(**options)
|
72
|
+
transaction.set_initial_sample_desicion
|
73
|
+
transaction
|
74
|
+
end
|
75
|
+
|
70
76
|
def capture_exception(exception, **options, &block)
|
71
77
|
return unless current_client
|
72
78
|
|
@@ -74,12 +80,16 @@ module Sentry
|
|
74
80
|
|
75
81
|
return unless event
|
76
82
|
|
83
|
+
options[:hint] ||= {}
|
84
|
+
options[:hint] = options[:hint].merge(exception: exception)
|
77
85
|
capture_event(event, **options, &block)
|
78
86
|
end
|
79
87
|
|
80
88
|
def capture_message(message, **options, &block)
|
81
89
|
return unless current_client
|
82
90
|
|
91
|
+
options[:hint] ||= {}
|
92
|
+
options[:hint] = options[:hint].merge(message: message)
|
83
93
|
event = current_client.event_from_message(message)
|
84
94
|
capture_event(event, **options, &block)
|
85
95
|
end
|
@@ -87,6 +97,7 @@ module Sentry
|
|
87
97
|
def capture_event(event, **options, &block)
|
88
98
|
return unless current_client
|
89
99
|
|
100
|
+
hint = options.delete(:hint)
|
90
101
|
scope = current_scope.dup
|
91
102
|
|
92
103
|
if block
|
@@ -97,9 +108,9 @@ module Sentry
|
|
97
108
|
scope.update_from_options(**options)
|
98
109
|
end
|
99
110
|
|
100
|
-
event = current_client.capture_event(event, scope)
|
111
|
+
event = current_client.capture_event(event, scope, hint)
|
101
112
|
|
102
|
-
@last_event_id = event.
|
113
|
+
@last_event_id = event.event_id
|
103
114
|
event
|
104
115
|
end
|
105
116
|
|
@@ -39,15 +39,6 @@ module Sentry
|
|
39
39
|
|
40
40
|
private
|
41
41
|
|
42
|
-
# Request ID based on ActionDispatch::RequestId
|
43
|
-
def read_request_id_from(env_hash)
|
44
|
-
REQUEST_ID_HEADERS.each do |key|
|
45
|
-
request_id = env_hash[key]
|
46
|
-
return request_id if request_id
|
47
|
-
end
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
|
51
42
|
# See Sentry server default limits at
|
52
43
|
# https://github.com/getsentry/sentry/blob/master/src/sentry/conf/server.py
|
53
44
|
def read_data_from(request)
|
@@ -67,7 +58,7 @@ module Sentry
|
|
67
58
|
begin
|
68
59
|
key = key.to_s # rack env can contain symbols
|
69
60
|
value = value.to_s
|
70
|
-
next memo['X-Request-Id'] ||=
|
61
|
+
next memo['X-Request-Id'] ||= Utils::RequestId.read_from(env_hash) if Utils::RequestId::REQUEST_ID_HEADERS.include?(key)
|
71
62
|
next unless key.upcase == key # Non-upper case stuff isn't either
|
72
63
|
|
73
64
|
# Rack adds in an incorrect HTTP_VERSION key, which causes downstream
|
data/lib/sentry/rack.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Sentry
|
2
|
+
module Rack
|
3
|
+
class Tracing
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
Sentry.clone_hub_to_current_thread unless Sentry.get_current_hub
|
10
|
+
|
11
|
+
if Sentry.configuration.traces_sample_rate.to_f == 0.0
|
12
|
+
return @app.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
Sentry.with_scope do |scope|
|
16
|
+
scope.clear_breadcrumbs
|
17
|
+
scope.set_transaction_name(env["PATH_INFO"]) if env["PATH_INFO"]
|
18
|
+
span = Sentry.start_transaction(name: scope.transaction_name, op: "rack.request")
|
19
|
+
scope.set_span(span)
|
20
|
+
|
21
|
+
begin
|
22
|
+
response = @app.call(env)
|
23
|
+
rescue
|
24
|
+
finish_span(span, 500)
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
finish_span(span, response[0])
|
29
|
+
response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish_span(span, status_code)
|
34
|
+
span.set_http_status(status_code)
|
35
|
+
span.finish
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/sentry/rake.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/task"
|
3
|
+
|
4
|
+
module Rake
|
5
|
+
class Application
|
6
|
+
alias orig_display_error_messsage display_error_message
|
7
|
+
def display_error_message(ex)
|
8
|
+
Sentry.capture_exception(ex) do |scope|
|
9
|
+
task_name = top_level_tasks.join(' ')
|
10
|
+
scope.set_transaction_name(task_name)
|
11
|
+
scope.set_tag("rake_task", task_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
orig_display_error_messsage(ex)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/sentry/scope.rb
CHANGED
@@ -3,7 +3,7 @@ require "etc"
|
|
3
3
|
|
4
4
|
module Sentry
|
5
5
|
class Scope
|
6
|
-
ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env]
|
6
|
+
ATTRIBUTES = [:transaction_names, :contexts, :extra, :tags, :user, :level, :breadcrumbs, :fingerprint, :event_processors, :rack_env, :span]
|
7
7
|
|
8
8
|
attr_reader(*ATTRIBUTES)
|
9
9
|
|
@@ -15,20 +15,25 @@ module Sentry
|
|
15
15
|
set_default_value
|
16
16
|
end
|
17
17
|
|
18
|
-
def apply_to_event(event)
|
18
|
+
def apply_to_event(event, hint = nil)
|
19
19
|
event.tags = tags.merge(event.tags)
|
20
20
|
event.user = user.merge(event.user)
|
21
21
|
event.extra = extra.merge(event.extra)
|
22
22
|
event.contexts = contexts.merge(event.contexts)
|
23
|
+
|
24
|
+
if span
|
25
|
+
event.contexts[:trace] = span.get_trace_context
|
26
|
+
end
|
27
|
+
|
23
28
|
event.fingerprint = fingerprint
|
24
|
-
event.level
|
29
|
+
event.level = level
|
25
30
|
event.transaction = transaction_names.last
|
26
31
|
event.breadcrumbs = breadcrumbs
|
27
32
|
event.rack_env = rack_env
|
28
33
|
|
29
34
|
unless @event_processors.empty?
|
30
35
|
@event_processors.each do |processor_block|
|
31
|
-
event = processor_block.call(event)
|
36
|
+
event = processor_block.call(event, hint)
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -52,6 +57,7 @@ module Sentry
|
|
52
57
|
copy.user = user.deep_dup
|
53
58
|
copy.transaction_names = transaction_names.deep_dup
|
54
59
|
copy.fingerprint = fingerprint.deep_dup
|
60
|
+
copy.span = span
|
55
61
|
copy
|
56
62
|
end
|
57
63
|
|
@@ -63,6 +69,7 @@ module Sentry
|
|
63
69
|
self.user = scope.user
|
64
70
|
self.transaction_names = scope.transaction_names
|
65
71
|
self.fingerprint = scope.fingerprint
|
72
|
+
self.span = scope.span
|
66
73
|
end
|
67
74
|
|
68
75
|
def update_from_options(
|
@@ -86,6 +93,11 @@ module Sentry
|
|
86
93
|
@rack_env = env
|
87
94
|
end
|
88
95
|
|
96
|
+
def set_span(span)
|
97
|
+
check_argument_type!(span, Span)
|
98
|
+
@span = span
|
99
|
+
end
|
100
|
+
|
89
101
|
def set_user(user_hash)
|
90
102
|
check_argument_type!(user_hash, Hash)
|
91
103
|
@user = user_hash
|
@@ -130,6 +142,15 @@ module Sentry
|
|
130
142
|
@transaction_names.last
|
131
143
|
end
|
132
144
|
|
145
|
+
def get_transaction
|
146
|
+
# transaction will always be the first in the span_recorder
|
147
|
+
span.span_recorder.spans.first if span
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_span
|
151
|
+
span
|
152
|
+
end
|
153
|
+
|
133
154
|
def set_fingerprint(fingerprint)
|
134
155
|
check_argument_type!(fingerprint, Array)
|
135
156
|
|
@@ -164,6 +185,7 @@ module Sentry
|
|
164
185
|
@transaction_names = []
|
165
186
|
@event_processors = []
|
166
187
|
@rack_env = {}
|
188
|
+
@span = nil
|
167
189
|
end
|
168
190
|
|
169
191
|
class << self
|
data/lib/sentry/span.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "securerandom"
|
3
|
+
|
4
|
+
module Sentry
|
5
|
+
class Span
|
6
|
+
STATUS_MAP = {
|
7
|
+
400 => "invalid_argument",
|
8
|
+
401 => "unauthenticated",
|
9
|
+
403 => "permission_denied",
|
10
|
+
404 => "not_found",
|
11
|
+
409 => "already_exists",
|
12
|
+
429 => "resource_exhausted",
|
13
|
+
499 => "cancelled",
|
14
|
+
500 => "internal_error",
|
15
|
+
501 => "unimplemented",
|
16
|
+
503 => "unavailable",
|
17
|
+
504 => "deadline_exceeded"
|
18
|
+
}
|
19
|
+
|
20
|
+
|
21
|
+
attr_reader :trace_id, :span_id, :parent_span_id, :sampled, :start_timestamp, :timestamp, :description, :op, :status, :tags, :data
|
22
|
+
attr_accessor :span_recorder
|
23
|
+
|
24
|
+
def initialize(description: nil, op: nil, status: nil, trace_id: nil, parent_span_id: nil, sampled: nil, start_timestamp: nil, timestamp: nil)
|
25
|
+
@trace_id = trace_id || SecureRandom.uuid.delete("-")
|
26
|
+
@span_id = SecureRandom.hex(8)
|
27
|
+
@parent_span_id = parent_span_id
|
28
|
+
@sampled = sampled
|
29
|
+
@start_timestamp = start_timestamp || Sentry.utc_now.to_f
|
30
|
+
@timestamp = timestamp
|
31
|
+
@description = description
|
32
|
+
@op = op
|
33
|
+
@status = status
|
34
|
+
@data = {}
|
35
|
+
@tags = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_span_recorder
|
39
|
+
@span_recorder = SpanRecorder.new(1000)
|
40
|
+
@span_recorder.add(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
def finish
|
44
|
+
# already finished
|
45
|
+
return if @timestamp
|
46
|
+
|
47
|
+
@timestamp = Sentry.utc_now.to_f
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_sentry_trace
|
52
|
+
sampled_flag = ""
|
53
|
+
sampled_flag = @sampled ? 1 : 0 unless @sampled.nil?
|
54
|
+
|
55
|
+
"#{@trace_id}-#{@span_id}-#{sampled_flag}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_hash
|
59
|
+
{
|
60
|
+
trace_id: @trace_id,
|
61
|
+
span_id: @span_id,
|
62
|
+
parent_span_id: @parent_span_id,
|
63
|
+
start_timestamp: @start_timestamp,
|
64
|
+
timestamp: @timestamp,
|
65
|
+
description: @description,
|
66
|
+
op: @op,
|
67
|
+
status: @status,
|
68
|
+
tags: @tags,
|
69
|
+
data: @data
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_trace_context
|
74
|
+
{
|
75
|
+
trace_id: @trace_id,
|
76
|
+
span_id: @span_id,
|
77
|
+
description: @description,
|
78
|
+
op: @op,
|
79
|
+
status: @status
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def start_child(**options)
|
84
|
+
options = options.dup.merge(trace_id: @trace_id, parent_span_id: @span_id, sampled: @sampled)
|
85
|
+
child_span = Span.new(options)
|
86
|
+
child_span.span_recorder = @span_recorder
|
87
|
+
|
88
|
+
if @span_recorder && @sampled
|
89
|
+
@span_recorder.add(child_span)
|
90
|
+
end
|
91
|
+
|
92
|
+
child_span
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_child_span(**options, &block)
|
96
|
+
child_span = start_child(**options)
|
97
|
+
|
98
|
+
yield(child_span)
|
99
|
+
|
100
|
+
child_span.finish
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_op(op)
|
104
|
+
@op = op
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_description(description)
|
108
|
+
@description = description
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_status(status)
|
112
|
+
@status = status
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_timestamp(timestamp)
|
116
|
+
@timestamp = timestamp
|
117
|
+
end
|
118
|
+
|
119
|
+
def set_http_status(status_code)
|
120
|
+
status_code = status_code.to_i
|
121
|
+
set_data("status_code", status_code)
|
122
|
+
|
123
|
+
status =
|
124
|
+
if status_code >= 200 && status_code < 299
|
125
|
+
"ok"
|
126
|
+
else
|
127
|
+
STATUS_MAP[status_code]
|
128
|
+
end
|
129
|
+
set_status(status)
|
130
|
+
end
|
131
|
+
|
132
|
+
def set_data(key, value)
|
133
|
+
@data[key] = value
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_tag(key, value)
|
137
|
+
@tags[key] = value
|
138
|
+
end
|
139
|
+
|
140
|
+
class SpanRecorder
|
141
|
+
attr_reader :max_length, :spans
|
142
|
+
|
143
|
+
def initialize(max_length)
|
144
|
+
@max_length = max_length
|
145
|
+
@spans = []
|
146
|
+
end
|
147
|
+
|
148
|
+
def add(span)
|
149
|
+
if @spans.count < @max_length
|
150
|
+
@spans << span
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|