zipkin 1.5.0 → 1.6.2
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 +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
|