jaeger-client 0.9.0 → 0.10.0

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -16
  3. data/crossdock/server +1 -1
  4. data/lib/jaeger/client.rb +25 -15
  5. data/lib/jaeger/client/version.rb +1 -1
  6. data/lib/jaeger/encoders/thrift_encoder.rb +92 -0
  7. data/lib/jaeger/extractors.rb +109 -0
  8. data/lib/jaeger/http_sender.rb +28 -0
  9. data/lib/jaeger/injectors.rb +69 -0
  10. data/lib/jaeger/rate_limiter.rb +61 -0
  11. data/lib/jaeger/{client/reporters.rb → reporters.rb} +0 -0
  12. data/lib/jaeger/reporters/composite_reporter.rb +17 -0
  13. data/lib/jaeger/reporters/in_memory_reporter.rb +30 -0
  14. data/lib/jaeger/reporters/logging_reporter.rb +22 -0
  15. data/lib/jaeger/reporters/null_reporter.rb +11 -0
  16. data/lib/jaeger/reporters/remote_reporter.rb +42 -0
  17. data/lib/jaeger/reporters/remote_reporter/buffer.rb +29 -0
  18. data/lib/jaeger/{client/samplers.rb → samplers.rb} +0 -0
  19. data/lib/jaeger/samplers/const.rb +24 -0
  20. data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +40 -0
  21. data/lib/jaeger/samplers/per_operation.rb +47 -0
  22. data/lib/jaeger/samplers/probabilistic.rb +26 -0
  23. data/lib/jaeger/samplers/rate_limiting.rb +33 -0
  24. data/lib/jaeger/scope.rb +38 -0
  25. data/lib/jaeger/scope_manager.rb +47 -0
  26. data/lib/jaeger/scope_manager/scope_identifier.rb +13 -0
  27. data/lib/jaeger/scope_manager/scope_stack.rb +33 -0
  28. data/lib/jaeger/span.rb +98 -0
  29. data/lib/jaeger/span/thrift_log_builder.rb +18 -0
  30. data/lib/jaeger/span/thrift_tag_builder.rb +43 -0
  31. data/lib/jaeger/span_context.rb +57 -0
  32. data/lib/jaeger/trace_id.rb +39 -0
  33. data/lib/jaeger/tracer.rb +195 -0
  34. data/lib/jaeger/udp_sender.rb +24 -0
  35. data/lib/jaeger/udp_sender/transport.rb +40 -0
  36. metadata +31 -32
  37. data/lib/jaeger/client/carrier.rb +0 -26
  38. data/lib/jaeger/client/encoders/thrift_encoder.rb +0 -94
  39. data/lib/jaeger/client/extractors.rb +0 -111
  40. data/lib/jaeger/client/http_sender.rb +0 -30
  41. data/lib/jaeger/client/injectors.rb +0 -71
  42. data/lib/jaeger/client/rate_limiter.rb +0 -63
  43. data/lib/jaeger/client/reporters/composite_reporter.rb +0 -19
  44. data/lib/jaeger/client/reporters/in_memory_reporter.rb +0 -32
  45. data/lib/jaeger/client/reporters/logging_reporter.rb +0 -24
  46. data/lib/jaeger/client/reporters/null_reporter.rb +0 -13
  47. data/lib/jaeger/client/reporters/remote_reporter.rb +0 -44
  48. data/lib/jaeger/client/reporters/remote_reporter/buffer.rb +0 -31
  49. data/lib/jaeger/client/samplers/const.rb +0 -26
  50. data/lib/jaeger/client/samplers/guaranteed_throughput_probabilistic.rb +0 -42
  51. data/lib/jaeger/client/samplers/per_operation.rb +0 -49
  52. data/lib/jaeger/client/samplers/probabilistic.rb +0 -28
  53. data/lib/jaeger/client/samplers/rate_limiting.rb +0 -35
  54. data/lib/jaeger/client/scope.rb +0 -40
  55. data/lib/jaeger/client/scope_manager.rb +0 -49
  56. data/lib/jaeger/client/scope_manager/scope_identifier.rb +0 -15
  57. data/lib/jaeger/client/scope_manager/scope_stack.rb +0 -35
  58. data/lib/jaeger/client/span.rb +0 -100
  59. data/lib/jaeger/client/span/thrift_log_builder.rb +0 -20
  60. data/lib/jaeger/client/span/thrift_tag_builder.rb +0 -45
  61. data/lib/jaeger/client/span_context.rb +0 -59
  62. data/lib/jaeger/client/trace_id.rb +0 -41
  63. data/lib/jaeger/client/tracer.rb +0 -197
  64. data/lib/jaeger/client/udp_sender.rb +0 -27
  65. data/lib/jaeger/client/udp_sender/transport.rb +0 -42
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ # RateLimiter is based on leaky bucket algorithm, formulated in terms of a
5
+ # credits balance that is replenished every time check_credit() method is
6
+ # called (tick) by the amount proportional to the time elapsed since the
7
+ # last tick, up to the max_balance. A call to check_credit() takes a cost
8
+ # of an item we want to pay with the balance. If the balance exceeds the
9
+ # cost of the item, the item is "purchased" and the balance reduced,
10
+ # indicated by returned value of true. Otherwise the balance is unchanged
11
+ # and return false.
12
+ #
13
+ # This can be used to limit a rate of messages emitted by a service by
14
+ # instantiating the Rate Limiter with the max number of messages a service
15
+ # is allowed to emit per second, and calling check_credit(1.0) for each
16
+ # message to determine if the message is within the rate limit.
17
+ #
18
+ # It can also be used to limit the rate of traffic in bytes, by setting
19
+ # credits_per_second to desired throughput as bytes/second, and calling
20
+ # check_credit() with the actual message size.
21
+ class RateLimiter
22
+ def initialize(credits_per_second:, max_balance:)
23
+ @credits_per_second = credits_per_second
24
+ @max_balance = max_balance
25
+ @balance = max_balance
26
+ @last_tick = Time.now
27
+ end
28
+
29
+ def check_credit(item_cost)
30
+ update_balance
31
+
32
+ return false if @balance < item_cost
33
+
34
+ @balance -= item_cost
35
+ true
36
+ end
37
+
38
+ def update(credits_per_second:, max_balance:)
39
+ update_balance
40
+
41
+ @credits_per_second = credits_per_second
42
+
43
+ # The new balance should be proportional to the old balance
44
+ @balance = max_balance * @balance / @max_balance
45
+ @max_balance = max_balance
46
+ end
47
+
48
+ private
49
+
50
+ def update_balance
51
+ current_time = Time.now
52
+ elapsed_time = current_time - @last_tick
53
+ @last_tick = current_time
54
+
55
+ @balance += elapsed_time * @credits_per_second
56
+ return if @balance <= @max_balance
57
+
58
+ @balance = @max_balance
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Reporters
5
+ class CompositeReporter
6
+ def initialize(reporters:)
7
+ @reporters = reporters
8
+ end
9
+
10
+ def report(span)
11
+ @reporters.each do |reporter|
12
+ reporter.report(span)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Reporters
5
+ class InMemoryReporter
6
+ def initialize
7
+ @spans = []
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def report(span)
12
+ @mutex.synchronize do
13
+ @spans << span
14
+ end
15
+ end
16
+
17
+ def spans
18
+ @mutex.synchronize do
19
+ @spans
20
+ end
21
+ end
22
+
23
+ def clear
24
+ @mutex.synchronize do
25
+ @spans.clear
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Reporters
5
+ class LoggingReporter
6
+ def initialize(logger: Logger.new($stdout))
7
+ @logger = logger
8
+ end
9
+
10
+ def report(span)
11
+ span_info = {
12
+ operation_name: span.operation_name,
13
+ start_time: span.start_time.iso8601,
14
+ end_time: span.end_time.iso8601,
15
+ trace_id: span.context.to_trace_id,
16
+ span_id: span.context.to_span_id
17
+ }
18
+ @logger.info "Span reported: #{span_info}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Reporters
5
+ class NullReporter
6
+ def report(_span)
7
+ # no-op
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './remote_reporter/buffer'
4
+
5
+ module Jaeger
6
+ module Reporters
7
+ class RemoteReporter
8
+ def initialize(sender:, flush_interval:)
9
+ @sender = sender
10
+ @flush_interval = flush_interval
11
+ @buffer = Buffer.new
12
+ end
13
+
14
+ def flush
15
+ spans = @buffer.retrieve
16
+ @sender.send_spans(spans) if spans.any?
17
+ spans
18
+ end
19
+
20
+ def report(span)
21
+ return if !span.context.sampled? && !span.context.debug?
22
+
23
+ init_reporter_thread
24
+ @buffer << span
25
+ end
26
+
27
+ private
28
+
29
+ def init_reporter_thread
30
+ return if @initializer_pid == Process.pid
31
+
32
+ @initializer_pid = Process.pid
33
+ Thread.new do
34
+ loop do
35
+ flush
36
+ sleep(@flush_interval)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Reporters
5
+ class RemoteReporter
6
+ class Buffer
7
+ def initialize
8
+ @buffer = []
9
+ @mutex = Mutex.new
10
+ end
11
+
12
+ def <<(element)
13
+ @mutex.synchronize do
14
+ @buffer << element
15
+ true
16
+ end
17
+ end
18
+
19
+ def retrieve
20
+ @mutex.synchronize do
21
+ elements = @buffer.dup
22
+ @buffer.clear
23
+ elements
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
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 `Jaeger::Samplers::Const.new(true)`
9
+ # to mark all new traces as sampled.
10
+ class Const
11
+ def initialize(decision)
12
+ @decision = decision
13
+ @tags = {
14
+ 'sampler.type' => 'const',
15
+ 'sampler.param' => @decision ? 1 : 0
16
+ }
17
+ end
18
+
19
+ def sample?(*)
20
+ [@decision, @tags]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Samplers
5
+ # A sampler that leverages both Probabilistic sampler and RateLimiting
6
+ # sampler. The RateLimiting is used as a guaranteed lower bound sampler
7
+ # such that every operation is sampled at least once in a time interval
8
+ # defined by the lower_bound. ie a lower_bound of 1.0 / (60 * 10) will
9
+ # sample an operation at least once every 10 minutes.
10
+ #
11
+ # The Probabilistic sampler is given higher priority when tags are
12
+ # emitted, ie. if is_sampled() for both samplers return true, the tags
13
+ # for Probabilistic sampler will be used.
14
+ class GuaranteedThroughputProbabilistic
15
+ attr_reader :tags
16
+
17
+ def initialize(lower_bound:, rate:, lower_bound_sampler: nil)
18
+ @probabilistic_sampler = Probabilistic.new(rate: rate)
19
+ @lower_bound_sampler = lower_bound_sampler || RateLimiting.new(max_traces_per_second: lower_bound)
20
+ @lower_bound_tags = {
21
+ 'sampler.type' => 'lowerbound',
22
+ 'sampler.param' => lower_bound
23
+ }
24
+ end
25
+
26
+ def sample?(*args)
27
+ is_sampled, probabilistic_tags = @probabilistic_sampler.sample?(*args)
28
+ if is_sampled
29
+ # We still call lower_bound_sampler to update the rate limiter budget
30
+ @lower_bound_sampler.sample?(*args)
31
+
32
+ return [is_sampled, probabilistic_tags]
33
+ end
34
+
35
+ is_sampled, _tags = @lower_bound_sampler.sample?(*args)
36
+ [is_sampled, @lower_bound_tags]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Samplers
5
+ # A sampler that leverages both Probabilistic sampler and RateLimiting
6
+ # sampler via the GuaranteedThroughputProbabilistic sampler. This sampler
7
+ # keeps track of all operations and delegates calls the the respective
8
+ # GuaranteedThroughputProbabilistic sampler.
9
+ class PerOperation
10
+ DEFAULT_SAMPLING_PROBABILITY = 0.001
11
+ DEFAULT_LOWER_BOUND = 1.0 / (10.0 * 60.0) # sample once every 10 minutes'
12
+
13
+ def initialize(strategies:, max_operations:)
14
+ @max_operations = max_operations
15
+ @default_sampling_probability =
16
+ strategies[:default_sampling_probability] || DEFAULT_SAMPLING_PROBABILITY
17
+ @lower_bound = strategies[:default_lower_bound_traces_per_second] || DEFAULT_LOWER_BOUND
18
+
19
+ @default_sampler = Probabilistic.new(rate: @default_sampling_probability)
20
+ @samplers = (strategies[:per_operation_strategies] || []).reduce({}) do |acc, strategy|
21
+ operation = strategy.fetch(:operation)
22
+ rate = strategy.fetch(:probabilistic_sampling)
23
+ sampler = GuaranteedThroughputProbabilistic.new(
24
+ lower_bound: @lower_bound,
25
+ rate: rate
26
+ )
27
+ acc.merge(operation => sampler)
28
+ end
29
+ end
30
+
31
+ def sample?(opts)
32
+ operation_name = opts.fetch(:operation_name)
33
+ sampler = @samplers[operation_name]
34
+ return sampler.sample?(opts) if sampler
35
+
36
+ return @default_sampler.sample?(opts) if @samplers.length >= @max_operations
37
+
38
+ sampler = GuaranteedThroughputProbabilistic.new(
39
+ lower_bound: @lower_bound,
40
+ rate: @default_sampling_probability
41
+ )
42
+ @samplers[operation_name] = sampler
43
+ sampler.sample?(opts)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
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
+
14
+ @boundary = TraceId::TRACE_ID_UPPER_BOUND * rate
15
+ @tags = {
16
+ 'sampler.type' => 'probabilistic',
17
+ 'sampler.param' => rate
18
+ }
19
+ end
20
+
21
+ def sample?(trace_id:, **)
22
+ [@boundary >= trace_id, @tags]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module Samplers
5
+ # Samples at most max_traces_per_second. The distribution of sampled
6
+ # traces follows burstiness of the service, i.e. a service with uniformly
7
+ # distributed requests will have those requests sampled uniformly as
8
+ # well, but if requests are bursty, especially sub-second, then a number
9
+ # of sequential requests can be sampled each second.
10
+ class RateLimiting
11
+ attr_reader :tags
12
+
13
+ def initialize(max_traces_per_second: 10)
14
+ if max_traces_per_second < 0.0
15
+ raise "max_traces_per_second must not be negative, got #{max_traces_per_second}"
16
+ end
17
+
18
+ @rate_limiter = RateLimiter.new(
19
+ credits_per_second: max_traces_per_second,
20
+ max_balance: [max_traces_per_second, 1.0].max
21
+ )
22
+ @tags = {
23
+ 'sampler.type' => 'ratelimiting',
24
+ 'sampler.param' => max_traces_per_second
25
+ }
26
+ end
27
+
28
+ def sample?(*)
29
+ [@rate_limiter.check_credit(1.0), @tags]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ # Scope represents an OpenTracing Scope
5
+ #
6
+ # See http://www.opentracing.io for more information.
7
+ class Scope
8
+ def initialize(span, scope_stack, finish_on_close:)
9
+ @span = span
10
+ @scope_stack = scope_stack
11
+ @finish_on_close = finish_on_close
12
+ @closed = false
13
+ end
14
+
15
+ # Return the Span scoped by this Scope
16
+ #
17
+ # @return [Span]
18
+ attr_reader :span
19
+
20
+ # Close scope
21
+ #
22
+ # Mark the end of the active period for the current thread and Scope,
23
+ # updating the ScopeManager#active in the process.
24
+ def close
25
+ raise "Tried to close already closed span: #{inspect}" if @closed
26
+ @closed = true
27
+
28
+ @span.finish if @finish_on_close
29
+ removed_scope = @scope_stack.pop
30
+
31
+ if removed_scope != self # rubocop:disable Style/GuardClause
32
+ raise 'Removed non-active scope, ' \
33
+ "removed: #{removed_scope.inspect}, "\
34
+ "expected: #{inspect}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scope_manager/scope_stack'
4
+ require_relative 'scope_manager/scope_identifier'
5
+
6
+ module Jaeger
7
+ # ScopeManager represents an OpenTracing ScopeManager
8
+ #
9
+ # See http://www.opentracing.io for more information.
10
+ #
11
+ # The ScopeManager interface abstracts both the activation of Span instances
12
+ # via ScopeManager#activate and access to an active Span/Scope via
13
+ # ScopeManager#active
14
+ #
15
+ class ScopeManager
16
+ def initialize
17
+ @scope_stack = ScopeStack.new
18
+ end
19
+
20
+ # Make a span instance active
21
+ #
22
+ # @param span [Span] the Span that should become active
23
+ # @param finish_on_close [Boolean] whether the Span should automatically be
24
+ # finished when Scope#close is called
25
+ # @return [Scope] instance to control the end of the active period for the
26
+ # Span. It is a programming error to neglect to call Scope#close on the
27
+ # returned instance.
28
+ def activate(span, finish_on_close: true)
29
+ return active if active && active.span == span
30
+ scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close)
31
+ @scope_stack.push(scope)
32
+ scope
33
+ end
34
+
35
+ # Return active scope
36
+ #
37
+ # If there is a non-null Scope, its wrapped Span becomes an implicit parent
38
+ # (as Reference#CHILD_OF) of any newly-created Span at
39
+ # Tracer#start_active_span or Tracer#start_span time.
40
+ #
41
+ # @return [Scope] the currently active Scope which can be used to access the
42
+ # currently active Span.
43
+ def active
44
+ @scope_stack.peek
45
+ end
46
+ end
47
+ end