jaeger-client 0.7.1 → 1.1.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +11 -2
  5. data/Makefile +1 -0
  6. data/README.md +127 -13
  7. data/crossdock/Dockerfile +29 -0
  8. data/crossdock/Gemfile +6 -0
  9. data/crossdock/Gemfile.lock +35 -0
  10. data/crossdock/docker-compose.yml +68 -0
  11. data/crossdock/jaeger-docker-compose.yml +48 -0
  12. data/crossdock/rules.mk +35 -0
  13. data/crossdock/server +173 -0
  14. data/jaeger-client.gemspec +4 -2
  15. data/lib/jaeger/client.rb +42 -18
  16. data/lib/jaeger/client/version.rb +1 -1
  17. data/lib/jaeger/encoders/thrift_encoder.rb +142 -0
  18. data/lib/jaeger/extractors.rb +173 -0
  19. data/lib/jaeger/http_sender.rb +28 -0
  20. data/lib/jaeger/injectors.rb +83 -0
  21. data/lib/jaeger/rate_limiter.rb +61 -0
  22. data/lib/jaeger/recurring_executor.rb +35 -0
  23. data/lib/jaeger/reporters.rb +7 -0
  24. data/lib/jaeger/reporters/composite_reporter.rb +17 -0
  25. data/lib/jaeger/reporters/in_memory_reporter.rb +30 -0
  26. data/lib/jaeger/reporters/logging_reporter.rb +22 -0
  27. data/lib/jaeger/reporters/null_reporter.rb +11 -0
  28. data/lib/jaeger/{client/async_reporter.rb → reporters/remote_reporter.rb} +21 -20
  29. data/lib/jaeger/{client/async_reporter → reporters/remote_reporter}/buffer.rb +2 -2
  30. data/lib/jaeger/samplers.rb +8 -0
  31. data/lib/jaeger/samplers/const.rb +24 -0
  32. data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +47 -0
  33. data/lib/jaeger/samplers/per_operation.rb +79 -0
  34. data/lib/jaeger/samplers/probabilistic.rb +38 -0
  35. data/lib/jaeger/samplers/rate_limiting.rb +51 -0
  36. data/lib/jaeger/samplers/remote_controlled.rb +119 -0
  37. data/lib/jaeger/samplers/remote_controlled/instructions_fetcher.rb +34 -0
  38. data/lib/jaeger/scope.rb +38 -0
  39. data/lib/jaeger/scope_manager.rb +47 -0
  40. data/lib/jaeger/scope_manager/scope_identifier.rb +13 -0
  41. data/lib/jaeger/scope_manager/scope_stack.rb +33 -0
  42. data/lib/jaeger/span.rb +97 -0
  43. data/lib/jaeger/span/thrift_log_builder.rb +18 -0
  44. data/lib/jaeger/span_context.rb +57 -0
  45. data/lib/jaeger/thrift_tag_builder.rb +41 -0
  46. data/lib/jaeger/trace_id.rb +48 -0
  47. data/lib/jaeger/tracer.rb +213 -0
  48. data/lib/jaeger/udp_sender.rb +26 -0
  49. data/lib/jaeger/udp_sender/transport.rb +40 -0
  50. metadata +78 -31
  51. data/lib/jaeger/client/carrier.rb +0 -26
  52. data/lib/jaeger/client/encoders/thrift_encoder.rb +0 -94
  53. data/lib/jaeger/client/extractors.rb +0 -88
  54. data/lib/jaeger/client/http_sender.rb +0 -30
  55. data/lib/jaeger/client/injectors.rb +0 -54
  56. data/lib/jaeger/client/samplers.rb +0 -4
  57. data/lib/jaeger/client/samplers/const.rb +0 -29
  58. data/lib/jaeger/client/samplers/probabilistic.rb +0 -30
  59. data/lib/jaeger/client/scope.rb +0 -40
  60. data/lib/jaeger/client/scope_manager.rb +0 -49
  61. data/lib/jaeger/client/scope_manager/scope_identifier.rb +0 -15
  62. data/lib/jaeger/client/scope_manager/scope_stack.rb +0 -35
  63. data/lib/jaeger/client/span.rb +0 -84
  64. data/lib/jaeger/client/span/thrift_log_builder.rb +0 -20
  65. data/lib/jaeger/client/span/thrift_tag_builder.rb +0 -45
  66. data/lib/jaeger/client/span_context.rb +0 -59
  67. data/lib/jaeger/client/trace_id.rb +0 -41
  68. data/lib/jaeger/client/tracer.rb +0 -189
  69. data/lib/jaeger/client/udp_sender.rb +0 -27
  70. data/lib/jaeger/client/udp_sender/transport.rb +0 -42
