dox-jaeger-client 2.0.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +33 -0
  3. data/.gitignore +12 -0
  4. data/.gitmodules +3 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +61 -0
  7. data/.rubocop_todo.yml +13 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/Makefile +1 -0
  11. data/README.md +210 -0
  12. data/Rakefile +9 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/crossdock/Dockerfile +31 -0
  16. data/crossdock/Gemfile +6 -0
  17. data/crossdock/Gemfile.lock +37 -0
  18. data/crossdock/docker-compose.yml +68 -0
  19. data/crossdock/jaeger-docker-compose.yml +53 -0
  20. data/crossdock/rules.mk +35 -0
  21. data/crossdock/server +175 -0
  22. data/jaeger-client.gemspec +37 -0
  23. data/lib/jaeger/client/version.rb +7 -0
  24. data/lib/jaeger/client.rb +77 -0
  25. data/lib/jaeger/encoders/thrift_encoder.rb +173 -0
  26. data/lib/jaeger/extractors.rb +173 -0
  27. data/lib/jaeger/http_sender.rb +28 -0
  28. data/lib/jaeger/injectors.rb +83 -0
  29. data/lib/jaeger/rate_limiter.rb +61 -0
  30. data/lib/jaeger/recurring_executor.rb +35 -0
  31. data/lib/jaeger/reporters/composite_reporter.rb +17 -0
  32. data/lib/jaeger/reporters/in_memory_reporter.rb +30 -0
  33. data/lib/jaeger/reporters/logging_reporter.rb +22 -0
  34. data/lib/jaeger/reporters/null_reporter.rb +11 -0
  35. data/lib/jaeger/reporters/remote_reporter/buffer.rb +29 -0
  36. data/lib/jaeger/reporters/remote_reporter.rb +42 -0
  37. data/lib/jaeger/reporters.rb +7 -0
  38. data/lib/jaeger/samplers/const.rb +24 -0
  39. data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +47 -0
  40. data/lib/jaeger/samplers/per_operation.rb +77 -0
  41. data/lib/jaeger/samplers/probabilistic.rb +40 -0
  42. data/lib/jaeger/samplers/rate_limiting.rb +51 -0
  43. data/lib/jaeger/samplers/remote_controlled/instructions_fetcher.rb +34 -0
  44. data/lib/jaeger/samplers/remote_controlled.rb +119 -0
  45. data/lib/jaeger/samplers.rb +8 -0
  46. data/lib/jaeger/scope.rb +39 -0
  47. data/lib/jaeger/scope_manager/scope_identifier.rb +13 -0
  48. data/lib/jaeger/scope_manager/scope_stack.rb +33 -0
  49. data/lib/jaeger/scope_manager.rb +48 -0
  50. data/lib/jaeger/span/thrift_log_builder.rb +18 -0
  51. data/lib/jaeger/span.rb +97 -0
  52. data/lib/jaeger/span_context.rb +57 -0
  53. data/lib/jaeger/thrift_tag_builder.rb +42 -0
  54. data/lib/jaeger/trace_id.rb +48 -0
  55. data/lib/jaeger/tracer.rb +214 -0
  56. data/lib/jaeger/udp_sender/transport.rb +41 -0
  57. data/lib/jaeger/udp_sender.rb +26 -0
  58. data/script/create_follows_from_trace +51 -0
  59. data/script/create_trace +52 -0
  60. data/thrift/agent.thrift +32 -0
  61. data/thrift/gen-rb/jaeger/thrift/agent/agent.rb +118 -0
  62. data/thrift/gen-rb/jaeger/thrift/agent/agent_constants.rb +15 -0
  63. data/thrift/gen-rb/jaeger/thrift/agent/agent_types.rb +17 -0
  64. data/thrift/gen-rb/jaeger/thrift/agent.rb +116 -0
  65. data/thrift/gen-rb/jaeger/thrift/agent_constants.rb +13 -0
  66. data/thrift/gen-rb/jaeger/thrift/agent_types.rb +15 -0
  67. data/thrift/gen-rb/jaeger/thrift/collector.rb +82 -0
  68. data/thrift/gen-rb/jaeger/thrift/jaeger_constants.rb +13 -0
  69. data/thrift/gen-rb/jaeger/thrift/jaeger_types.rb +211 -0
  70. data/thrift/gen-rb/jaeger/thrift/zipkin/zipkin_collector.rb +84 -0
  71. data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_constants.rb +41 -0
  72. data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_types.rb +220 -0
  73. data/thrift/jaeger.thrift +88 -0
  74. data/thrift/zipkincore.thrift +300 -0
  75. metadata +260 -0
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'remote_controlled/instructions_fetcher'
4
+
5
+ module Jaeger
6
+ module Samplers
7
+ class RemoteControlled
8
+ DEFAULT_REFRESH_INTERVAL = 60
9
+ DEFAULT_SAMPLING_HOST = 'localhost'
10
+ DEFAULT_SAMPLING_PORT = 5778
11
+
12
+ attr_reader :sampler
13
+
14
+ def initialize(opts = {})
15
+ @sampler = opts.fetch(:sampler, Probabilistic.new)
16
+ @logger = opts.fetch(:logger, Logger.new($stdout))
17
+
18
+ @poll_executor = opts[:poll_executor] || begin
19
+ refresh_interval = opts.fetch(:refresh_interval, DEFAULT_REFRESH_INTERVAL)
20
+ RecurringExecutor.new(interval: refresh_interval)
21
+ end
22
+
23
+ @instructions_fetcher = opts[:instructions_fetcher] || begin
24
+ service_name = opts.fetch(:service_name)
25
+ host = opts.fetch(:host, DEFAULT_SAMPLING_HOST)
26
+ port = opts.fetch(:port, DEFAULT_SAMPLING_PORT)
27
+ InstructionsFetcher.new(host: host, port: port, service_name: service_name)
28
+ end
29
+ end
30
+
31
+ def sample(*args)
32
+ @poll_executor.start(&method(:poll)) unless @poll_executor.running?
33
+
34
+ @sampler.sample(*args)
35
+ end
36
+
37
+ def poll
38
+ @logger.debug 'Fetching sampling strategy'
39
+
40
+ instructions = @instructions_fetcher.fetch
41
+ handle_instructions(instructions)
42
+ rescue InstructionsFetcher::FetchFailed => e
43
+ @logger.warn "Fetching sampling strategy failed: #{e.message}"
44
+ end
45
+
46
+ private
47
+
48
+ def handle_instructions(instructions)
49
+ if instructions['operationSampling']
50
+ update_per_operation_sampler(instructions['operationSampling'])
51
+ else
52
+ update_rate_limiting_or_probabilistic_sampler(instructions['strategyType'], instructions)
53
+ end
54
+ end
55
+
56
+ def update_per_operation_sampler(instructions)
57
+ strategies = normalize(instructions)
58
+
59
+ if @sampler.is_a?(PerOperation)
60
+ @sampler.update(strategies: strategies)
61
+ else
62
+ @sampler = PerOperation.new(strategies: strategies, max_operations: 2000)
63
+ end
64
+ end
65
+
66
+ def normalize(instructions)
67
+ {
68
+ default_sampling_probability: instructions['defaultSamplingProbability'],
69
+ default_lower_bound_traces_per_second: instructions['defaultLowerBoundTracesPerSecond'],
70
+ per_operation_strategies: instructions['perOperationStrategies'].map do |strategy|
71
+ {
72
+ operation: strategy['operation'],
73
+ probabilistic_sampling: {
74
+ sampling_rate: strategy['probabilisticSampling']['samplingRate']
75
+ }
76
+ }
77
+ end
78
+ }
79
+ end
80
+
81
+ def update_rate_limiting_or_probabilistic_sampler(strategy, instructions)
82
+ case strategy
83
+ when 'PROBABILISTIC'
84
+ update_probabilistic_strategy(instructions['probabilisticSampling'])
85
+ when 'RATE_LIMITING'
86
+ update_rate_limiting_strategy(instructions['rateLimitingSampling'])
87
+ else
88
+ @logger.warn "Unknown sampling strategy #{strategy}"
89
+ end
90
+ end
91
+
92
+ def update_probabilistic_strategy(instructions)
93
+ rate = instructions['samplingRate']
94
+ return unless rate
95
+
96
+ if @sampler.is_a?(Probabilistic)
97
+ @sampler.update(rate: rate)
98
+ @logger.info "Updated Probabilistic sampler (rate=#{rate})"
99
+ else
100
+ @sampler = Probabilistic.new(rate: rate)
101
+ @logger.info "Updated sampler to Probabilistic (rate=#{rate})"
102
+ end
103
+ end
104
+
105
+ def update_rate_limiting_strategy(instructions)
106
+ max_traces_per_second = instructions['maxTracesPerSecond']
107
+ return unless max_traces_per_second
108
+
109
+ if @sampler.is_a?(RateLimiting)
110
+ @sampler.update(max_traces_per_second: max_traces_per_second)
111
+ @logger.info "Updated Ratelimiting sampler (max_traces_per_second=#{max_traces_per_second})"
112
+ else
113
+ @sampler = RateLimiting.new(max_traces_per_second: max_traces_per_second)
114
+ @logger.info "Updated sampler to Ratelimiting (max_traces_per_second=#{max_traces_per_second})"
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'samplers/const'
4
+ require_relative 'samplers/guaranteed_throughput_probabilistic'
5
+ require_relative 'samplers/per_operation'
6
+ require_relative 'samplers/probabilistic'
7
+ require_relative 'samplers/rate_limiting'
8
+ require_relative 'samplers/remote_controlled'
@@ -0,0 +1,39 @@
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
+
27
+ @closed = true
28
+
29
+ @span.finish if @finish_on_close
30
+ removed_scope = @scope_stack.pop
31
+
32
+ if removed_scope != self # rubocop:disable Style/GuardClause
33
+ raise 'Removed non-active scope, ' \
34
+ "removed: #{removed_scope.inspect}, "\
35
+ "expected: #{inspect}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class ScopeManager
5
+ # @api private
6
+ class ScopeIdentifier
7
+ def self.generate
8
+ # 65..90.chr are characters between A and Z
9
+ "opentracing_#{(0...8).map { rand(65..90).chr }.join}".to_sym
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class ScopeManager
5
+ # @api private
6
+ class ScopeStack
7
+ def initialize
8
+ # Generate a random identifier to use as the Thread.current key. This is
9
+ # needed so that it would be possible to create multiple tracers in one
10
+ # thread (mostly useful for testing purposes)
11
+ @scope_identifier = ScopeIdentifier.generate
12
+ end
13
+
14
+ def push(scope)
15
+ store << scope
16
+ end
17
+
18
+ def pop
19
+ store.pop
20
+ end
21
+
22
+ def peek
23
+ store.last
24
+ end
25
+
26
+ private
27
+
28
+ def store
29
+ Thread.current[@scope_identifier] ||= []
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
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
+
31
+ scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close)
32
+ @scope_stack.push(scope)
33
+ scope
34
+ end
35
+
36
+ # Return active scope
37
+ #
38
+ # If there is a non-null Scope, its wrapped Span becomes an implicit parent
39
+ # (as Reference#CHILD_OF) of any newly-created Span at
40
+ # Tracer#start_active_span or Tracer#start_span time.
41
+ #
42
+ # @return [Scope] the currently active Scope which can be used to access the
43
+ # currently active Span.
44
+ def active
45
+ @scope_stack.peek
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class Span
5
+ class ThriftLogBuilder
6
+ FIELDS = Jaeger::Thrift::Log::FIELDS
7
+ TIMESTAMP = FIELDS[Jaeger::Thrift::Log::TIMESTAMP].fetch(:name)
8
+ LOG_FIELDS = FIELDS[Jaeger::Thrift::Log::LOG_FIELDS].fetch(:name)
9
+
10
+ def self.build(timestamp, fields)
11
+ Jaeger::Thrift::Log.new(
12
+ TIMESTAMP => (timestamp.to_f * 1_000_000).to_i,
13
+ LOG_FIELDS => fields.map { |key, value| ThriftTagBuilder.build(key, value) }
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'span/thrift_log_builder'
4
+
5
+ module Jaeger
6
+ class Span
7
+ attr_accessor :operation_name
8
+
9
+ attr_reader :context, :start_time, :end_time, :references, :tags, :logs
10
+
11
+ # Creates a new {Span}
12
+ #
13
+ # @param context [SpanContext] the context of the span
14
+ # @param operation_name [String] the operation name
15
+ # @param reporter [#report] span reporter
16
+ #
17
+ # @return [Span] a new Span
18
+ def initialize(context, operation_name, reporter, start_time: Time.now, references: [], tags: {})
19
+ @context = context
20
+ @operation_name = operation_name
21
+ @reporter = reporter
22
+ @start_time = start_time
23
+ @references = references
24
+ @tags = []
25
+ @logs = []
26
+
27
+ tags.each { |key, value| set_tag(key, value) }
28
+ end
29
+
30
+ # Set a tag value on this span
31
+ #
32
+ # @param key [String] the key of the tag
33
+ # @param value [String, Numeric, Boolean] the value of the tag. If it's not
34
+ # a String, Numeric, or Boolean it will be encoded with to_s
35
+ def set_tag(key, value)
36
+ if key == 'sampling.priority'
37
+ if value.to_i.positive?
38
+ return self if @context.debug?
39
+
40
+ @context.flags = @context.flags | SpanContext::Flags::SAMPLED | SpanContext::Flags::DEBUG
41
+ else
42
+ @context.flags = @context.flags & ~SpanContext::Flags::SAMPLED
43
+ end
44
+ return self
45
+ end
46
+
47
+ # Using Thrift::Tag to avoid unnecessary memory allocations
48
+ @tags << ThriftTagBuilder.build(key, value)
49
+
50
+ self
51
+ end
52
+
53
+ # Set a baggage item on the span
54
+ #
55
+ # @param key [String] the key of the baggage item
56
+ # @param value [String] the value of the baggage item
57
+ def set_baggage_item(key, value)
58
+ @context.set_baggage_item(key, value)
59
+ self
60
+ end
61
+
62
+ # Get a baggage item
63
+ #
64
+ # @param key [String] the key of the baggage item
65
+ #
66
+ # @return Value of the baggage item
67
+ def get_baggage_item(key)
68
+ @context.get_baggage_item(key)
69
+ end
70
+
71
+ # Add a log entry to this span
72
+ #
73
+ # @deprecated Use {#log_kv} instead.
74
+ def log(...)
75
+ warn 'Span#log is deprecated. Please use Span#log_kv instead.'
76
+ log_kv(...)
77
+ end
78
+
79
+ # Add a log entry to this span
80
+ #
81
+ # @param timestamp [Time] time of the log
82
+ # @param fields [Hash] Additional information to log
83
+ def log_kv(timestamp: Time.now, **fields)
84
+ # Using Thrift::Log to avoid unnecessary memory allocations
85
+ @logs << ThriftLogBuilder.build(timestamp, fields)
86
+ nil
87
+ end
88
+
89
+ # Finish the {Span}
90
+ #
91
+ # @param end_time [Time] custom end time, if not now
92
+ def finish(end_time: Time.now)
93
+ @end_time = end_time
94
+ @reporter.report(self)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ # SpanContext holds the data for a span that gets inherited to child spans
5
+ class SpanContext
6
+ module Flags
7
+ NONE = 0x00
8
+ SAMPLED = 0x01
9
+ DEBUG = 0x02
10
+ end
11
+
12
+ def self.create_from_parent_context(span_context)
13
+ new(
14
+ trace_id: span_context.trace_id,
15
+ parent_id: span_context.span_id,
16
+ span_id: TraceId.generate,
17
+ flags: span_context.flags,
18
+ baggage: span_context.baggage.dup
19
+ )
20
+ end
21
+
22
+ attr_accessor :flags
23
+ attr_reader :span_id, :parent_id, :trace_id, :baggage
24
+
25
+ def initialize(span_id:, trace_id:, flags:, parent_id: 0, baggage: {})
26
+ @span_id = span_id
27
+ @parent_id = parent_id
28
+ @trace_id = trace_id
29
+ @baggage = baggage
30
+ @flags = flags
31
+ end
32
+
33
+ def sampled?
34
+ @flags & Flags::SAMPLED == Flags::SAMPLED
35
+ end
36
+
37
+ def debug?
38
+ @flags & Flags::DEBUG == Flags::DEBUG
39
+ end
40
+
41
+ def to_trace_id
42
+ @to_trace_id ||= @trace_id.to_s(16)
43
+ end
44
+
45
+ def to_span_id
46
+ @to_span_id ||= @span_id.to_s(16)
47
+ end
48
+
49
+ def set_baggage_item(key, value)
50
+ @baggage[key.to_s] = value.to_s
51
+ end
52
+
53
+ def get_baggage_item(key)
54
+ @baggage[key.to_s]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class ThriftTagBuilder
5
+ FIELDS = Jaeger::Thrift::Tag::FIELDS
6
+ KEY = FIELDS[Jaeger::Thrift::Tag::KEY].fetch(:name)
7
+ VTYPE = FIELDS[Jaeger::Thrift::Tag::VTYPE].fetch(:name)
8
+ VLONG = FIELDS[Jaeger::Thrift::Tag::VLONG].fetch(:name)
9
+ VDOUBLE = FIELDS[Jaeger::Thrift::Tag::VDOUBLE].fetch(:name)
10
+ VBOOL = FIELDS[Jaeger::Thrift::Tag::VBOOL].fetch(:name)
11
+ VSTR = FIELDS[Jaeger::Thrift::Tag::VSTR].fetch(:name)
12
+
13
+ def self.build(key, value)
14
+ case value
15
+ when Integer
16
+ Jaeger::Thrift::Tag.new(
17
+ KEY => key.to_s,
18
+ VTYPE => Jaeger::Thrift::TagType::LONG,
19
+ VLONG => value
20
+ )
21
+ when Float
22
+ Jaeger::Thrift::Tag.new(
23
+ KEY => key.to_s,
24
+ VTYPE => Jaeger::Thrift::TagType::DOUBLE,
25
+ VDOUBLE => value
26
+ )
27
+ when TrueClass, FalseClass
28
+ Jaeger::Thrift::Tag.new(
29
+ KEY => key.to_s,
30
+ VTYPE => Jaeger::Thrift::TagType::BOOL,
31
+ VBOOL => value
32
+ )
33
+ else
34
+ Jaeger::Thrift::Tag.new(
35
+ KEY => key.to_s,
36
+ VTYPE => Jaeger::Thrift::TagType::STRING,
37
+ VSTR => value.to_s
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ module TraceId
5
+ MAX_64BIT_SIGNED_INT = (1 << 63) - 1
6
+ MAX_64BIT_UNSIGNED_INT = (1 << 64) - 1
7
+ MAX_128BIT_UNSIGNED_INT = (1 << 128) - 1
8
+ TRACE_ID_UPPER_BOUND = MAX_64BIT_UNSIGNED_INT + 1
9
+
10
+ def self.generate
11
+ rand(TRACE_ID_UPPER_BOUND)
12
+ end
13
+
14
+ def self.base16_hex_id_to_uint64(id)
15
+ return nil unless id
16
+
17
+ value = id.to_i(16)
18
+ value > MAX_64BIT_UNSIGNED_INT || value.negative? ? 0 : value
19
+ end
20
+
21
+ def self.base16_hex_id_to_uint128(id)
22
+ return nil unless id
23
+
24
+ value = id.to_i(16)
25
+ value > MAX_128BIT_UNSIGNED_INT || value.negative? ? 0 : value
26
+ end
27
+
28
+ # Thrift defines ID fields as i64, which is signed, therefore we convert
29
+ # large IDs (> 2^63) to negative longs
30
+ def self.uint64_id_to_int64(id)
31
+ id > MAX_64BIT_SIGNED_INT ? id - MAX_64BIT_UNSIGNED_INT - 1 : id
32
+ end
33
+
34
+ # Convert an integer id into a 0 padded hex string.
35
+ # If the string is shorter than 16 characters, it will be padded to 16.
36
+ # If it is longer than 16 characters, it is padded to 32.
37
+ def self.to_hex(id)
38
+ hex_str = id.to_s(16)
39
+
40
+ # pad the string with '0's to 16 or 32 characters
41
+ if hex_str.length > 16
42
+ hex_str.rjust(32, '0')
43
+ else
44
+ hex_str.rjust(16, '0')
45
+ end
46
+ end
47
+ end
48
+ end