zipkin 1.5.0 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +2 -2
- data/README.md +10 -0
- data/lib/zipkin/async_reporter.rb +39 -0
- data/lib/zipkin/async_reporter/buffer.rb +27 -0
- data/lib/zipkin/encoders.rb +3 -0
- data/lib/zipkin/encoders/json_encoder.rb +104 -0
- data/lib/zipkin/encoders/json_encoder/log_annotations.rb +34 -0
- data/lib/zipkin/encoders/json_encoder/timestamp.rb +13 -0
- data/lib/zipkin/endpoint.rb +19 -15
- data/lib/zipkin/{json_client.rb → http_client.rb} +6 -26
- data/lib/zipkin/samplers.rb +4 -0
- data/lib/zipkin/samplers/const.rb +20 -0
- data/lib/zipkin/samplers/probabilistic.rb +21 -0
- data/lib/zipkin/span.rb +12 -11
- data/lib/zipkin/span_context.rb +3 -2
- data/lib/zipkin/trace_id.rb +21 -1
- data/lib/zipkin/tracer.rb +21 -22
- data/script/create_follows_from_trace +3 -5
- data/script/create_http_trace +56 -0
- data/script/create_trace +34 -20
- data/zipkin.gemspec +3 -3
- metadata +17 -11
- data/lib/zipkin/collector.rb +0 -71
- data/lib/zipkin/collector/log_annotations.rb +0 -27
- data/lib/zipkin/collector/timestamp.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d406755c3fae0a6ad07050d0bfa19a903ce5e5ca414ad0530d172061ef0e2a69
|
4
|
+
data.tar.gz: 8f377efd4748398e92dc6cf565f40819e25cf26ca0a0b2f9c119e21ed7ce36c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c94f44c0d12367b67bddef7c928fa27528a13eedddf4a1b99c4c3eec34553ee8a6c949dc43797e00ce13ba8a47f59b615f4779fd7375ea1d28e2dda1a1fc6058
|
7
|
+
data.tar.gz: 904580f268829d5699001cc260ed53c26def532e84e50bf6eb9b9e44c89daa5861482d77a24722b729d775c1b5dbb514812d4ff68ac0b242fbfbf873409baffc
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -31,6 +31,16 @@ end
|
|
31
31
|
|
32
32
|
See [opentracing-ruby](https://github.com/opentracing/opentracing-ruby) for more examples.
|
33
33
|
|
34
|
+
### Samplers
|
35
|
+
|
36
|
+
#### Const sampler
|
37
|
+
|
38
|
+
`Const` sampler always makes the same decision for new traces depending on the initialization value. Set `sampler` to: `Zipkin::Samplers::Const.new(true)` to mark all new traces as sampled.
|
39
|
+
|
40
|
+
#### Probabilistic sampler
|
41
|
+
|
42
|
+
`Probabilistic` sampler samples traces with probability equal to `rate` (must be between 0.0 and 1.0). Set `sampler` to `Zipkin::Samplers::Probabilistic.new(rate: 0.1)` to mark 10% of new traces as sampled.
|
43
|
+
|
34
44
|
## Development
|
35
45
|
|
36
46
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
require_relative './async_reporter/buffer'
|
6
|
+
|
7
|
+
module Zipkin
|
8
|
+
class AsyncReporter
|
9
|
+
def self.create(sender:, flush_interval:)
|
10
|
+
reporter = new(sender)
|
11
|
+
|
12
|
+
# start flush thread
|
13
|
+
Thread.new do
|
14
|
+
loop do
|
15
|
+
reporter.flush
|
16
|
+
sleep(flush_interval)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
reporter
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(sender)
|
24
|
+
@sender = sender
|
25
|
+
@buffer = Buffer.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def flush
|
29
|
+
spans = @buffer.retrieve
|
30
|
+
@sender.send_spans(spans) if spans.any?
|
31
|
+
spans
|
32
|
+
end
|
33
|
+
|
34
|
+
def report(span)
|
35
|
+
return unless span.context.sampled?
|
36
|
+
@buffer << span
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zipkin
|
4
|
+
class AsyncReporter
|
5
|
+
class Buffer
|
6
|
+
def initialize
|
7
|
+
@buffer = []
|
8
|
+
@mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(element)
|
12
|
+
@mutex.synchronize do
|
13
|
+
@buffer << element
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def retrieve
|
19
|
+
@mutex.synchronize do
|
20
|
+
elements = @buffer.dup
|
21
|
+
@buffer.clear
|
22
|
+
elements
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative 'json_encoder/timestamp'
|
6
|
+
require_relative 'json_encoder/log_annotations'
|
7
|
+
|
8
|
+
module Zipkin
|
9
|
+
module Encoders
|
10
|
+
class JsonEncoder
|
11
|
+
OT_KIND_TO_ZIPKIN_KIND = {
|
12
|
+
'server' => 'SERVER',
|
13
|
+
'client' => 'CLIENT',
|
14
|
+
'producer' => 'PRODUCER',
|
15
|
+
'consumer' => 'CONSUMER'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
CONTENT_TYPE = 'application/json'.freeze
|
19
|
+
|
20
|
+
module Fields
|
21
|
+
TRACE_ID = 'traceId'.freeze
|
22
|
+
SPAN_ID = 'id'.freeze
|
23
|
+
PARENT_ID = 'parentId'.freeze
|
24
|
+
OPERATION_NAME = 'name'.freeze
|
25
|
+
KIND = 'kind'.freeze
|
26
|
+
TIMESTAMP = 'timestamp'.freeze
|
27
|
+
DURATION = 'duration'.freeze
|
28
|
+
DEBUG = 'debug'.freeze
|
29
|
+
SHARED = 'shared'.freeze
|
30
|
+
LOCAL_ENDPOINT = 'localEndpoint'.freeze
|
31
|
+
REMOTE_ENDPOINT = 'remoteEndpoint'.freeze
|
32
|
+
ANNOTATIONS = 'annotations'.freeze
|
33
|
+
TAGS = 'tags'.freeze
|
34
|
+
|
35
|
+
module Endpoint
|
36
|
+
SERVICE_NAME = 'serviceName'.freeze
|
37
|
+
IPV4 = 'ipv4'.freeze
|
38
|
+
IPV6 = 'ipv6'.freeze
|
39
|
+
PORT = 'port'.freeze
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(local_endpoint)
|
44
|
+
@adapter = defined?(Oj) ? OjAdapter : JsonAdapter
|
45
|
+
@local_endpoint = serialize_endpoint(local_endpoint)
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_type
|
49
|
+
CONTENT_TYPE
|
50
|
+
end
|
51
|
+
|
52
|
+
def encode(spans)
|
53
|
+
@adapter.dump(spans.map(&method(:serialize)))
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def serialize(span)
|
59
|
+
finish_ts = Timestamp.create(span.end_time)
|
60
|
+
start_ts = Timestamp.create(span.start_time)
|
61
|
+
duration = finish_ts - start_ts
|
62
|
+
|
63
|
+
{
|
64
|
+
Fields::TRACE_ID => span.context.trace_id,
|
65
|
+
Fields::SPAN_ID => span.context.span_id,
|
66
|
+
Fields::PARENT_ID => span.context.parent_id,
|
67
|
+
Fields::OPERATION_NAME => span.operation_name,
|
68
|
+
Fields::KIND => OT_KIND_TO_ZIPKIN_KIND[span.tags[:'span.kind'] || 'server'],
|
69
|
+
Fields::TIMESTAMP => start_ts,
|
70
|
+
Fields::DURATION => duration,
|
71
|
+
Fields::DEBUG => false,
|
72
|
+
Fields::SHARED => false,
|
73
|
+
Fields::LOCAL_ENDPOINT => @local_endpoint,
|
74
|
+
Fields::REMOTE_ENDPOINT => serialize_endpoint(Endpoint.remote_endpoint(span)),
|
75
|
+
Fields::ANNOTATIONS => LogAnnotations.build(span),
|
76
|
+
Fields::TAGS => span.tags
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def serialize_endpoint(endpoint)
|
81
|
+
return nil unless endpoint
|
82
|
+
|
83
|
+
{
|
84
|
+
Fields::Endpoint::SERVICE_NAME => endpoint.service_name,
|
85
|
+
Fields::Endpoint::IPV4 => endpoint.ipv4,
|
86
|
+
Fields::Endpoint::IPV6 => endpoint.ipv6,
|
87
|
+
Fields::Endpoint::PORT => endpoint.port
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
module OjAdapter
|
92
|
+
def self.dump(payload)
|
93
|
+
Oj.dump(payload, mode: :object)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
module JsonAdapter
|
98
|
+
def self.dump(payload)
|
99
|
+
JSON.dump(payload)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zipkin
|
4
|
+
module Encoders
|
5
|
+
class JsonEncoder
|
6
|
+
module LogAnnotations
|
7
|
+
module Fields
|
8
|
+
TIMESTAMP = 'timestamp'.freeze
|
9
|
+
VALUE = 'value'.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.build(span)
|
13
|
+
span.logs.map do |log|
|
14
|
+
{
|
15
|
+
Fields::TIMESTAMP => Timestamp.create(log.fetch(:timestamp)),
|
16
|
+
Fields::VALUE => format_log_value(log)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.format_log_value(log)
|
22
|
+
if log.keys == %i[event timestamp]
|
23
|
+
log.fetch(:event)
|
24
|
+
else
|
25
|
+
log
|
26
|
+
.reject { |key, _value| key == :timestamp }
|
27
|
+
.map { |key, value| "#{key}=#{value}" }
|
28
|
+
.join(' ')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/zipkin/endpoint.rb
CHANGED
@@ -17,10 +17,10 @@ module Zipkin
|
|
17
17
|
end
|
18
18
|
|
19
19
|
module PeerInfo
|
20
|
-
SERVICE =
|
21
|
-
IPV4 =
|
22
|
-
IPV6 =
|
23
|
-
PORT =
|
20
|
+
SERVICE = 'peer.service'.freeze
|
21
|
+
IPV4 = 'peer.ipv4'.freeze
|
22
|
+
IPV6 = 'peer.ipv6'.freeze
|
23
|
+
PORT = 'peer.port'.freeze
|
24
24
|
|
25
25
|
def self.keys
|
26
26
|
[SERVICE, IPV4, IPV6, PORT]
|
@@ -28,34 +28,38 @@ module Zipkin
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.local_endpoint(service_name)
|
31
|
-
|
32
|
-
serviceName: service_name,
|
33
|
-
ipv4: LOCAL_IP
|
34
|
-
}
|
31
|
+
new(service_name: service_name, ipv4: LOCAL_IP)
|
35
32
|
end
|
36
33
|
|
37
34
|
def self.remote_endpoint(span)
|
38
35
|
tags = span.tags
|
39
|
-
kind = tags[
|
36
|
+
kind = tags['span.kind'] || SpanKind::SERVER
|
40
37
|
|
41
38
|
case kind
|
42
39
|
when SpanKind::SERVER, SpanKind::CLIENT
|
43
40
|
return nil if (tags.keys & PeerInfo.keys).empty?
|
44
41
|
|
45
|
-
|
46
|
-
|
42
|
+
new(
|
43
|
+
service_name: tags[PeerInfo::SERVICE],
|
47
44
|
ipv4: tags[PeerInfo::IPV4],
|
48
45
|
ipv6: tags[PeerInfo::IPV6],
|
49
46
|
port: tags[PeerInfo::PORT]
|
50
|
-
|
47
|
+
)
|
51
48
|
when SpanKind::PRODUCER, SpanKind::CONSUMER
|
52
|
-
|
53
|
-
serviceName: 'broker'
|
54
|
-
}
|
49
|
+
new(service_name: 'broker')
|
55
50
|
else
|
56
51
|
warn "Unkown span kind: #{kind}"
|
57
52
|
nil
|
58
53
|
end
|
59
54
|
end
|
55
|
+
|
56
|
+
def initialize(service_name: nil, ipv4: nil, ipv6: nil, port: nil)
|
57
|
+
@service_name = service_name
|
58
|
+
@ipv4 = ipv4
|
59
|
+
@ipv6 = ipv6
|
60
|
+
@port = port
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_reader :service_name, :ipv4, :ipv6, :port
|
60
64
|
end
|
61
65
|
end
|
@@ -2,43 +2,23 @@
|
|
2
2
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'uri'
|
5
|
-
require 'json'
|
6
5
|
|
7
6
|
module Zipkin
|
8
|
-
class
|
9
|
-
def initialize(url:,
|
10
|
-
@
|
11
|
-
@flush_interval = flush_interval
|
7
|
+
class HTTPClient
|
8
|
+
def initialize(url:, encoder:, logger:)
|
9
|
+
@encoder = encoder
|
12
10
|
@spans_uri = URI.parse("#{url}/api/v2/spans")
|
13
11
|
@logger = logger
|
14
12
|
end
|
15
13
|
|
16
|
-
def
|
17
|
-
@thread = Thread.new do
|
18
|
-
loop do
|
19
|
-
emit_batch(@collector.retrieve)
|
20
|
-
sleep @flush_interval
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def stop
|
26
|
-
@thread.terminate if @thread
|
27
|
-
emit_batch(@collector.retrieve)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def emit_batch(spans)
|
33
|
-
return if spans.empty?
|
34
|
-
|
14
|
+
def send_spans(spans)
|
35
15
|
http = Net::HTTP.new(@spans_uri.host, @spans_uri.port)
|
36
16
|
http.use_ssl = @spans_uri.scheme == 'https'
|
37
17
|
request = Net::HTTP::Post.new(
|
38
18
|
@spans_uri.request_uri,
|
39
|
-
'Content-Type' =>
|
19
|
+
'Content-Type' => @encoder.content_type
|
40
20
|
)
|
41
|
-
request.body =
|
21
|
+
request.body = @encoder.encode(spans)
|
42
22
|
response = http.request(request)
|
43
23
|
|
44
24
|
if response.code != '202'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zipkin
|
4
|
+
module Samplers
|
5
|
+
# Const sampler
|
6
|
+
#
|
7
|
+
# A sampler that always makes the same decision for new traces depending
|
8
|
+
# on the initialization value. Use `Zipkin::Samplers::Const.new(true)`
|
9
|
+
# to mark all new traces as sampled.
|
10
|
+
class Const
|
11
|
+
def initialize(decision)
|
12
|
+
@decision = decision
|
13
|
+
end
|
14
|
+
|
15
|
+
def sample?(*)
|
16
|
+
@decision
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zipkin
|
4
|
+
module Samplers
|
5
|
+
# Probabilistic sampler
|
6
|
+
#
|
7
|
+
# Sample a portion of traces using trace_id as the random decision
|
8
|
+
class Probabilistic
|
9
|
+
def initialize(rate: 0.001)
|
10
|
+
if rate < 0.0 || rate > 1.0
|
11
|
+
raise "Sampling rate must be between 0.0 and 1.0, got #{rate.inspect}"
|
12
|
+
end
|
13
|
+
@boundary = TraceId::TRACE_ID_UPPER_BOUND * rate
|
14
|
+
end
|
15
|
+
|
16
|
+
def sample?(trace_id:, **)
|
17
|
+
@boundary >= trace_id.to_i(16)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/zipkin/span.rb
CHANGED
@@ -4,30 +4,32 @@ module Zipkin
|
|
4
4
|
class Span
|
5
5
|
attr_accessor :operation_name
|
6
6
|
|
7
|
-
attr_reader :context, :start_time, :tags, :logs, :references
|
7
|
+
attr_reader :context, :start_time, :end_time, :tags, :logs, :references
|
8
8
|
|
9
9
|
# Creates a new {Span}
|
10
10
|
#
|
11
11
|
# @param context [SpanContext] the context of the span
|
12
12
|
# @param operation_name [String] the operation name
|
13
|
-
# @param
|
13
|
+
# @param reporter [#report] the span reporter
|
14
14
|
#
|
15
15
|
# @return [Span] a new Span
|
16
16
|
def initialize(
|
17
17
|
context,
|
18
18
|
operation_name,
|
19
|
-
|
19
|
+
reporter,
|
20
20
|
start_time: Time.now,
|
21
21
|
tags: {},
|
22
22
|
references: nil
|
23
23
|
)
|
24
24
|
@context = context
|
25
25
|
@operation_name = operation_name
|
26
|
-
@
|
26
|
+
@reporter = reporter
|
27
27
|
@start_time = start_time
|
28
|
-
@tags =
|
28
|
+
@tags = {}
|
29
29
|
@logs = []
|
30
30
|
@references = references
|
31
|
+
|
32
|
+
tags.each(&method(:set_tag))
|
31
33
|
end
|
32
34
|
|
33
35
|
# Set a tag value on this span
|
@@ -37,7 +39,7 @@ module Zipkin
|
|
37
39
|
# a String, Numeric, or Boolean it will be encoded with to_s
|
38
40
|
def set_tag(key, value)
|
39
41
|
sanitized_value = valid_tag_value?(value) ? value : value.to_s
|
40
|
-
@tags = @tags.merge(key.
|
42
|
+
@tags = @tags.merge(key.to_s => sanitized_value)
|
41
43
|
end
|
42
44
|
|
43
45
|
# Set a baggage item on the span
|
@@ -78,16 +80,15 @@ module Zipkin
|
|
78
80
|
#
|
79
81
|
# @param end_time [Time] custom end time, if not now
|
80
82
|
def finish(end_time: Time.now)
|
81
|
-
@
|
83
|
+
@end_time = end_time
|
84
|
+
@reporter.report(self)
|
82
85
|
end
|
83
86
|
|
84
87
|
private
|
85
88
|
|
89
|
+
# Zipkin supports only strings and numbers
|
86
90
|
def valid_tag_value?(value)
|
87
|
-
value.is_a?(String) ||
|
88
|
-
value.is_a?(Numeric) ||
|
89
|
-
value.is_a?(TrueClass) ||
|
90
|
-
value.is_a?(FalseClass)
|
91
|
+
value.is_a?(String) || value.is_a?(Numeric)
|
91
92
|
end
|
92
93
|
end
|
93
94
|
end
|
data/lib/zipkin/span_context.rb
CHANGED
@@ -3,9 +3,10 @@
|
|
3
3
|
module Zipkin
|
4
4
|
# SpanContext holds the data for a span that gets inherited to child spans
|
5
5
|
class SpanContext
|
6
|
-
def self.create_parent_context
|
6
|
+
def self.create_parent_context(sampler = Samplers::Const.new(true))
|
7
7
|
trace_id = TraceId.generate
|
8
|
-
|
8
|
+
sampled = sampler.sample?(trace_id: trace_id)
|
9
|
+
new(trace_id: trace_id, span_id: trace_id, sampled: sampled)
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.create_from_parent_context(span_context)
|
data/lib/zipkin/trace_id.rb
CHANGED
@@ -4,8 +4,28 @@ module Zipkin
|
|
4
4
|
module TraceId
|
5
5
|
TRACE_ID_UPPER_BOUND = 2**64
|
6
6
|
|
7
|
+
# Random number generator for generating IDs. This is an object that can
|
8
|
+
# respond to `#bytes` and uses the system PRNG. The current logic is
|
9
|
+
# compatible with Ruby 2.5 (which does not implement the `Random.bytes`
|
10
|
+
# class method) and with Ruby 3.0+ (which deprecates `Random::DEFAULT`).
|
11
|
+
# When we drop support for Ruby 2.5, this can simply be replaced with
|
12
|
+
# the class `Random`.
|
13
|
+
#
|
14
|
+
# @return [#bytes]
|
15
|
+
RANDOM = Random.respond_to?(:bytes) ? Random : Random::DEFAULT
|
16
|
+
|
17
|
+
# An invalid trace identifier, an 8-byte string with all zero bytes.
|
18
|
+
INVALID_TRACE_ID = ("\0" * 8).b
|
19
|
+
|
20
|
+
# Generates a valid trace identifier, an 8-byte string with at least one
|
21
|
+
# non-zero byte.
|
22
|
+
#
|
23
|
+
# @return [String] a valid trace ID.
|
7
24
|
def self.generate
|
8
|
-
|
25
|
+
loop do
|
26
|
+
id = RANDOM.bytes(8)
|
27
|
+
return id.unpack1('H*') if id != INVALID_TRACE_ID
|
28
|
+
end
|
9
29
|
end
|
10
30
|
end
|
11
31
|
end
|
data/lib/zipkin/tracer.rb
CHANGED
@@ -7,37 +7,36 @@ require_relative 'span'
|
|
7
7
|
require_relative 'span_context'
|
8
8
|
require_relative 'carrier'
|
9
9
|
require_relative 'trace_id'
|
10
|
-
require_relative '
|
10
|
+
require_relative 'http_client'
|
11
11
|
require_relative 'endpoint'
|
12
|
-
require_relative '
|
12
|
+
require_relative 'async_reporter'
|
13
13
|
require_relative 'scope_manager'
|
14
14
|
require_relative 'scope'
|
15
|
+
require_relative 'samplers'
|
16
|
+
require_relative 'encoders'
|
15
17
|
|
16
18
|
module Zipkin
|
17
19
|
class Tracer
|
18
20
|
DEFAULT_FLUSH_INTERVAL = 10
|
19
21
|
|
20
|
-
def self.build(url:,
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
)
|
28
|
-
sender
|
29
|
-
new(
|
22
|
+
def self.build(url:,
|
23
|
+
service_name:,
|
24
|
+
flush_interval: DEFAULT_FLUSH_INTERVAL,
|
25
|
+
logger: Logger.new(STDOUT),
|
26
|
+
sampler: Samplers::Const.new(true),
|
27
|
+
encoder: Encoders::JsonEncoder)
|
28
|
+
encoder = encoder.new(Endpoint.local_endpoint(service_name))
|
29
|
+
sender = HTTPClient.new(url: url, encoder: encoder, logger: logger)
|
30
|
+
reporter = AsyncReporter.create(sender: sender, flush_interval: flush_interval)
|
31
|
+
new(reporter, sender, logger: logger, sampler: sampler)
|
30
32
|
end
|
31
33
|
|
32
|
-
def initialize(
|
33
|
-
@
|
34
|
+
def initialize(reporter, sender, logger: Logger.new(STDOUT), sampler:)
|
35
|
+
@reporter = reporter
|
34
36
|
@sender = sender
|
35
37
|
@logger = logger
|
36
38
|
@scope_manager = ScopeManager.new
|
37
|
-
|
38
|
-
|
39
|
-
def stop
|
40
|
-
@sender.stop
|
39
|
+
@sampler = sampler
|
41
40
|
end
|
42
41
|
|
43
42
|
# @return [ScopeManager] the current ScopeManager, which may be a no-op but
|
@@ -75,7 +74,7 @@ module Zipkin
|
|
75
74
|
def start_span(operation_name,
|
76
75
|
child_of: nil,
|
77
76
|
start_time: Time.now,
|
78
|
-
tags:
|
77
|
+
tags: nil,
|
79
78
|
references: nil,
|
80
79
|
ignore_active_scope: false,
|
81
80
|
**)
|
@@ -87,10 +86,10 @@ module Zipkin
|
|
87
86
|
Span.new(
|
88
87
|
context,
|
89
88
|
operation_name,
|
90
|
-
@
|
89
|
+
@reporter,
|
91
90
|
start_time: start_time,
|
92
91
|
references: references,
|
93
|
-
tags: tags
|
92
|
+
tags: tags || {}
|
94
93
|
)
|
95
94
|
end
|
96
95
|
|
@@ -220,7 +219,7 @@ module Zipkin
|
|
220
219
|
if context
|
221
220
|
SpanContext.create_from_parent_context(context)
|
222
221
|
else
|
223
|
-
SpanContext.create_parent_context
|
222
|
+
SpanContext.create_parent_context(@sampler)
|
224
223
|
end
|
225
224
|
end
|
226
225
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'bundler'
|
4
5
|
Bundler.setup
|
@@ -7,8 +8,8 @@ require 'zipkin/tracer'
|
|
7
8
|
|
8
9
|
url = ENV['ZIPKIN_URL'] || 'http://localhost:9411'
|
9
10
|
|
10
|
-
tracer1 = Zipkin::Tracer.build(url: url, service_name: 'test-service')
|
11
|
-
tracer2 = Zipkin::Tracer.build(url: url, service_name: 'downstream-service')
|
11
|
+
tracer1 = Zipkin::Tracer.build(url: url, service_name: 'test-service', flush_interval: 1)
|
12
|
+
tracer2 = Zipkin::Tracer.build(url: url, service_name: 'downstream-service', flush_interval: 1)
|
12
13
|
|
13
14
|
rpc_span = tracer1.start_span(
|
14
15
|
'receive request',
|
@@ -45,7 +46,4 @@ async_span = tracer2.start_span(
|
|
45
46
|
sleep 0.3 # emulate network delay
|
46
47
|
async_span.finish
|
47
48
|
|
48
|
-
tracer1.stop
|
49
|
-
tracer2.stop
|
50
|
-
|
51
49
|
puts 'Finished'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.setup
|
6
|
+
|
7
|
+
require 'zipkin/tracer'
|
8
|
+
|
9
|
+
url = ENV['ZIPKIN_URL'] || 'http://localhost:9411'
|
10
|
+
|
11
|
+
tracer1 = Zipkin::Tracer.build(url: url, service_name: 'user-service', flush_interval: 1)
|
12
|
+
tracer2 = Zipkin::Tracer.build(url: url, service_name: 'name-service', flush_interval: 1)
|
13
|
+
|
14
|
+
root_span = tracer1.start_span(
|
15
|
+
'GET /users/:id',
|
16
|
+
tags: {
|
17
|
+
'span.kind' => 'server',
|
18
|
+
'http.method' => 'get',
|
19
|
+
'http.url' => 'http://example.com/users/5'
|
20
|
+
}
|
21
|
+
)
|
22
|
+
sleep 1
|
23
|
+
|
24
|
+
fetch_name_span = tracer1.start_span(
|
25
|
+
'get',
|
26
|
+
child_of: root_span,
|
27
|
+
tags: {
|
28
|
+
'span.kind' => 'client',
|
29
|
+
'http.method' => 'get',
|
30
|
+
'http.url' => 'GET /users/5/name'
|
31
|
+
}
|
32
|
+
)
|
33
|
+
sleep 0.3 # emulate network delay
|
34
|
+
|
35
|
+
name_service_span = tracer2.start_span(
|
36
|
+
'GET /users/:id/name',
|
37
|
+
child_of: fetch_name_span,
|
38
|
+
tags: {
|
39
|
+
'span.kind' => 'server',
|
40
|
+
'http.method' => 'get',
|
41
|
+
'http.url' => 'http://example.com/users/5/name'
|
42
|
+
}
|
43
|
+
)
|
44
|
+
sleep 0.5
|
45
|
+
name_service_span.finish
|
46
|
+
|
47
|
+
sleep 0.2 # emulate network delay
|
48
|
+
fetch_name_span.finish
|
49
|
+
|
50
|
+
sleep 0.1 # doing something with fetched info
|
51
|
+
root_span.finish
|
52
|
+
|
53
|
+
puts 'Finishing...'
|
54
|
+
sleep 3
|
55
|
+
|
56
|
+
puts 'Finished'
|
data/script/create_trace
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'bundler'
|
4
5
|
Bundler.setup
|
@@ -7,10 +8,10 @@ require 'zipkin/tracer'
|
|
7
8
|
|
8
9
|
url = ENV['ZIPKIN_URL'] || 'http://localhost:9411'
|
9
10
|
|
10
|
-
tracer1 = Zipkin::Tracer.build(url: url, service_name: 'test-service')
|
11
|
-
tracer2 = Zipkin::Tracer.build(url: url, service_name: 'downstream-service')
|
11
|
+
tracer1 = Zipkin::Tracer.build(url: url, service_name: 'test-service', flush_interval: 1)
|
12
|
+
tracer2 = Zipkin::Tracer.build(url: url, service_name: 'downstream-service-1', flush_interval: 1)
|
12
13
|
|
13
|
-
|
14
|
+
root_span = tracer1.start_span(
|
14
15
|
'receive request',
|
15
16
|
tags: {
|
16
17
|
'span.kind' => 'server'
|
@@ -18,39 +19,52 @@ outer_span = tracer1.start_span(
|
|
18
19
|
)
|
19
20
|
sleep 1
|
20
21
|
|
21
|
-
|
22
|
-
'fetch info from
|
23
|
-
child_of:
|
22
|
+
fetch_info_from_service1_span = tracer1.start_span(
|
23
|
+
'fetch info from service 1',
|
24
|
+
child_of: root_span,
|
24
25
|
tags: {
|
25
26
|
'span.kind' => 'client',
|
26
|
-
'peer.
|
27
|
-
'peer.ipv4' => '6.6.6.6',
|
27
|
+
'peer.ipv4' => '2.2.2.2',
|
28
28
|
'peer.port' => 443
|
29
29
|
}
|
30
30
|
)
|
31
31
|
sleep 0.3 # emulate network delay
|
32
32
|
|
33
|
-
|
34
|
-
'
|
35
|
-
child_of:
|
36
|
-
tags: {
|
33
|
+
service1_span = tracer2.start_span(
|
34
|
+
'service 1 operation',
|
35
|
+
child_of: fetch_info_from_service1_span,
|
36
|
+
tags: {
|
37
|
+
'span.kind' => 'server',
|
38
|
+
'peer.ipv4' => '1.1.1.1',
|
39
|
+
'peer.port' => 443
|
40
|
+
}
|
37
41
|
)
|
38
|
-
|
39
|
-
|
42
|
+
service1_span.log_kv(event: 'custom-event')
|
43
|
+
service1_span.log_kv(foo: 'bar', baz: 'buz')
|
40
44
|
sleep 0.5
|
41
|
-
|
45
|
+
service1_span.finish
|
42
46
|
|
43
47
|
sleep 0.2 # emulate network delay
|
44
48
|
|
45
|
-
|
49
|
+
fetch_info_from_service1_span.finish
|
50
|
+
|
51
|
+
fetch_info_from_service2_span = tracer1.start_span(
|
52
|
+
'fetch info from service 2 which does not have tracing',
|
53
|
+
child_of: root_span,
|
54
|
+
tags: {
|
55
|
+
'span.kind' => 'client',
|
56
|
+
'peer.service' => 'downstream-service-2',
|
57
|
+
'peer.ipv4' => '3.3.3.3',
|
58
|
+
'peer.port' => 443
|
59
|
+
}
|
60
|
+
)
|
61
|
+
sleep 0.3 # emulate network delay
|
62
|
+
fetch_info_from_service2_span.finish
|
46
63
|
|
47
64
|
sleep 0.1 # doing something with fetched info
|
48
|
-
|
65
|
+
root_span.finish
|
49
66
|
|
50
67
|
puts 'Finishing...'
|
51
68
|
sleep 3
|
52
69
|
|
53
|
-
tracer1.stop
|
54
|
-
tracer2.stop
|
55
|
-
|
56
70
|
puts 'Finished'
|
data/zipkin.gemspec
CHANGED
@@ -3,13 +3,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |spec|
|
5
5
|
spec.name = 'zipkin'
|
6
|
-
spec.version = '1.
|
6
|
+
spec.version = '1.6.2'
|
7
7
|
spec.authors = ['SaleMove TechMovers']
|
8
8
|
spec.email = ['techmovers@salemove.com']
|
9
9
|
|
10
10
|
spec.summary = 'OpenTracing Tracer implementation for Zipkin in Ruby'
|
11
11
|
spec.description = ''
|
12
|
-
spec.homepage = ''
|
12
|
+
spec.homepage = 'https://github.com/salemove/zipkin-ruby-opentracing'
|
13
13
|
spec.license = 'MIT'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency 'bundler', '~>
|
21
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
22
22
|
spec.add_development_dependency 'rake', '~> 10.0'
|
23
23
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
24
|
spec.add_development_dependency 'rubocop', '~> 0.54.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zipkin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SaleMove TechMovers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,12 +126,18 @@ files:
|
|
126
126
|
- bin/console
|
127
127
|
- bin/setup
|
128
128
|
- lib/zipkin.rb
|
129
|
+
- lib/zipkin/async_reporter.rb
|
130
|
+
- lib/zipkin/async_reporter/buffer.rb
|
129
131
|
- lib/zipkin/carrier.rb
|
130
|
-
- lib/zipkin/
|
131
|
-
- lib/zipkin/
|
132
|
-
- lib/zipkin/
|
132
|
+
- lib/zipkin/encoders.rb
|
133
|
+
- lib/zipkin/encoders/json_encoder.rb
|
134
|
+
- lib/zipkin/encoders/json_encoder/log_annotations.rb
|
135
|
+
- lib/zipkin/encoders/json_encoder/timestamp.rb
|
133
136
|
- lib/zipkin/endpoint.rb
|
134
|
-
- lib/zipkin/
|
137
|
+
- lib/zipkin/http_client.rb
|
138
|
+
- lib/zipkin/samplers.rb
|
139
|
+
- lib/zipkin/samplers/const.rb
|
140
|
+
- lib/zipkin/samplers/probabilistic.rb
|
135
141
|
- lib/zipkin/scope.rb
|
136
142
|
- lib/zipkin/scope_manager.rb
|
137
143
|
- lib/zipkin/scope_manager/scope_identifier.rb
|
@@ -141,9 +147,10 @@ files:
|
|
141
147
|
- lib/zipkin/trace_id.rb
|
142
148
|
- lib/zipkin/tracer.rb
|
143
149
|
- script/create_follows_from_trace
|
150
|
+
- script/create_http_trace
|
144
151
|
- script/create_trace
|
145
152
|
- zipkin.gemspec
|
146
|
-
homepage:
|
153
|
+
homepage: https://github.com/salemove/zipkin-ruby-opentracing
|
147
154
|
licenses:
|
148
155
|
- MIT
|
149
156
|
metadata: {}
|
@@ -162,8 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
169
|
- !ruby/object:Gem::Version
|
163
170
|
version: '0'
|
164
171
|
requirements: []
|
165
|
-
|
166
|
-
rubygems_version: 2.6.11
|
172
|
+
rubygems_version: 3.0.3
|
167
173
|
signing_key:
|
168
174
|
specification_version: 4
|
169
175
|
summary: OpenTracing Tracer implementation for Zipkin in Ruby
|
data/lib/zipkin/collector.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'thread'
|
4
|
-
|
5
|
-
require_relative './collector/timestamp'
|
6
|
-
require_relative './collector/log_annotations'
|
7
|
-
|
8
|
-
module Zipkin
|
9
|
-
class Collector
|
10
|
-
OT_KIND_TO_ZIPKIN_KIND = {
|
11
|
-
'server' => 'SERVER',
|
12
|
-
'client' => 'CLIENT',
|
13
|
-
'producer' => 'PRODUCER',
|
14
|
-
'consumer' => 'CONSUMER'
|
15
|
-
}.freeze
|
16
|
-
|
17
|
-
def initialize(local_endpoint)
|
18
|
-
@buffer = Buffer.new
|
19
|
-
@local_endpoint = local_endpoint
|
20
|
-
end
|
21
|
-
|
22
|
-
def retrieve
|
23
|
-
@buffer.retrieve
|
24
|
-
end
|
25
|
-
|
26
|
-
def send_span(span, end_time)
|
27
|
-
finish_ts = Timestamp.create(end_time)
|
28
|
-
start_ts = Timestamp.create(span.start_time)
|
29
|
-
duration = finish_ts - start_ts
|
30
|
-
return unless span.context.sampled?
|
31
|
-
|
32
|
-
@buffer << {
|
33
|
-
traceId: span.context.trace_id,
|
34
|
-
id: span.context.span_id,
|
35
|
-
parentId: span.context.parent_id,
|
36
|
-
name: span.operation_name,
|
37
|
-
kind: OT_KIND_TO_ZIPKIN_KIND[span.tags[:'span.kind'] || 'server'],
|
38
|
-
timestamp: start_ts,
|
39
|
-
duration: duration,
|
40
|
-
debug: false,
|
41
|
-
shared: false,
|
42
|
-
localEndpoint: @local_endpoint,
|
43
|
-
remoteEndpoint: Endpoint.remote_endpoint(span),
|
44
|
-
annotations: LogAnnotations.build(span),
|
45
|
-
tags: span.tags
|
46
|
-
}
|
47
|
-
end
|
48
|
-
|
49
|
-
class Buffer
|
50
|
-
def initialize
|
51
|
-
@buffer = []
|
52
|
-
@mutex = Mutex.new
|
53
|
-
end
|
54
|
-
|
55
|
-
def <<(element)
|
56
|
-
@mutex.synchronize do
|
57
|
-
@buffer << element
|
58
|
-
true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def retrieve
|
63
|
-
@mutex.synchronize do
|
64
|
-
elements = @buffer.dup
|
65
|
-
@buffer.clear
|
66
|
-
elements
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Zipkin
|
4
|
-
class Collector
|
5
|
-
module LogAnnotations
|
6
|
-
def self.build(span)
|
7
|
-
span.logs.map do |log|
|
8
|
-
{
|
9
|
-
timestamp: Timestamp.create(log.fetch(:timestamp)),
|
10
|
-
value: format_log_value(log)
|
11
|
-
}
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.format_log_value(log)
|
16
|
-
if log.keys == %i[event timestamp]
|
17
|
-
log.fetch(:event)
|
18
|
-
else
|
19
|
-
log
|
20
|
-
.reject { |key, _value| key == :timestamp }
|
21
|
-
.map { |key, value| "#{key}=#{value}" }
|
22
|
-
.join(' ')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|