jaeger-client 0.10.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +33 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +13 -0
- data/README.md +55 -3
- data/crossdock/Dockerfile +3 -1
- data/crossdock/Gemfile +1 -1
- data/crossdock/Gemfile.lock +15 -13
- data/crossdock/jaeger-docker-compose.yml +19 -14
- data/crossdock/server +6 -4
- data/jaeger-client.gemspec +10 -5
- data/lib/jaeger/client/version.rb +1 -1
- data/lib/jaeger/client.rb +14 -7
- data/lib/jaeger/encoders/thrift_encoder.rb +113 -32
- data/lib/jaeger/extractors.rb +71 -7
- data/lib/jaeger/http_sender.rb +3 -3
- data/lib/jaeger/injectors.rb +19 -5
- data/lib/jaeger/recurring_executor.rb +35 -0
- data/lib/jaeger/samplers/const.rb +1 -1
- data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +13 -6
- data/lib/jaeger/samplers/per_operation.rb +44 -14
- data/lib/jaeger/samplers/probabilistic.rb +15 -1
- data/lib/jaeger/samplers/rate_limiting.rb +24 -6
- data/lib/jaeger/samplers/remote_controlled/instructions_fetcher.rb +34 -0
- data/lib/jaeger/samplers/remote_controlled.rb +119 -0
- data/lib/jaeger/samplers.rb +1 -0
- data/lib/jaeger/scope.rb +3 -2
- data/lib/jaeger/scope_manager.rb +1 -0
- data/lib/jaeger/span.rb +3 -4
- data/lib/jaeger/span_context.rb +3 -3
- data/lib/jaeger/thrift_tag_builder.rb +42 -0
- data/lib/jaeger/trace_id.rb +10 -1
- data/lib/jaeger/tracer.rb +29 -10
- data/lib/jaeger/udp_sender/transport.rb +4 -3
- data/lib/jaeger/udp_sender.rb +9 -7
- metadata +55 -20
- data/.travis.yml +0 -14
- data/lib/jaeger/span/thrift_tag_builder.rb +0 -43
data/lib/jaeger/extractors.rb
CHANGED
@@ -6,10 +6,12 @@ module Jaeger
|
|
6
6
|
def self.parse(trace)
|
7
7
|
return nil if !trace || trace == ''
|
8
8
|
|
9
|
-
trace_arguments = trace.split(':')
|
9
|
+
trace_arguments = trace.split(':')
|
10
10
|
return nil if trace_arguments.size != 4
|
11
11
|
|
12
|
-
trace_id
|
12
|
+
trace_id = TraceId.base16_hex_id_to_uint128(trace_arguments[0])
|
13
|
+
span_id, parent_id, flags = trace_arguments[1..3].map(&TraceId.method(:base16_hex_id_to_uint64))
|
14
|
+
|
13
15
|
return nil if trace_id.zero? || span_id.zero?
|
14
16
|
|
15
17
|
SpanContext.new(
|
@@ -63,13 +65,47 @@ module Jaeger
|
|
63
65
|
end
|
64
66
|
|
65
67
|
class B3RackCodec
|
68
|
+
class Keys
|
69
|
+
TRACE_ID = 'HTTP_X_B3_TRACEID'
|
70
|
+
SPAN_ID = 'HTTP_X_B3_SPANID'
|
71
|
+
PARENT_SPAN_ID = 'HTTP_X_B3_PARENTSPANID'
|
72
|
+
FLAGS = 'HTTP_X_B3_FLAGS'
|
73
|
+
SAMPLED = 'HTTP_X_B3_SAMPLED'
|
74
|
+
end.freeze
|
75
|
+
|
76
|
+
def self.extract(carrier)
|
77
|
+
B3CodecCommon.extract(carrier, Keys)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class B3TextMapCodec
|
82
|
+
class Keys
|
83
|
+
TRACE_ID = 'x-b3-traceid'
|
84
|
+
SPAN_ID = 'x-b3-spanid'
|
85
|
+
PARENT_SPAN_ID = 'x-b3-parentspanid'
|
86
|
+
FLAGS = 'x-b3-flags'
|
87
|
+
SAMPLED = 'x-b3-sampled'
|
88
|
+
end.freeze
|
89
|
+
|
66
90
|
def self.extract(carrier)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
91
|
+
B3CodecCommon.extract(carrier, Keys)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class B3CodecCommon
|
96
|
+
def self.extract(carrier, keys)
|
97
|
+
return nil if carrier[keys::TRACE_ID].nil? || carrier[keys::SPAN_ID].nil?
|
98
|
+
|
99
|
+
trace_id = if carrier[keys::TRACE_ID].length <= 16
|
100
|
+
TraceId.base16_hex_id_to_uint64(carrier[keys::TRACE_ID])
|
101
|
+
else
|
102
|
+
TraceId.base16_hex_id_to_uint128(carrier[keys::TRACE_ID])
|
103
|
+
end
|
104
|
+
|
105
|
+
span_id = TraceId.base16_hex_id_to_uint64(carrier[keys::SPAN_ID])
|
106
|
+
parent_id = TraceId.base16_hex_id_to_uint64(carrier[keys::PARENT_SPAN_ID])
|
107
|
+
flags = parse_flags(carrier[keys::FLAGS], carrier[keys::SAMPLED])
|
71
108
|
|
72
|
-
return nil if span_id.nil? || trace_id.nil?
|
73
109
|
return nil if span_id.zero? || trace_id.zero?
|
74
110
|
|
75
111
|
SpanContext.new(
|
@@ -91,6 +127,34 @@ module Jaeger
|
|
91
127
|
private_class_method :parse_flags
|
92
128
|
end
|
93
129
|
|
130
|
+
class TraceContextRackCodec
|
131
|
+
# Internal regex used to identify the TraceContext version
|
132
|
+
VERSION_PATTERN = /^([0-9a-fA-F]{2})-(.+)$/.freeze
|
133
|
+
|
134
|
+
# Internal regex used to parse fields in version 0
|
135
|
+
HEADER_V0_PATTERN = /^([0-9a-fA-F]{32})-([0-9a-fA-F]{16})(-([0-9a-fA-F]{2}))?$/.freeze
|
136
|
+
|
137
|
+
def self.extract(carrier)
|
138
|
+
header_value = carrier['HTTP_TRACEPARENT']
|
139
|
+
|
140
|
+
version_match = VERSION_PATTERN.match(header_value)
|
141
|
+
return nil unless version_match
|
142
|
+
|
143
|
+
# We currently only support version 0
|
144
|
+
return nil if version_match[1].to_i(16) != 0
|
145
|
+
|
146
|
+
match = HEADER_V0_PATTERN.match(version_match[2])
|
147
|
+
return nil unless match
|
148
|
+
|
149
|
+
trace_id = TraceId.base16_hex_id_to_uint128(match[1])
|
150
|
+
span_id = TraceId.base16_hex_id_to_uint64(match[2])
|
151
|
+
flags = TraceId.base16_hex_id_to_uint64(match[4])
|
152
|
+
return nil if trace_id.zero? || span_id.zero?
|
153
|
+
|
154
|
+
SpanContext.new(trace_id: trace_id, span_id: span_id, flags: flags)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
94
158
|
DEFAULT_EXTRACTORS = {
|
95
159
|
OpenTracing::FORMAT_TEXT_MAP => JaegerTextMapCodec,
|
96
160
|
OpenTracing::FORMAT_BINARY => JaegerBinaryCodec,
|
data/lib/jaeger/http_sender.rb
CHANGED
@@ -4,7 +4,7 @@ require 'logger'
|
|
4
4
|
|
5
5
|
module Jaeger
|
6
6
|
class HttpSender
|
7
|
-
def initialize(url:, headers: {},
|
7
|
+
def initialize(url:, encoder:, headers: {}, logger: Logger.new($stdout))
|
8
8
|
@encoder = encoder
|
9
9
|
@logger = logger
|
10
10
|
|
@@ -21,8 +21,8 @@ module Jaeger
|
|
21
21
|
batch = @encoder.encode(spans)
|
22
22
|
@transport.write(@serializer.serialize(batch))
|
23
23
|
@transport.flush
|
24
|
-
rescue StandardError =>
|
25
|
-
@logger.error("Failure while sending a batch of spans: #{
|
24
|
+
rescue StandardError => e
|
25
|
+
@logger.error("Failure while sending a batch of spans: #{e}")
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/jaeger/injectors.rb
CHANGED
@@ -51,18 +51,32 @@ module Jaeger
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
class TraceContextRackCodec
|
55
|
+
def self.inject(span_context, carrier)
|
56
|
+
flags = span_context.sampled? || span_context.debug? ? 1 : 0
|
57
|
+
|
58
|
+
carrier['traceparent'] = format(
|
59
|
+
'%<version>s-%<trace_id>s-%<span_id>s-%<flags>s',
|
60
|
+
version: '00',
|
61
|
+
trace_id: span_context.trace_id.to_s(16).rjust(32, '0'),
|
62
|
+
span_id: span_context.span_id.to_s(16).rjust(16, '0'),
|
63
|
+
flags: flags.to_s(16).rjust(2, '0')
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
54
68
|
DEFAULT_INJECTORS = {
|
55
69
|
OpenTracing::FORMAT_TEXT_MAP => JaegerTextMapCodec,
|
56
70
|
OpenTracing::FORMAT_BINARY => JaegerBinaryCodec,
|
57
71
|
OpenTracing::FORMAT_RACK => JaegerRackCodec
|
58
72
|
}.freeze
|
59
73
|
|
60
|
-
def self.prepare(
|
61
|
-
DEFAULT_INJECTORS.reduce(
|
62
|
-
|
63
|
-
|
74
|
+
def self.prepare(injectors)
|
75
|
+
DEFAULT_INJECTORS.reduce(injectors) do |acc, (format, default)|
|
76
|
+
provided_injectors = Array(injectors[format])
|
77
|
+
provided_injectors += [default] if provided_injectors.empty?
|
64
78
|
|
65
|
-
acc.merge(format =>
|
79
|
+
acc.merge(format => provided_injectors)
|
66
80
|
end
|
67
81
|
end
|
68
82
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jaeger
|
4
|
+
# Executes a given block periodically. The block will be executed only once
|
5
|
+
# when interval is set to 0.
|
6
|
+
class RecurringExecutor
|
7
|
+
def initialize(interval:)
|
8
|
+
@interval = interval
|
9
|
+
end
|
10
|
+
|
11
|
+
def start(&block)
|
12
|
+
raise 'Already running' if @thread
|
13
|
+
|
14
|
+
@thread = Thread.new do
|
15
|
+
if @interval <= 0
|
16
|
+
yield
|
17
|
+
else
|
18
|
+
loop do
|
19
|
+
yield
|
20
|
+
sleep @interval
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def running?
|
27
|
+
@thread&.alive?
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
@thread.kill
|
32
|
+
@thread = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -12,27 +12,34 @@ module Jaeger
|
|
12
12
|
# emitted, ie. if is_sampled() for both samplers return true, the tags
|
13
13
|
# for Probabilistic sampler will be used.
|
14
14
|
class GuaranteedThroughputProbabilistic
|
15
|
-
attr_reader :tags
|
15
|
+
attr_reader :tags, :probabilistic_sampler, :lower_bound_sampler
|
16
16
|
|
17
17
|
def initialize(lower_bound:, rate:, lower_bound_sampler: nil)
|
18
18
|
@probabilistic_sampler = Probabilistic.new(rate: rate)
|
19
19
|
@lower_bound_sampler = lower_bound_sampler || RateLimiting.new(max_traces_per_second: lower_bound)
|
20
20
|
@lower_bound_tags = {
|
21
21
|
'sampler.type' => 'lowerbound',
|
22
|
-
'sampler.param' =>
|
22
|
+
'sampler.param' => rate
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
26
|
+
def update(lower_bound:, rate:)
|
27
|
+
is_updated = @probabilistic_sampler.update(rate: rate)
|
28
|
+
is_updated = @lower_bound_sampler.update(max_traces_per_second: lower_bound) || is_updated
|
29
|
+
@lower_bound_tags['sampler.param'] = rate
|
30
|
+
is_updated
|
31
|
+
end
|
32
|
+
|
33
|
+
def sample(...)
|
34
|
+
is_sampled, probabilistic_tags = @probabilistic_sampler.sample(...)
|
28
35
|
if is_sampled
|
29
36
|
# We still call lower_bound_sampler to update the rate limiter budget
|
30
|
-
@lower_bound_sampler.sample
|
37
|
+
@lower_bound_sampler.sample(...)
|
31
38
|
|
32
39
|
return [is_sampled, probabilistic_tags]
|
33
40
|
end
|
34
41
|
|
35
|
-
is_sampled, _tags = @lower_bound_sampler.sample
|
42
|
+
is_sampled, _tags = @lower_bound_sampler.sample(...)
|
36
43
|
[is_sampled, @lower_bound_tags]
|
37
44
|
end
|
38
45
|
end
|
@@ -10,37 +10,67 @@ module Jaeger
|
|
10
10
|
DEFAULT_SAMPLING_PROBABILITY = 0.001
|
11
11
|
DEFAULT_LOWER_BOUND = 1.0 / (10.0 * 60.0) # sample once every 10 minutes'
|
12
12
|
|
13
|
+
attr_reader :default_sampling_probability, :lower_bound, :samplers
|
14
|
+
|
13
15
|
def initialize(strategies:, max_operations:)
|
14
16
|
@max_operations = max_operations
|
17
|
+
@samplers = {}
|
18
|
+
update(strategies: strategies)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(strategies:)
|
22
|
+
is_updated = false
|
23
|
+
|
15
24
|
@default_sampling_probability =
|
16
25
|
strategies[:default_sampling_probability] || DEFAULT_SAMPLING_PROBABILITY
|
17
|
-
@lower_bound =
|
26
|
+
@lower_bound =
|
27
|
+
strategies[:default_lower_bound_traces_per_second] || DEFAULT_LOWER_BOUND
|
18
28
|
|
19
|
-
@default_sampler
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
sampler = GuaranteedThroughputProbabilistic.new(
|
24
|
-
lower_bound: @lower_bound,
|
25
|
-
rate: rate
|
26
|
-
)
|
27
|
-
acc.merge(operation => sampler)
|
29
|
+
if @default_sampler
|
30
|
+
is_updated = @default_sampler.update(rate: @default_sampling_probability)
|
31
|
+
else
|
32
|
+
@default_sampler = Probabilistic.new(rate: @default_sampling_probability)
|
28
33
|
end
|
34
|
+
|
35
|
+
update_operation_strategies(strategies) || is_updated
|
29
36
|
end
|
30
37
|
|
31
|
-
def sample
|
38
|
+
def sample(opts)
|
32
39
|
operation_name = opts.fetch(:operation_name)
|
33
40
|
sampler = @samplers[operation_name]
|
34
|
-
return sampler.sample
|
41
|
+
return sampler.sample(opts) if sampler
|
35
42
|
|
36
|
-
return @default_sampler.sample
|
43
|
+
return @default_sampler.sample(opts) if @samplers.length >= @max_operations
|
37
44
|
|
38
45
|
sampler = GuaranteedThroughputProbabilistic.new(
|
39
46
|
lower_bound: @lower_bound,
|
40
47
|
rate: @default_sampling_probability
|
41
48
|
)
|
42
49
|
@samplers[operation_name] = sampler
|
43
|
-
sampler.sample
|
50
|
+
sampler.sample(opts)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def update_operation_strategies(strategies)
|
56
|
+
is_updated = false
|
57
|
+
|
58
|
+
(strategies[:per_operation_strategies] || []).each do |strategy|
|
59
|
+
operation = strategy.fetch(:operation)
|
60
|
+
rate = strategy.fetch(:probabilistic_sampling).fetch(:sampling_rate)
|
61
|
+
|
62
|
+
if (sampler = @samplers[operation])
|
63
|
+
is_updated = sampler.update(lower_bound: @lower_bound, rate: rate) || is_updated
|
64
|
+
else
|
65
|
+
@samplers[operation] = GuaranteedThroughputProbabilistic.new(
|
66
|
+
lower_bound: @lower_bound,
|
67
|
+
rate: rate
|
68
|
+
)
|
69
|
+
is_updated = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
is_updated
|
44
74
|
end
|
45
75
|
end
|
46
76
|
end
|
@@ -6,19 +6,33 @@ module Jaeger
|
|
6
6
|
#
|
7
7
|
# Sample a portion of traces using trace_id as the random decision
|
8
8
|
class Probabilistic
|
9
|
+
attr_reader :rate
|
10
|
+
|
9
11
|
def initialize(rate: 0.001)
|
12
|
+
update(rate: rate)
|
13
|
+
end
|
14
|
+
|
15
|
+
def update(rate:)
|
10
16
|
if rate < 0.0 || rate > 1.0
|
11
17
|
raise "Sampling rate must be between 0.0 and 1.0, got #{rate.inspect}"
|
12
18
|
end
|
13
19
|
|
20
|
+
new_boundary = TraceId::TRACE_ID_UPPER_BOUND * rate
|
21
|
+
return false if @boundary == new_boundary
|
22
|
+
|
23
|
+
@rate = rate
|
14
24
|
@boundary = TraceId::TRACE_ID_UPPER_BOUND * rate
|
15
25
|
@tags = {
|
16
26
|
'sampler.type' => 'probabilistic',
|
17
27
|
'sampler.param' => rate
|
18
28
|
}
|
29
|
+
|
30
|
+
true
|
19
31
|
end
|
20
32
|
|
21
|
-
def sample
|
33
|
+
def sample(opts)
|
34
|
+
trace_id = opts.fetch(:trace_id)
|
35
|
+
|
22
36
|
[@boundary >= trace_id, @tags]
|
23
37
|
end
|
24
38
|
end
|
@@ -8,24 +8,42 @@ module Jaeger
|
|
8
8
|
# well, but if requests are bursty, especially sub-second, then a number
|
9
9
|
# of sequential requests can be sampled each second.
|
10
10
|
class RateLimiting
|
11
|
-
attr_reader :tags
|
11
|
+
attr_reader :tags, :max_traces_per_second
|
12
12
|
|
13
13
|
def initialize(max_traces_per_second: 10)
|
14
|
+
update(max_traces_per_second: max_traces_per_second)
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(max_traces_per_second:)
|
14
18
|
if max_traces_per_second < 0.0
|
15
19
|
raise "max_traces_per_second must not be negative, got #{max_traces_per_second}"
|
16
20
|
end
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
max_balance: [max_traces_per_second, 1.0].max
|
21
|
-
)
|
22
|
+
return false if max_traces_per_second == @max_traces_per_second
|
23
|
+
|
22
24
|
@tags = {
|
23
25
|
'sampler.type' => 'ratelimiting',
|
24
26
|
'sampler.param' => max_traces_per_second
|
25
27
|
}
|
28
|
+
@max_traces_per_second = max_traces_per_second
|
29
|
+
max_balance = [max_traces_per_second, 1.0].max
|
30
|
+
|
31
|
+
if @rate_limiter
|
32
|
+
@rate_limiter.update(
|
33
|
+
credits_per_second: max_traces_per_second,
|
34
|
+
max_balance: max_balance
|
35
|
+
)
|
36
|
+
else
|
37
|
+
@rate_limiter = RateLimiter.new(
|
38
|
+
credits_per_second: max_traces_per_second,
|
39
|
+
max_balance: [max_traces_per_second, 1.0].max
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
true
|
26
44
|
end
|
27
45
|
|
28
|
-
def sample
|
46
|
+
def sample(*)
|
29
47
|
[@rate_limiter.check_credit(1.0), @tags]
|
30
48
|
end
|
31
49
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jaeger
|
4
|
+
module Samplers
|
5
|
+
class RemoteControlled
|
6
|
+
class InstructionsFetcher
|
7
|
+
FetchFailed = Class.new(StandardError)
|
8
|
+
|
9
|
+
def initialize(host:, port:, service_name:)
|
10
|
+
@host = host
|
11
|
+
@port = port
|
12
|
+
@service_name = service_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def fetch
|
16
|
+
http = Net::HTTP.new(@host, @port)
|
17
|
+
path = "/sampling?service=#{CGI.escape(@service_name)}"
|
18
|
+
response =
|
19
|
+
begin
|
20
|
+
http.request(Net::HTTP::Get.new(path))
|
21
|
+
rescue StandardError => e
|
22
|
+
raise FetchFailed, e.inspect
|
23
|
+
end
|
24
|
+
|
25
|
+
unless response.is_a?(Net::HTTPSuccess)
|
26
|
+
raise FetchFailed, "Unsuccessful response (code=#{response.code})"
|
27
|
+
end
|
28
|
+
|
29
|
+
JSON.parse(response.body)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'remote_controlled/instructions_fetcher'
|
4
|
+
|
5
|
+
module Jaeger
|
6
|
+
module Samplers
|
7
|
+
class RemoteControlled
|
8
|
+
DEFAULT_REFRESH_INTERVAL = 60
|
9
|
+
DEFAULT_SAMPLING_HOST = 'localhost'
|
10
|
+
DEFAULT_SAMPLING_PORT = 5778
|
11
|
+
|
12
|
+
attr_reader :sampler
|
13
|
+
|
14
|
+
def initialize(opts = {})
|
15
|
+
@sampler = opts.fetch(:sampler, Probabilistic.new)
|
16
|
+
@logger = opts.fetch(:logger, Logger.new($stdout))
|
17
|
+
|
18
|
+
@poll_executor = opts[:poll_executor] || begin
|
19
|
+
refresh_interval = opts.fetch(:refresh_interval, DEFAULT_REFRESH_INTERVAL)
|
20
|
+
RecurringExecutor.new(interval: refresh_interval)
|
21
|
+
end
|
22
|
+
|
23
|
+
@instructions_fetcher = opts[:instructions_fetcher] || begin
|
24
|
+
service_name = opts.fetch(:service_name)
|
25
|
+
host = opts.fetch(:host, DEFAULT_SAMPLING_HOST)
|
26
|
+
port = opts.fetch(:port, DEFAULT_SAMPLING_PORT)
|
27
|
+
InstructionsFetcher.new(host: host, port: port, service_name: service_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sample(*args)
|
32
|
+
@poll_executor.start(&method(:poll)) unless @poll_executor.running?
|
33
|
+
|
34
|
+
@sampler.sample(*args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def poll
|
38
|
+
@logger.debug 'Fetching sampling strategy'
|
39
|
+
|
40
|
+
instructions = @instructions_fetcher.fetch
|
41
|
+
handle_instructions(instructions)
|
42
|
+
rescue InstructionsFetcher::FetchFailed => e
|
43
|
+
@logger.warn "Fetching sampling strategy failed: #{e.message}"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def handle_instructions(instructions)
|
49
|
+
if instructions['operationSampling']
|
50
|
+
update_per_operation_sampler(instructions['operationSampling'])
|
51
|
+
else
|
52
|
+
update_rate_limiting_or_probabilistic_sampler(instructions['strategyType'], instructions)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_per_operation_sampler(instructions)
|
57
|
+
strategies = normalize(instructions)
|
58
|
+
|
59
|
+
if @sampler.is_a?(PerOperation)
|
60
|
+
@sampler.update(strategies: strategies)
|
61
|
+
else
|
62
|
+
@sampler = PerOperation.new(strategies: strategies, max_operations: 2000)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def normalize(instructions)
|
67
|
+
{
|
68
|
+
default_sampling_probability: instructions['defaultSamplingProbability'],
|
69
|
+
default_lower_bound_traces_per_second: instructions['defaultLowerBoundTracesPerSecond'],
|
70
|
+
per_operation_strategies: instructions['perOperationStrategies'].map do |strategy|
|
71
|
+
{
|
72
|
+
operation: strategy['operation'],
|
73
|
+
probabilistic_sampling: {
|
74
|
+
sampling_rate: strategy['probabilisticSampling']['samplingRate']
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def update_rate_limiting_or_probabilistic_sampler(strategy, instructions)
|
82
|
+
case strategy
|
83
|
+
when 'PROBABILISTIC'
|
84
|
+
update_probabilistic_strategy(instructions['probabilisticSampling'])
|
85
|
+
when 'RATE_LIMITING'
|
86
|
+
update_rate_limiting_strategy(instructions['rateLimitingSampling'])
|
87
|
+
else
|
88
|
+
@logger.warn "Unknown sampling strategy #{strategy}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_probabilistic_strategy(instructions)
|
93
|
+
rate = instructions['samplingRate']
|
94
|
+
return unless rate
|
95
|
+
|
96
|
+
if @sampler.is_a?(Probabilistic)
|
97
|
+
@sampler.update(rate: rate)
|
98
|
+
@logger.info "Updated Probabilistic sampler (rate=#{rate})"
|
99
|
+
else
|
100
|
+
@sampler = Probabilistic.new(rate: rate)
|
101
|
+
@logger.info "Updated sampler to Probabilistic (rate=#{rate})"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_rate_limiting_strategy(instructions)
|
106
|
+
max_traces_per_second = instructions['maxTracesPerSecond']
|
107
|
+
return unless max_traces_per_second
|
108
|
+
|
109
|
+
if @sampler.is_a?(RateLimiting)
|
110
|
+
@sampler.update(max_traces_per_second: max_traces_per_second)
|
111
|
+
@logger.info "Updated Ratelimiting sampler (max_traces_per_second=#{max_traces_per_second})"
|
112
|
+
else
|
113
|
+
@sampler = RateLimiting.new(max_traces_per_second: max_traces_per_second)
|
114
|
+
@logger.info "Updated sampler to Ratelimiting (max_traces_per_second=#{max_traces_per_second})"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/jaeger/samplers.rb
CHANGED
data/lib/jaeger/scope.rb
CHANGED
@@ -23,6 +23,7 @@ module Jaeger
|
|
23
23
|
# updating the ScopeManager#active in the process.
|
24
24
|
def close
|
25
25
|
raise "Tried to close already closed span: #{inspect}" if @closed
|
26
|
+
|
26
27
|
@closed = true
|
27
28
|
|
28
29
|
@span.finish if @finish_on_close
|
@@ -30,8 +31,8 @@ module Jaeger
|
|
30
31
|
|
31
32
|
if removed_scope != self # rubocop:disable Style/GuardClause
|
32
33
|
raise 'Removed non-active scope, ' \
|
33
|
-
|
34
|
-
|
34
|
+
"removed: #{removed_scope.inspect}, "\
|
35
|
+
"expected: #{inspect}"
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
data/lib/jaeger/scope_manager.rb
CHANGED
data/lib/jaeger/span.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'span/thrift_tag_builder'
|
4
3
|
require_relative 'span/thrift_log_builder'
|
5
4
|
|
6
5
|
module Jaeger
|
@@ -35,7 +34,7 @@ module Jaeger
|
|
35
34
|
# a String, Numeric, or Boolean it will be encoded with to_s
|
36
35
|
def set_tag(key, value)
|
37
36
|
if key == 'sampling.priority'
|
38
|
-
if value.to_i
|
37
|
+
if value.to_i.positive?
|
39
38
|
return self if @context.debug?
|
40
39
|
|
41
40
|
@context.flags = @context.flags | SpanContext::Flags::SAMPLED | SpanContext::Flags::DEBUG
|
@@ -72,9 +71,9 @@ module Jaeger
|
|
72
71
|
# Add a log entry to this span
|
73
72
|
#
|
74
73
|
# @deprecated Use {#log_kv} instead.
|
75
|
-
def log(
|
74
|
+
def log(...)
|
76
75
|
warn 'Span#log is deprecated. Please use Span#log_kv instead.'
|
77
|
-
log_kv(
|
76
|
+
log_kv(...)
|
78
77
|
end
|
79
78
|
|
80
79
|
# Add a log entry to this span
|
data/lib/jaeger/span_context.rb
CHANGED
@@ -19,10 +19,10 @@ module Jaeger
|
|
19
19
|
)
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
attr_accessor :flags
|
23
|
+
attr_reader :span_id, :parent_id, :trace_id, :baggage
|
24
24
|
|
25
|
-
def initialize(span_id:, parent_id: 0,
|
25
|
+
def initialize(span_id:, trace_id:, flags:, parent_id: 0, baggage: {})
|
26
26
|
@span_id = span_id
|
27
27
|
@parent_id = parent_id
|
28
28
|
@trace_id = trace_id
|