@@ -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 > 0
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(*args)
75
+ warn 'Span#log is deprecated. Please use Span#log_kv instead.'
76
+ log_kv(*args)
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,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,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_reader :span_id, :parent_id, :trace_id, :baggage, :flags
23
+ attr_writer :flags
24
+
25
+ def initialize(span_id:, parent_id: 0, trace_id:, flags:, 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,41 @@
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
+ if value.is_a?(Integer)
15
+ Jaeger::Thrift::Tag.new(
16
+ KEY => key.to_s,
17
+ VTYPE => Jaeger::Thrift::TagType::LONG,
18
+ VLONG => value
19
+ )
20
+ elsif value.is_a?(Float)
21
+ Jaeger::Thrift::Tag.new(
22
+ KEY => key.to_s,
23
+ VTYPE => Jaeger::Thrift::TagType::DOUBLE,
24
+ VDOUBLE => value
25
+ )
26
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
27
+ Jaeger::Thrift::Tag.new(
28
+ KEY => key.to_s,
29
+ VTYPE => Jaeger::Thrift::TagType::BOOL,
30
+ VBOOL => value
31
+ )
32
+ else
33
+ Jaeger::Thrift::Tag.new(
34
+ KEY => key.to_s,
35
+ VTYPE => Jaeger::Thrift::TagType::STRING,
36
+ VSTR => value.to_s
37
+ )
38
+ end
39
+ end
40
+ end
41
+ 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 < 0 ? 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 < 0 ? 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
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class Tracer
5
+ def initialize(reporter:, sampler:, injectors:, extractors:)
6
+ @reporter = reporter
7
+ @sampler = sampler
8
+ @injectors = injectors
9
+ @extractors = extractors
10
+ @scope_manager = ScopeManager.new
11
+ end
12
+
13
+ # @return [ScopeManager] the current ScopeManager, which may be a no-op
14
+ # but may not be nil.
15
+ attr_reader :scope_manager
16
+
17
+ # @return [Span, nil] the active span. This is a shorthand for
18
+ # `scope_manager.active.span`, and nil will be returned if
19
+ # Scope#active is nil.
20
+ def active_span
21
+ scope = scope_manager.active
22
+ scope.span if scope
23
+ end
24
+
25
+ # Starts a new span.
26
+ #
27
+ # This is similar to #start_active_span, but the returned Span will not
28
+ # be registered via the ScopeManager.
29
+ #
30
+ # @param operation_name [String] The operation name for the Span
31
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
32
+ # the newly-started Span. If a Span instance is provided, its
33
+ # context is automatically substituted. See [Reference] for more
34
+ # information.
35
+ #
36
+ # If specified, the `references` parameter must be omitted.
37
+ # @param references [Array<Reference>] An array of reference
38
+ # objects that identify one or more parent SpanContexts.
39
+ # @param start_time [Time] When the Span started, if not now
40
+ # @param tags [Hash] Tags to assign to the Span at start time
41
+ # @param ignore_active_scope [Boolean] whether to create an implicit
42
+ # References#CHILD_OF reference to the ScopeManager#active.
43
+ #
44
+ # @yield [Span] If passed an optional block, start_span will yield the
45
+ # newly-created span to the block. The span will be finished automatically
46
+ # after the block is executed.
47
+ # @return [Span, Object] If passed an optional block, start_span will return
48
+ # the block's return value, otherwise it returns the newly-started Span
49
+ # instance, which has not been automatically registered via the
50
+ # ScopeManager
51
+ def start_span(operation_name,
52
+ child_of: nil,
53
+ references: nil,
54
+ start_time: Time.now,
55
+ tags: {},
56
+ ignore_active_scope: false,
57
+ **)
58
+ context, sampler_tags = prepare_span_context(
59
+ operation_name: operation_name,
60
+ child_of: child_of,
61
+ references: references,
62
+ ignore_active_scope: ignore_active_scope
63
+ )
64
+ span = Span.new(
65
+ context,
66
+ operation_name,
67
+ @reporter,
68
+ start_time: start_time,
69
+ references: references,
70
+ tags: tags.merge(sampler_tags)
71
+ )
72
+
73
+ if block_given?
74
+ begin
75
+ yield(span)
76
+ ensure
77
+ span.finish
78
+ end
79
+ else
80
+ span
81
+ end
82
+ end
83
+
84
+ # Creates a newly started and activated Scope
85
+ #
86
+ # If the Tracer's ScopeManager#active is not nil, no explicit references
87
+ # are provided, and `ignore_active_scope` is false, then an inferred
88
+ # References#CHILD_OF reference is created to the ScopeManager#active's
89
+ # SpanContext when start_active is invoked.
90
+ #
91
+ # @param operation_name [String] The operation name for the Span
92
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
93
+ # the newly-started Span. If a Span instance is provided, its
94
+ # context is automatically substituted. See [Reference] for more
95
+ # information.
96
+ #
97
+ # If specified, the `references` parameter must be omitted.
98
+ # @param references [Array<Reference>] An array of reference
99
+ # objects that identify one or more parent SpanContexts.
100
+ # @param start_time [Time] When the Span started, if not now
101
+ # @param tags [Hash] Tags to assign to the Span at start time
102
+ # @param ignore_active_scope [Boolean] whether to create an implicit
103
+ # References#CHILD_OF reference to the ScopeManager#active.
104
+ # @param finish_on_close [Boolean] whether span should automatically be
105
+ # finished when Scope#close is called
106
+ # @yield [Scope] If an optional block is passed to start_active_span it
107
+ # will yield the newly-started Scope. If `finish_on_close` is true then the
108
+ # Span will be finished automatically after the block is executed.
109
+ # @return [Scope, Object] If passed an optional block, start_active_span
110
+ # returns the block's return value, otherwise it returns the newly-started
111
+ # and activated Scope
112
+ def start_active_span(operation_name,
113
+ child_of: nil,
114
+ references: nil,
115
+ start_time: Time.now,
116
+ tags: {},
117
+ ignore_active_scope: false,
118
+ finish_on_close: true,
119
+ **)
120
+ span = start_span(
121
+ operation_name,
122
+ child_of: child_of,
123
+ references: references,
124
+ start_time: start_time,
125
+ tags: tags,
126
+ ignore_active_scope: ignore_active_scope
127
+ )
128
+ scope = @scope_manager.activate(span, finish_on_close: finish_on_close)
129
+
130
+ if block_given?
131
+ begin
132
+ yield scope
133
+ ensure
134
+ scope.close
135
+ end
136
+ else
137
+ scope
138
+ end
139
+ end
140
+
141
+ # Inject a SpanContext into the given carrier
142
+ #
143
+ # @param span_context [SpanContext]
144
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
145
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
146
+ def inject(span_context, format, carrier)
147
+ @injectors.fetch(format).each do |injector|
148
+ injector.inject(span_context, carrier)
149
+ end
150
+ end
151
+
152
+ # Extract a SpanContext in the given format from the given carrier.
153
+ #
154
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
155
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
156
+ # @return [SpanContext] the extracted SpanContext or nil if none could be found
157
+ def extract(format, carrier)
158
+ @extractors
159
+ .fetch(format)
160
+ .lazy
161
+ .map { |extractor| extractor.extract(carrier) }
162
+ .reject(&:nil?)
163
+ .first
164
+ end
165
+
166
+ private
167
+
168
+ def prepare_span_context(operation_name:, child_of:, references:, ignore_active_scope:)
169
+ context =
170
+ context_from_child_of(child_of) ||
171
+ context_from_references(references) ||
172
+ context_from_active_scope(ignore_active_scope)
173
+
174
+ if context
175
+ [SpanContext.create_from_parent_context(context), {}]
176
+ else
177
+ trace_id = TraceId.generate
178
+ is_sampled, tags = @sampler.sample(
179
+ trace_id: trace_id,
180
+ operation_name: operation_name
181
+ )
182
+ span_context = SpanContext.new(
183
+ trace_id: trace_id,
184
+ span_id: trace_id,
185
+ flags: is_sampled ? SpanContext::Flags::SAMPLED : SpanContext::Flags::NONE
186
+ )
187
+ [span_context, tags]
188
+ end
189
+ end
190
+
191
+ def context_from_child_of(child_of)
192
+ return nil unless child_of
193
+ child_of.respond_to?(:context) ? child_of.context : child_of
194
+ end
195
+
196
+ def context_from_references(references)
197
+ return nil if !references || references.none?
198
+
199
+ # Prefer CHILD_OF reference if present
200
+ ref = references.detect do |reference|
201
+ reference.type == OpenTracing::Reference::CHILD_OF
202
+ end
203
+ (ref || references[0]).context
204
+ end
205
+
206
+ def context_from_active_scope(ignore_active_scope)
207
+ return if ignore_active_scope
208
+
209
+ active_scope = @scope_manager.active
210
+ active_scope.span.context if active_scope
211
+ end
212
+ end
213
+ end