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,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,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'span/thrift_tag_builder'
4
+ require_relative 'span/thrift_log_builder'
5
+
6
+ module Jaeger
7
+ class Span
8
+ attr_accessor :operation_name
9
+
10
+ attr_reader :context, :start_time, :end_time, :references, :tags, :logs
11
+
12
+ # Creates a new {Span}
13
+ #
14
+ # @param context [SpanContext] the context of the span
15
+ # @param operation_name [String] the operation name
16
+ # @param reporter [#report] span reporter
17
+ #
18
+ # @return [Span] a new Span
19
+ def initialize(context, operation_name, reporter, start_time: Time.now, references: [], tags: {})
20
+ @context = context
21
+ @operation_name = operation_name
22
+ @reporter = reporter
23
+ @start_time = start_time
24
+ @references = references
25
+ @tags = []
26
+ @logs = []
27
+
28
+ tags.each { |key, value| set_tag(key, value) }
29
+ end
30
+
31
+ # Set a tag value on this span
32
+ #
33
+ # @param key [String] the key of the tag
34
+ # @param value [String, Numeric, Boolean] the value of the tag. If it's not
35
+ # a String, Numeric, or Boolean it will be encoded with to_s
36
+ def set_tag(key, value)
37
+ if key == 'sampling.priority'
38
+ if value.to_i > 0
39
+ return self if @context.debug?
40
+
41
+ @context.flags = @context.flags | SpanContext::Flags::SAMPLED | SpanContext::Flags::DEBUG
42
+ else
43
+ @context.flags = @context.flags & ~SpanContext::Flags::SAMPLED
44
+ end
45
+ return self
46
+ end
47
+
48
+ # Using Thrift::Tag to avoid unnecessary memory allocations
49
+ @tags << ThriftTagBuilder.build(key, value)
50
+
51
+ self
52
+ end
53
+
54
+ # Set a baggage item on the span
55
+ #
56
+ # @param key [String] the key of the baggage item
57
+ # @param value [String] the value of the baggage item
58
+ def set_baggage_item(key, value)
59
+ @context.set_baggage_item(key, value)
60
+ self
61
+ end
62
+
63
+ # Get a baggage item
64
+ #
65
+ # @param key [String] the key of the baggage item
66
+ #
67
+ # @return Value of the baggage item
68
+ def get_baggage_item(key)
69
+ @context.get_baggage_item(key)
70
+ end
71
+
72
+ # Add a log entry to this span
73
+ #
74
+ # @deprecated Use {#log_kv} instead.
75
+ def log(*args)
76
+ warn 'Span#log is deprecated. Please use Span#log_kv instead.'
77
+ log_kv(*args)
78
+ end
79
+
80
+ # Add a log entry to this span
81
+ #
82
+ # @param timestamp [Time] time of the log
83
+ # @param fields [Hash] Additional information to log
84
+ def log_kv(timestamp: Time.now, **fields)
85
+ # Using Thrift::Log to avoid unnecessary memory allocations
86
+ @logs << ThriftLogBuilder.build(timestamp, fields)
87
+ nil
88
+ end
89
+
90
+ # Finish the {Span}
91
+ #
92
+ # @param end_time [Time] custom end time, if not now
93
+ def finish(end_time: Time.now)
94
+ @end_time = end_time
95
+ @reporter.report(self)
96
+ end
97
+ end
98
+ 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,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaeger
4
+ class Span
5
+ class ThriftTagBuilder
6
+ FIELDS = Jaeger::Thrift::Tag::FIELDS
7
+ KEY = FIELDS[Jaeger::Thrift::Tag::KEY].fetch(:name)
8
+ VTYPE = FIELDS[Jaeger::Thrift::Tag::VTYPE].fetch(:name)
9
+ VLONG = FIELDS[Jaeger::Thrift::Tag::VLONG].fetch(:name)
10
+ VDOUBLE = FIELDS[Jaeger::Thrift::Tag::VDOUBLE].fetch(:name)
11
+ VBOOL = FIELDS[Jaeger::Thrift::Tag::VBOOL].fetch(:name)
12
+ VSTR = FIELDS[Jaeger::Thrift::Tag::VSTR].fetch(:name)
13
+
14
+ def self.build(key, value)
15
+ if value.is_a?(Integer)
16
+ Jaeger::Thrift::Tag.new(
17
+ KEY => key.to_s,
18
+ VTYPE => Jaeger::Thrift::TagType::LONG,
19
+ VLONG => value
20
+ )
21
+ elsif value.is_a?(Float)
22
+ Jaeger::Thrift::Tag.new(
23
+ KEY => key.to_s,
24
+ VTYPE => Jaeger::Thrift::TagType::DOUBLE,
25
+ VDOUBLE => value
26
+ )
27
+ elsif value.is_a?(TrueClass) || value.is_a?(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
43
+ 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,39 @@
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
+ TRACE_ID_UPPER_BOUND = MAX_64BIT_UNSIGNED_INT + 1
8
+
9
+ def self.generate
10
+ rand(TRACE_ID_UPPER_BOUND)
11
+ end
12
+
13
+ def self.base16_hex_id_to_uint64(id)
14
+ return nil unless id
15
+ value = id.to_i(16)
16
+ value > MAX_64BIT_UNSIGNED_INT || value < 0 ? 0 : value
17
+ end
18
+
19
+ # Thrift defines ID fields as i64, which is signed, therefore we convert
20
+ # large IDs (> 2^63) to negative longs
21
+ def self.uint64_id_to_int64(id)
22
+ id > MAX_64BIT_SIGNED_INT ? id - MAX_64BIT_UNSIGNED_INT - 1 : id
23
+ end
24
+
25
+ # Convert an integer id into a 0 padded hex string.
26
+ # If the string is shorter than 16 characters, it will be padded to 16.
27
+ # If it is longer than 16 characters, it is padded to 32.
28
+ def self.to_hex(id)
29
+ hex_str = id.to_s(16)
30
+
31
+ # pad the string with '0's to 16 or 32 characters
32
+ if hex_str.length > 16
33
+ hex_str.rjust(32, '0')
34
+ else
35
+ hex_str.rjust(16, '0')
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,195 @@
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
+ # @return [Span] The newly-started Span
45
+ def start_span(operation_name,
46
+ child_of: nil,
47
+ references: nil,
48
+ start_time: Time.now,
49
+ tags: {},
50
+ ignore_active_scope: false,
51
+ **)
52
+ context, sampler_tags = prepare_span_context(
53
+ operation_name: operation_name,
54
+ child_of: child_of,
55
+ references: references,
56
+ ignore_active_scope: ignore_active_scope
57
+ )
58
+ Span.new(
59
+ context,
60
+ operation_name,
61
+ @reporter,
62
+ start_time: start_time,
63
+ references: references,
64
+ tags: tags.merge(sampler_tags)
65
+ )
66
+ end
67
+
68
+ # Creates a newly started and activated Scope
69
+ #
70
+ # If the Tracer's ScopeManager#active is not nil, no explicit references
71
+ # are provided, and `ignore_active_scope` is false, then an inferred
72
+ # References#CHILD_OF reference is created to the ScopeManager#active's
73
+ # SpanContext when start_active is invoked.
74
+ #
75
+ # @param operation_name [String] The operation name for the Span
76
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
77
+ # the newly-started Span. If a Span instance is provided, its
78
+ # context is automatically substituted. See [Reference] for more
79
+ # information.
80
+ #
81
+ # If specified, the `references` parameter must be omitted.
82
+ # @param references [Array<Reference>] An array of reference
83
+ # objects that identify one or more parent SpanContexts.
84
+ # @param start_time [Time] When the Span started, if not now
85
+ # @param tags [Hash] Tags to assign to the Span at start time
86
+ # @param ignore_active_scope [Boolean] whether to create an implicit
87
+ # References#CHILD_OF reference to the ScopeManager#active.
88
+ # @param finish_on_close [Boolean] whether span should automatically be
89
+ # finished when Scope#close is called
90
+ # @yield [Scope] If an optional block is passed to start_active it will
91
+ # yield the newly-started Scope. If `finish_on_close` is true then the
92
+ # Span will be finished automatically after the block is executed.
93
+ # @return [Scope] The newly-started and activated Scope
94
+ def start_active_span(operation_name,
95
+ child_of: nil,
96
+ references: nil,
97
+ start_time: Time.now,
98
+ tags: {},
99
+ ignore_active_scope: false,
100
+ finish_on_close: true,
101
+ **)
102
+ span = start_span(
103
+ operation_name,
104
+ child_of: child_of,
105
+ references: references,
106
+ start_time: start_time,
107
+ tags: tags,
108
+ ignore_active_scope: ignore_active_scope
109
+ )
110
+ scope = @scope_manager.activate(span, finish_on_close: finish_on_close)
111
+
112
+ if block_given?
113
+ begin
114
+ yield scope
115
+ ensure
116
+ scope.close
117
+ end
118
+ end
119
+
120
+ scope
121
+ end
122
+
123
+ # Inject a SpanContext into the given carrier
124
+ #
125
+ # @param span_context [SpanContext]
126
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
127
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
128
+ def inject(span_context, format, carrier)
129
+ @injectors.fetch(format).each do |injector|
130
+ injector.inject(span_context, carrier)
131
+ end
132
+ end
133
+
134
+ # Extract a SpanContext in the given format from the given carrier.
135
+ #
136
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
137
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
138
+ # @return [SpanContext] the extracted SpanContext or nil if none could be found
139
+ def extract(format, carrier)
140
+ @extractors
141
+ .fetch(format)
142
+ .lazy
143
+ .map { |extractor| extractor.extract(carrier) }
144
+ .reject(&:nil?)
145
+ .first
146
+ end
147
+
148
+ private
149
+
150
+ def prepare_span_context(operation_name:, child_of:, references:, ignore_active_scope:)
151
+ context =
152
+ context_from_child_of(child_of) ||
153
+ context_from_references(references) ||
154
+ context_from_active_scope(ignore_active_scope)
155
+
156
+ if context
157
+ [SpanContext.create_from_parent_context(context), {}]
158
+ else
159
+ trace_id = TraceId.generate
160
+ is_sampled, tags = @sampler.sample?(
161
+ trace_id: trace_id,
162
+ operation_name: operation_name
163
+ )
164
+ span_context = SpanContext.new(
165
+ trace_id: trace_id,
166
+ span_id: trace_id,
167
+ flags: is_sampled ? SpanContext::Flags::SAMPLED : SpanContext::Flags::NONE
168
+ )
169
+ [span_context, tags]
170
+ end
171
+ end
172
+
173
+ def context_from_child_of(child_of)
174
+ return nil unless child_of
175
+ child_of.respond_to?(:context) ? child_of.context : child_of
176
+ end
177
+
178
+ def context_from_references(references)
179
+ return nil if !references || references.none?
180
+
181
+ # Prefer CHILD_OF reference if present
182
+ ref = references.detect do |reference|
183
+ reference.type == OpenTracing::Reference::CHILD_OF
184
+ end
185
+ (ref || references[0]).context
186
+ end
187
+
188
+ def context_from_active_scope(ignore_active_scope)
189
+ return if ignore_active_scope
190
+
191
+ active_scope = @scope_manager.active
192
+ active_scope.span.context if active_scope
193
+ end
194
+ end
195
+ end