jaeger-client-with-ruby-32-support 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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