jaeger-client 0.9.0 → 0.10.0

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