dox-jaeger-client 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +33 -0
- data/.gitignore +12 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +61 -0
- data/.rubocop_todo.yml +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/Makefile +1 -0
- data/README.md +210 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/crossdock/Dockerfile +31 -0
- data/crossdock/Gemfile +6 -0
- data/crossdock/Gemfile.lock +37 -0
- data/crossdock/docker-compose.yml +68 -0
- data/crossdock/jaeger-docker-compose.yml +53 -0
- data/crossdock/rules.mk +35 -0
- data/crossdock/server +175 -0
- data/jaeger-client.gemspec +37 -0
- data/lib/jaeger/client/version.rb +7 -0
- data/lib/jaeger/client.rb +77 -0
- data/lib/jaeger/encoders/thrift_encoder.rb +173 -0
- data/lib/jaeger/extractors.rb +173 -0
- data/lib/jaeger/http_sender.rb +28 -0
- data/lib/jaeger/injectors.rb +83 -0
- data/lib/jaeger/rate_limiter.rb +61 -0
- data/lib/jaeger/recurring_executor.rb +35 -0
- data/lib/jaeger/reporters/composite_reporter.rb +17 -0
- data/lib/jaeger/reporters/in_memory_reporter.rb +30 -0
- data/lib/jaeger/reporters/logging_reporter.rb +22 -0
- data/lib/jaeger/reporters/null_reporter.rb +11 -0
- data/lib/jaeger/reporters/remote_reporter/buffer.rb +29 -0
- data/lib/jaeger/reporters/remote_reporter.rb +42 -0
- data/lib/jaeger/reporters.rb +7 -0
- data/lib/jaeger/samplers/const.rb +24 -0
- data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +47 -0
- data/lib/jaeger/samplers/per_operation.rb +77 -0
- data/lib/jaeger/samplers/probabilistic.rb +40 -0
- data/lib/jaeger/samplers/rate_limiting.rb +51 -0
- 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 +8 -0
- data/lib/jaeger/scope.rb +39 -0
- data/lib/jaeger/scope_manager/scope_identifier.rb +13 -0
- data/lib/jaeger/scope_manager/scope_stack.rb +33 -0
- data/lib/jaeger/scope_manager.rb +48 -0
- data/lib/jaeger/span/thrift_log_builder.rb +18 -0
- data/lib/jaeger/span.rb +97 -0
- data/lib/jaeger/span_context.rb +57 -0
- data/lib/jaeger/thrift_tag_builder.rb +42 -0
- data/lib/jaeger/trace_id.rb +48 -0
- data/lib/jaeger/tracer.rb +214 -0
- data/lib/jaeger/udp_sender/transport.rb +41 -0
- data/lib/jaeger/udp_sender.rb +26 -0
- data/script/create_follows_from_trace +51 -0
- data/script/create_trace +52 -0
- data/thrift/agent.thrift +32 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent.rb +118 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent_constants.rb +15 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent_types.rb +17 -0
- data/thrift/gen-rb/jaeger/thrift/agent.rb +116 -0
- data/thrift/gen-rb/jaeger/thrift/agent_constants.rb +13 -0
- data/thrift/gen-rb/jaeger/thrift/agent_types.rb +15 -0
- data/thrift/gen-rb/jaeger/thrift/collector.rb +82 -0
- data/thrift/gen-rb/jaeger/thrift/jaeger_constants.rb +13 -0
- data/thrift/gen-rb/jaeger/thrift/jaeger_types.rb +211 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkin_collector.rb +84 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_constants.rb +41 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_types.rb +220 -0
- data/thrift/jaeger.thrift +88 -0
- data/thrift/zipkincore.thrift +300 -0
- metadata +260 -0
data/crossdock/server
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
require 'sinatra/base'
|
6
|
+
require 'webrick'
|
7
|
+
require 'jaeger/client'
|
8
|
+
require 'net/http'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
class HealthServer < Sinatra::Application
|
12
|
+
get '/' do
|
13
|
+
status 200
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class HttpServer < Sinatra::Application
|
18
|
+
post '/start_trace' do
|
19
|
+
puts "Got request to start trace: #{trace_request}"
|
20
|
+
|
21
|
+
parent_context = tracer.extract(OpenTracing::FORMAT_RACK, request.env)
|
22
|
+
server_span = tracer.start_span('/start_trace', child_of: parent_context)
|
23
|
+
|
24
|
+
server_span.set_baggage_item('crossdock-baggage-key', trace_request['baggage'])
|
25
|
+
if trace_request.key?('sampled')
|
26
|
+
server_span.set_tag('sampling.priority', trace_request['sampled'] ? 1 : 0)
|
27
|
+
end
|
28
|
+
|
29
|
+
response = {
|
30
|
+
span: observe_span(server_span),
|
31
|
+
notImplementedError: ''
|
32
|
+
}
|
33
|
+
|
34
|
+
if trace_request['downstream']
|
35
|
+
downstream = trace_request['downstream']
|
36
|
+
transport = downstream['transport']
|
37
|
+
|
38
|
+
response[:downstream] =
|
39
|
+
case transport
|
40
|
+
when 'HTTP'
|
41
|
+
call_downstream_http(downstream, server_span)
|
42
|
+
when 'DUMMY'
|
43
|
+
{ notImplementedError: 'Dummy has not been implemented' }
|
44
|
+
else
|
45
|
+
{ notImplementedError: "Unrecognized transport received: #{transport}" }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "Response: #{response}"
|
50
|
+
|
51
|
+
server_span.finish
|
52
|
+
body JSON.dump(response)
|
53
|
+
end
|
54
|
+
|
55
|
+
post '/join_trace' do
|
56
|
+
puts 'Got request to join trace' \
|
57
|
+
"\n Params: #{trace_request}" \
|
58
|
+
"\n Headers: #{request_headers(request)}"
|
59
|
+
|
60
|
+
parent_context = tracer.extract(OpenTracing::FORMAT_RACK, request.env)
|
61
|
+
server_span = tracer.start_span('/join_trace', child_of: parent_context)
|
62
|
+
|
63
|
+
response = {
|
64
|
+
span: observe_span(server_span),
|
65
|
+
notImplementedError: ''
|
66
|
+
}
|
67
|
+
|
68
|
+
if trace_request['downstream']
|
69
|
+
downstream = trace_request['downstream']
|
70
|
+
transport = downstream['transport']
|
71
|
+
|
72
|
+
response[:downstream] =
|
73
|
+
case transport
|
74
|
+
when 'HTTP'
|
75
|
+
call_downstream_http(downstream, server_span)
|
76
|
+
when 'DUMMY'
|
77
|
+
{ notImplementedError: 'Dummy has not been implemented' }
|
78
|
+
else
|
79
|
+
{ notImplementedError: "Unrecognized transport received: #{transport}" }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "Response: #{response}"
|
84
|
+
|
85
|
+
server_span.finish
|
86
|
+
body JSON.dump(response)
|
87
|
+
end
|
88
|
+
|
89
|
+
post '/create_traces' do
|
90
|
+
puts "Got request to create traces: #{trace_request}"
|
91
|
+
|
92
|
+
trace_request['count'].times do
|
93
|
+
span = tracer.start_span(trace_request['operation'], tags: trace_request['tags'])
|
94
|
+
span.finish
|
95
|
+
end
|
96
|
+
|
97
|
+
status 200
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def tracer
|
103
|
+
@tracer ||= Jaeger::Client.build(
|
104
|
+
service_name: 'crossdock-ruby',
|
105
|
+
host: 'jaeger-agent',
|
106
|
+
port: 6831,
|
107
|
+
flush_interval: 1,
|
108
|
+
sampler: Jaeger::Samplers::Const.new(true)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def trace_request
|
113
|
+
@trace_request ||= begin
|
114
|
+
request.body.rewind
|
115
|
+
JSON.parse(request.body.read)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def observe_span(span)
|
120
|
+
if span
|
121
|
+
{
|
122
|
+
traceId: span.context.to_trace_id,
|
123
|
+
sampled: span.context.sampled?,
|
124
|
+
baggage: span.get_baggage_item('crossdock-baggage-key')
|
125
|
+
}
|
126
|
+
else
|
127
|
+
{
|
128
|
+
traceId: 'no span found',
|
129
|
+
sampled: false,
|
130
|
+
baggage: 'no span found'
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def call_downstream_http(downstream, server_span)
|
136
|
+
downstream_url = "http://#{downstream['host']}:#{downstream['port']}/join_trace"
|
137
|
+
|
138
|
+
client_span = tracer.start_span('client-span', child_of: server_span)
|
139
|
+
|
140
|
+
headers = { 'Content-Type' => 'application/json' }
|
141
|
+
tracer.inject(client_span.context, OpenTracing::FORMAT_RACK, headers)
|
142
|
+
|
143
|
+
response = Net::HTTP.post(
|
144
|
+
URI(downstream_url),
|
145
|
+
JSON.dump(
|
146
|
+
serverRole: downstream['serverRole'],
|
147
|
+
downstream: downstream['downstream']
|
148
|
+
),
|
149
|
+
headers
|
150
|
+
)
|
151
|
+
|
152
|
+
client_span.finish
|
153
|
+
|
154
|
+
if response.is_a?(Net::HTTPSuccess)
|
155
|
+
JSON.parse(response.body)
|
156
|
+
else
|
157
|
+
{ error: response.body }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def request_headers(request)
|
162
|
+
request.env.select do |key, _value|
|
163
|
+
key.start_with?('HTTP_')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
threads = []
|
169
|
+
threads << Thread.new do
|
170
|
+
Rack::Handler::WEBrick.run(HealthServer, Port: 8080, Host: '0.0.0.0')
|
171
|
+
end
|
172
|
+
threads << Thread.new do
|
173
|
+
Rack::Handler::WEBrick.run(HttpServer, Port: 8081, Host: '0.0.0.0')
|
174
|
+
end
|
175
|
+
threads.each(&:join)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
require 'jaeger/client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'dox-jaeger-client'
|
8
|
+
spec.version = Jaeger::Client::VERSION
|
9
|
+
spec.authors = ['Jeremiah Hemphill']
|
10
|
+
spec.email = ['jhemphill@doximity.com']
|
11
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
12
|
+
|
13
|
+
spec.summary = 'OpenTracing Tracer implementation for Jaeger in Ruby with ruby 3.2 support'
|
14
|
+
spec.description = 'OpenTracing Tracer implementation for Jaeger in Ruby with ruby 3.2 support'
|
15
|
+
spec.homepage = 'https://github.com/doximity/jaeger-client-ruby'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.required_ruby_version = ['>= 2.7', '< 3.3']
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.25'
|
30
|
+
spec.add_development_dependency 'rubocop-rake', '~> 0.6'
|
31
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.8'
|
32
|
+
spec.add_development_dependency 'timecop', '~> 0.9'
|
33
|
+
spec.add_development_dependency 'webmock', '~> 3.14'
|
34
|
+
|
35
|
+
spec.add_dependency 'opentracing', '~> 0.3'
|
36
|
+
spec.add_dependency 'thrift'
|
37
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push("#{File.dirname(__FILE__)}/../../thrift/gen-rb")
|
4
|
+
|
5
|
+
require 'opentracing'
|
6
|
+
require 'jaeger/thrift/agent'
|
7
|
+
require 'logger'
|
8
|
+
require 'time'
|
9
|
+
require 'net/http'
|
10
|
+
require 'cgi'
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
require_relative 'tracer'
|
14
|
+
require_relative 'span'
|
15
|
+
require_relative 'span_context'
|
16
|
+
require_relative 'scope'
|
17
|
+
require_relative 'scope_manager'
|
18
|
+
require_relative 'trace_id'
|
19
|
+
require_relative 'udp_sender'
|
20
|
+
require_relative 'http_sender'
|
21
|
+
require_relative 'reporters'
|
22
|
+
require_relative 'client/version'
|
23
|
+
require_relative 'samplers'
|
24
|
+
require_relative 'encoders/thrift_encoder'
|
25
|
+
require_relative 'injectors'
|
26
|
+
require_relative 'extractors'
|
27
|
+
require_relative 'rate_limiter'
|
28
|
+
require_relative 'thrift_tag_builder'
|
29
|
+
require_relative 'recurring_executor'
|
30
|
+
|
31
|
+
module Jaeger
|
32
|
+
module Client
|
33
|
+
# We initially had everything under Jaeger::Client namespace. This however
|
34
|
+
# was not very useful and was removed. These assignments are here for
|
35
|
+
# backwards compatibility. Fine to remove in the next major version.
|
36
|
+
UdpSender = Jaeger::UdpSender
|
37
|
+
HttpSender = Jaeger::HttpSender
|
38
|
+
Encoders = Jaeger::Encoders
|
39
|
+
Samplers = Jaeger::Samplers
|
40
|
+
Reporters = Jaeger::Reporters
|
41
|
+
Injectors = Jaeger::Injectors
|
42
|
+
Extractors = Jaeger::Extractors
|
43
|
+
|
44
|
+
DEFAULT_FLUSH_INTERVAL = 10
|
45
|
+
|
46
|
+
def self.build(service_name:,
|
47
|
+
host: '127.0.0.1',
|
48
|
+
port: 6831,
|
49
|
+
flush_interval: DEFAULT_FLUSH_INTERVAL,
|
50
|
+
sampler: Samplers::Const.new(true),
|
51
|
+
logger: Logger.new($stdout),
|
52
|
+
sender: nil,
|
53
|
+
reporter: nil,
|
54
|
+
injectors: {},
|
55
|
+
extractors: {},
|
56
|
+
tags: {})
|
57
|
+
encoder = Encoders::ThriftEncoder.new(service_name: service_name, tags: tags, logger: logger)
|
58
|
+
|
59
|
+
if sender
|
60
|
+
warn '[DEPRECATION] Passing `sender` directly to Jaeger::Client.build is deprecated.' \
|
61
|
+
'Please use `reporter` instead.'
|
62
|
+
end
|
63
|
+
|
64
|
+
reporter ||= Reporters::RemoteReporter.new(
|
65
|
+
sender: sender || UdpSender.new(host: host, port: port, encoder: encoder, logger: logger),
|
66
|
+
flush_interval: flush_interval
|
67
|
+
)
|
68
|
+
|
69
|
+
Tracer.new(
|
70
|
+
reporter: reporter,
|
71
|
+
sampler: sampler,
|
72
|
+
injectors: Injectors.prepare(injectors),
|
73
|
+
extractors: Extractors.prepare(extractors)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jaeger
|
4
|
+
module Encoders
|
5
|
+
class ThriftEncoder
|
6
|
+
def initialize(service_name:, logger:, tags: {})
|
7
|
+
@service_name = service_name
|
8
|
+
@logger = logger
|
9
|
+
@tags = prepare_tags(tags)
|
10
|
+
@process = Jaeger::Thrift::Process.new('serviceName' => @service_name, 'tags' => @tags)
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode(spans)
|
14
|
+
encode_batch(spans.map(&method(:encode_span)))
|
15
|
+
end
|
16
|
+
|
17
|
+
def encode_limited_size(spans, protocol_class, max_message_length)
|
18
|
+
batches = []
|
19
|
+
current_batch = []
|
20
|
+
current_batch_size = 0
|
21
|
+
|
22
|
+
max_spans_length = calculate_max_spans_length(protocol_class, max_message_length)
|
23
|
+
|
24
|
+
spans.each do |span|
|
25
|
+
encoded_span = encode_span(span)
|
26
|
+
span_size = message_size(encoded_span, protocol_class)
|
27
|
+
|
28
|
+
if span_size > max_spans_length
|
29
|
+
@logger.warn("Skip span #{span.operation_name} with size #{span_size}")
|
30
|
+
next
|
31
|
+
end
|
32
|
+
|
33
|
+
if (current_batch_size + span_size) > max_spans_length
|
34
|
+
batches << encode_batch(current_batch)
|
35
|
+
current_batch = []
|
36
|
+
current_batch_size = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
current_batch << encoded_span
|
40
|
+
current_batch_size += span_size
|
41
|
+
end
|
42
|
+
batches << encode_batch(current_batch) unless current_batch.empty?
|
43
|
+
batches
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def encode_batch(encoded_spans)
|
49
|
+
Jaeger::Thrift::Batch.new('process' => @process, 'spans' => encoded_spans)
|
50
|
+
end
|
51
|
+
|
52
|
+
def encode_span(span)
|
53
|
+
context = span.context
|
54
|
+
start_ts, duration = build_timestamps(span)
|
55
|
+
|
56
|
+
Jaeger::Thrift::Span.new(
|
57
|
+
'traceIdLow' => TraceId.uint64_id_to_int64(trace_id_to_low(context.trace_id)),
|
58
|
+
'traceIdHigh' => TraceId.uint64_id_to_int64(trace_id_to_high(context.trace_id)),
|
59
|
+
'spanId' => TraceId.uint64_id_to_int64(context.span_id),
|
60
|
+
'parentSpanId' => TraceId.uint64_id_to_int64(context.parent_id),
|
61
|
+
'operationName' => span.operation_name,
|
62
|
+
'references' => build_references(span.references || []),
|
63
|
+
'flags' => context.flags,
|
64
|
+
'startTime' => start_ts,
|
65
|
+
'duration' => duration,
|
66
|
+
'tags' => span.tags,
|
67
|
+
'logs' => span.logs
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_references(references)
|
72
|
+
references.map do |ref|
|
73
|
+
Jaeger::Thrift::SpanRef.new(
|
74
|
+
'refType' => span_ref_type(ref.type),
|
75
|
+
'traceIdLow' => TraceId.uint64_id_to_int64(trace_id_to_low(ref.context.trace_id)),
|
76
|
+
'traceIdHigh' => TraceId.uint64_id_to_int64(trace_id_to_high(ref.context.trace_id)),
|
77
|
+
'spanId' => TraceId.uint64_id_to_int64(ref.context.span_id)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_timestamps(span)
|
83
|
+
start_ts = (span.start_time.to_f * 1_000_000).to_i
|
84
|
+
end_ts = (span.end_time.to_f * 1_000_000).to_i
|
85
|
+
duration = end_ts - start_ts
|
86
|
+
[start_ts, duration]
|
87
|
+
end
|
88
|
+
|
89
|
+
def span_ref_type(type)
|
90
|
+
case type
|
91
|
+
when OpenTracing::Reference::CHILD_OF
|
92
|
+
Jaeger::Thrift::SpanRefType::CHILD_OF
|
93
|
+
when OpenTracing::Reference::FOLLOWS_FROM
|
94
|
+
Jaeger::Thrift::SpanRefType::FOLLOWS_FROM
|
95
|
+
else
|
96
|
+
warn "Jaeger::Client with format #{type} is not supported yet"
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def prepare_tags(tags)
|
102
|
+
with_default_tags = tags.dup
|
103
|
+
with_default_tags['jaeger.version'] = "Ruby-#{Jaeger::Client::VERSION}"
|
104
|
+
with_default_tags['hostname'] ||= Socket.gethostname
|
105
|
+
|
106
|
+
unless with_default_tags['ip']
|
107
|
+
ipv4 = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }
|
108
|
+
with_default_tags['ip'] = ipv4.ip_address unless ipv4.nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
with_default_tags.map do |key, value|
|
112
|
+
ThriftTagBuilder.build(key, value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the right most 64 bits of trace id
|
117
|
+
def trace_id_to_low(trace_id)
|
118
|
+
trace_id & TraceId::MAX_64BIT_UNSIGNED_INT
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns the left most 64 bits of trace id
|
122
|
+
def trace_id_to_high(trace_id)
|
123
|
+
(trace_id >> 64) & TraceId::MAX_64BIT_UNSIGNED_INT
|
124
|
+
end
|
125
|
+
|
126
|
+
class DummyTransport
|
127
|
+
attr_accessor :size
|
128
|
+
|
129
|
+
def initialize
|
130
|
+
@size = 0
|
131
|
+
end
|
132
|
+
|
133
|
+
def write(buf)
|
134
|
+
@size += buf.size
|
135
|
+
end
|
136
|
+
|
137
|
+
def flush
|
138
|
+
@size = 0
|
139
|
+
end
|
140
|
+
|
141
|
+
def close; end
|
142
|
+
end
|
143
|
+
|
144
|
+
def message_size(message, protocol_class)
|
145
|
+
transport = DummyTransport.new
|
146
|
+
protocol = protocol_class.new(transport)
|
147
|
+
message.write(protocol)
|
148
|
+
transport.size
|
149
|
+
end
|
150
|
+
|
151
|
+
# Compact protocol have dynamic size of list header
|
152
|
+
# https://erikvanoosten.github.io/thrift-missing-specification/#_list_and_set_2
|
153
|
+
BATCH_SPANS_SIZE_WINDOW = 4
|
154
|
+
|
155
|
+
def empty_batch_size_cache
|
156
|
+
@empty_batch_size_cache ||= {}
|
157
|
+
end
|
158
|
+
|
159
|
+
def caclulate_empty_batch_size(protocol_class)
|
160
|
+
empty_batch_size_cache[protocol_class] ||=
|
161
|
+
message_size(encode_batch([]), protocol_class) + BATCH_SPANS_SIZE_WINDOW
|
162
|
+
end
|
163
|
+
|
164
|
+
def calculate_max_spans_length(protocol_class, max_message_length)
|
165
|
+
empty_batch_size = caclulate_empty_batch_size(protocol_class)
|
166
|
+
max_spans_length = max_message_length - empty_batch_size
|
167
|
+
return max_spans_length if max_spans_length.positive?
|
168
|
+
|
169
|
+
raise "Batch header have size #{empty_batch_size}, but limit #{max_message_length}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jaeger
|
4
|
+
module Extractors
|
5
|
+
class SerializedJaegerTrace
|
6
|
+
def self.parse(trace)
|
7
|
+
return nil if !trace || trace == ''
|
8
|
+
|
9
|
+
trace_arguments = trace.split(':')
|
10
|
+
return nil if trace_arguments.size != 4
|
11
|
+
|
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
|
+
|
15
|
+
return nil if trace_id.zero? || span_id.zero?
|
16
|
+
|
17
|
+
SpanContext.new(
|
18
|
+
trace_id: trace_id,
|
19
|
+
parent_id: parent_id,
|
20
|
+
span_id: span_id,
|
21
|
+
flags: flags
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class JaegerTextMapCodec
|
27
|
+
def self.extract(carrier)
|
28
|
+
context = SerializedJaegerTrace.parse(carrier['uber-trace-id'])
|
29
|
+
return nil unless context
|
30
|
+
|
31
|
+
carrier.each do |key, value|
|
32
|
+
baggage_match = key.match(/\Auberctx-([\w-]+)\Z/)
|
33
|
+
if baggage_match
|
34
|
+
context.set_baggage_item(baggage_match[1], value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class JaegerRackCodec
|
43
|
+
def self.extract(carrier)
|
44
|
+
serialized_trace = carrier['HTTP_UBER_TRACE_ID']
|
45
|
+
serialized_trace = CGI.unescape(serialized_trace) if serialized_trace
|
46
|
+
context = SerializedJaegerTrace.parse(serialized_trace)
|
47
|
+
return nil unless context
|
48
|
+
|
49
|
+
carrier.each do |key, value|
|
50
|
+
baggage_match = key.match(/\AHTTP_UBERCTX_(\w+)\Z/)
|
51
|
+
if baggage_match
|
52
|
+
key = baggage_match[1].downcase.tr('_', '-')
|
53
|
+
context.set_baggage_item(key, CGI.unescape(value))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class JaegerBinaryCodec
|
62
|
+
def self.extract(_carrier)
|
63
|
+
warn 'Jaeger::Client with binary format is not supported yet'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
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
|
+
|
90
|
+
def self.extract(carrier)
|
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])
|
108
|
+
|
109
|
+
return nil if span_id.zero? || trace_id.zero?
|
110
|
+
|
111
|
+
SpanContext.new(
|
112
|
+
trace_id: trace_id,
|
113
|
+
parent_id: parent_id,
|
114
|
+
span_id: span_id,
|
115
|
+
flags: flags
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
# if the flags header is '1' then the sampled header should not be present
|
120
|
+
def self.parse_flags(flags_header, sampled_header)
|
121
|
+
if flags_header == '1'
|
122
|
+
Jaeger::SpanContext::Flags::DEBUG
|
123
|
+
else
|
124
|
+
TraceId.base16_hex_id_to_uint64(sampled_header)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
private_class_method :parse_flags
|
128
|
+
end
|
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
|
+
|
158
|
+
DEFAULT_EXTRACTORS = {
|
159
|
+
OpenTracing::FORMAT_TEXT_MAP => JaegerTextMapCodec,
|
160
|
+
OpenTracing::FORMAT_BINARY => JaegerBinaryCodec,
|
161
|
+
OpenTracing::FORMAT_RACK => JaegerRackCodec
|
162
|
+
}.freeze
|
163
|
+
|
164
|
+
def self.prepare(extractors)
|
165
|
+
DEFAULT_EXTRACTORS.reduce(extractors) do |acc, (format, default)|
|
166
|
+
provided_extractors = Array(extractors[format])
|
167
|
+
provided_extractors += [default] if provided_extractors.empty?
|
168
|
+
|
169
|
+
acc.merge(format => provided_extractors)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Jaeger
|
6
|
+
class HttpSender
|
7
|
+
def initialize(url:, encoder:, headers: {}, logger: Logger.new($stdout))
|
8
|
+
@encoder = encoder
|
9
|
+
@logger = logger
|
10
|
+
|
11
|
+
@uri = URI(url)
|
12
|
+
@uri.query = 'format=jaeger.thrift'
|
13
|
+
|
14
|
+
@transport = ::Thrift::HTTPClientTransport.new(@uri.to_s)
|
15
|
+
@transport.add_headers(headers)
|
16
|
+
|
17
|
+
@serializer = ::Thrift::Serializer.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def send_spans(spans)
|
21
|
+
batch = @encoder.encode(spans)
|
22
|
+
@transport.write(@serializer.serialize(batch))
|
23
|
+
@transport.flush
|
24
|
+
rescue StandardError => e
|
25
|
+
@logger.error("Failure while sending a batch of spans: #{e}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|