jaeger-client 0.7.1 → 1.1.0

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