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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '08c97e0494e2aada96486d717d1cd8b645da89c2'
4
- data.tar.gz: dc7681e5f0a7c4efe293f22ad496f9c352675137
2
+ SHA256:
3
+ metadata.gz: d406755c3fae0a6ad07050d0bfa19a903ce5e5ca414ad0530d172061ef0e2a69
4
+ data.tar.gz: 8f377efd4748398e92dc6cf565f40819e25cf26ca0a0b2f9c119e21ed7ce36c6
5
5
  SHA512:
6
- metadata.gz: 3f80229eec38c0f34c83276264b90073195b80e74f5389569ff5458957e9f90e85a02856684a169a116239cb5c804a9eef69161b32851ac38222545090a26306
7
- data.tar.gz: b2ee56339a6e2eb7aa615becf7fd163fd86d66e165fb065a3f50e9cc89fd217a3be3628f7484491f90c866b8460b4f4eea0c487575de29693da928895307d059
6
+ metadata.gz: c94f44c0d12367b67bddef7c928fa27528a13eedddf4a1b99c4c3eec34553ee8a6c949dc43797e00ce13ba8a47f59b615f4779fd7375ea1d28e2dda1a1fc6058
7
+ data.tar.gz: 904580f268829d5699001cc260ed53c26def532e84e50bf6eb9b9e44c89daa5861482d77a24722b729d775c1b5dbb514812d4ff68ac0b242fbfbf873409baffc
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.0
5
- before_install: gem install bundler -v 1.14.4
4
+ - 2.6.0
5
+ before_install: gem install bundler
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,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'encoders/json_encoder'
@@ -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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zipkin
4
+ module Encoders
5
+ class JsonEncoder
6
+ module Timestamp
7
+ def self.create(time)
8
+ (time.to_f * 1_000_000).to_i
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -17,10 +17,10 @@ module Zipkin
17
17
  end
18
18
 
19
19
  module PeerInfo
20
- SERVICE = :'peer.service'
21
- IPV4 = :'peer.ipv4'
22
- IPV6 = :'peer.ipv6'
23
- PORT = :'peer.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[:'span.kind'] || SpanKind::SERVER
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
- serviceName: tags[PeerInfo::SERVICE],
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 JsonClient
9
- def initialize(url:, collector:, flush_interval:, logger: Logger.new(STDOUT))
10
- @collector = collector
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 start
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' => 'application/json'
19
+ 'Content-Type' => @encoder.content_type
40
20
  )
41
- request.body = JSON.dump(spans)
21
+ request.body = @encoder.encode(spans)
42
22
  response = http.request(request)
43
23
 
44
24
  if response.code != '202'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'samplers/const'
4
+ require_relative 'samplers/probabilistic'
@@ -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
@@ -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 collector [Collector] the span collector
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
- collector,
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
- @collector = collector
26
+ @reporter = reporter
27
27
  @start_time = start_time
28
- @tags = 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.to_sym => sanitized_value)
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
- @collector.send_span(self, end_time)
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
@@ -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
- new(trace_id: trace_id, span_id: trace_id, sampled: true)
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)
@@ -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
- rand(TRACE_ID_UPPER_BOUND).to_s(16)
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
@@ -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 'json_client'
10
+ require_relative 'http_client'
11
11
  require_relative 'endpoint'
12
- require_relative 'collector'
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:, service_name:, flush_interval: DEFAULT_FLUSH_INTERVAL, logger: Logger.new(STDOUT))
21
- collector = Collector.new(Endpoint.local_endpoint(service_name))
22
- sender = JsonClient.new(
23
- url: url,
24
- collector: collector,
25
- flush_interval: flush_interval,
26
- logger: logger
27
- )
28
- sender.start
29
- new(collector, sender, logger: logger)
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(collector, sender, logger: Logger.new(STDOUT))
33
- @collector = collector
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
- end
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
- @collector,
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'
@@ -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
- outer_span = tracer1.start_span(
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
- inner_span = tracer1.start_span(
22
- 'fetch info from downstream',
23
- child_of: outer_span,
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.service' => 'downstream-service',
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
- downstream_span = tracer2.start_span(
34
- 'downstream operation',
35
- child_of: inner_span,
36
- tags: { 'span.kind' => 'server' }
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
- downstream_span.log_kv(event: 'custom-event')
39
- downstream_span.log_kv(foo: 'bar', baz: 'buz')
42
+ service1_span.log_kv(event: 'custom-event')
43
+ service1_span.log_kv(foo: 'bar', baz: 'buz')
40
44
  sleep 0.5
41
- downstream_span.finish
45
+ service1_span.finish
42
46
 
43
47
  sleep 0.2 # emulate network delay
44
48
 
45
- inner_span.finish
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
- outer_span.finish
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'
@@ -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.5.0'
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', '~> 1.14'
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.5.0
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: 2018-07-01 00:00:00.000000000 Z
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: '1.14'
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: '1.14'
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/collector.rb
131
- - lib/zipkin/collector/log_annotations.rb
132
- - lib/zipkin/collector/timestamp.rb
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/json_client.rb
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
- rubyforge_project:
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
@@ -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
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Zipkin
4
- class Collector
5
- module Timestamp
6
- def self.create(time)
7
- (time.to_f * 1_000_000).to_i
8
- end
9
- end
10
- end
11
- end