opentelemetry-sdk 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +1 -0
  4. data/LICENSE +201 -0
  5. data/README.md +73 -0
  6. data/lib/opentelemetry-sdk.rb +7 -0
  7. data/lib/opentelemetry/sdk.rb +69 -0
  8. data/lib/opentelemetry/sdk/configurator.rb +171 -0
  9. data/lib/opentelemetry/sdk/correlation_context.rb +16 -0
  10. data/lib/opentelemetry/sdk/correlation_context/builder.rb +40 -0
  11. data/lib/opentelemetry/sdk/correlation_context/manager.rb +87 -0
  12. data/lib/opentelemetry/sdk/instrumentation_library.rb +13 -0
  13. data/lib/opentelemetry/sdk/internal.rb +52 -0
  14. data/lib/opentelemetry/sdk/resources.rb +16 -0
  15. data/lib/opentelemetry/sdk/resources/constants.rb +124 -0
  16. data/lib/opentelemetry/sdk/resources/resource.rb +84 -0
  17. data/lib/opentelemetry/sdk/trace.rb +24 -0
  18. data/lib/opentelemetry/sdk/trace/config.rb +18 -0
  19. data/lib/opentelemetry/sdk/trace/config/trace_config.rb +77 -0
  20. data/lib/opentelemetry/sdk/trace/export.rb +30 -0
  21. data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +144 -0
  22. data/lib/opentelemetry/sdk/trace/export/console_span_exporter.rb +40 -0
  23. data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +86 -0
  24. data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +58 -0
  25. data/lib/opentelemetry/sdk/trace/export/noop_span_exporter.rb +42 -0
  26. data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +72 -0
  27. data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +62 -0
  28. data/lib/opentelemetry/sdk/trace/noop_span_processor.rb +50 -0
  29. data/lib/opentelemetry/sdk/trace/samplers.rb +90 -0
  30. data/lib/opentelemetry/sdk/trace/samplers/constant_sampler.rb +33 -0
  31. data/lib/opentelemetry/sdk/trace/samplers/decision.rb +26 -0
  32. data/lib/opentelemetry/sdk/trace/samplers/parent_or_else.rb +43 -0
  33. data/lib/opentelemetry/sdk/trace/samplers/probability_sampler.rb +64 -0
  34. data/lib/opentelemetry/sdk/trace/samplers/result.rb +55 -0
  35. data/lib/opentelemetry/sdk/trace/span.rb +336 -0
  36. data/lib/opentelemetry/sdk/trace/span_data.rb +34 -0
  37. data/lib/opentelemetry/sdk/trace/tracer.rb +78 -0
  38. data/lib/opentelemetry/sdk/trace/tracer_provider.rb +84 -0
  39. data/lib/opentelemetry/sdk/version.rb +12 -0
  40. metadata +207 -0
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2019 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Trace
10
+ module Samplers
11
+ # The Result class represents an arbitrary sampling result. It has
12
+ # boolean values for the sampling decision and whether to record
13
+ # events, and a collection of attributes to be attached to a sampled
14
+ # root span.
15
+ class Result
16
+ EMPTY_HASH = {}.freeze
17
+ DECISIONS = [Decision::RECORD, Decision::NOT_RECORD, Decision::RECORD_AND_SAMPLED].freeze
18
+ private_constant(:EMPTY_HASH, :DECISIONS)
19
+
20
+ # Returns a frozen hash of attributes to be attached span.
21
+ #
22
+ # @return [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
23
+ attr_reader :attributes
24
+
25
+ # Returns a new sampling result with the specified decision and
26
+ # attributes.
27
+ #
28
+ # @param [Symbol] decision Whether or not a span should be sampled
29
+ # and/or record events.
30
+ # @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
31
+ # attributes A frozen or freezable hash containing attributes to be
32
+ # attached to the span.
33
+ def initialize(decision:, attributes: nil)
34
+ @decision = decision
35
+ @attributes = attributes.freeze || EMPTY_HASH
36
+ end
37
+
38
+ # Returns true if this span should be sampled.
39
+ #
40
+ # @return [Boolean] sampling decision
41
+ def sampled?
42
+ @decision == Decision::RECORD_AND_SAMPLED
43
+ end
44
+
45
+ # Returns true if this span should record events, attributes, status, etc.
46
+ #
47
+ # @return [Boolean] recording decision
48
+ def recording?
49
+ @decision != Decision::NOT_RECORD
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,336 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2019 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Trace
10
+ # Implementation of {OpenTelemetry::Trace::Span} that records trace events.
11
+ #
12
+ # This implementation includes reader methods intended to allow access to
13
+ # internal state by SpanProcessors (see {NoopSpanProcessor} for the interface).
14
+ # Instrumentation should use the API provided by {OpenTelemetry::Trace::Span}
15
+ # and should consider {Span} to be write-only.
16
+ #
17
+ # rubocop:disable Metrics/ClassLength
18
+ class Span < OpenTelemetry::Trace::Span
19
+ # The following readers are intended for the use of SpanProcessors and
20
+ # should not be considered part of the public interface for instrumentation.
21
+ attr_reader :name, :status, :kind, :parent_span_id, :start_timestamp, :end_timestamp, :links, :library_resource, :instrumentation_library
22
+
23
+ # Return a frozen copy of the current attributes. This is intended for
24
+ # use of SpanProcesses and should not be considered part of the public
25
+ # interface for instrumentation.
26
+ #
27
+ # @return [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] may be nil.
28
+ def attributes
29
+ # Don't bother synchronizing. Access by SpanProcessors is expected to
30
+ # be serialized.
31
+ @attributes&.clone.freeze
32
+ end
33
+
34
+ # Return a frozen copy of the current events. This is intended for use
35
+ # of SpanProcessors and should not be considered part of the public
36
+ # interface for instrumentation.
37
+ #
38
+ # @return [Array<Event>] may be nil.
39
+ def events
40
+ # Don't bother synchronizing. Access by SpanProcessors is expected to
41
+ # be serialized.
42
+ @events&.clone.freeze
43
+ end
44
+
45
+ # Return the flag whether this span is recording events
46
+ #
47
+ # @return [Boolean] true if this Span is active and recording information
48
+ # like events with the #add_event operation and attributes using
49
+ # #set_attribute.
50
+ def recording?
51
+ true
52
+ end
53
+
54
+ # Set attribute
55
+ #
56
+ # Note that the OpenTelemetry project
57
+ # {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
58
+ # documents} certain "standard attributes" that have prescribed semantic
59
+ # meanings.
60
+ #
61
+ # @param [String] key
62
+ # @param [String, Boolean, Numeric] value
63
+ #
64
+ # @return [self] returns itself
65
+ def set_attribute(key, value)
66
+ super
67
+ @mutex.synchronize do
68
+ if @ended
69
+ OpenTelemetry.logger.warn('Calling set_attribute on an ended Span.')
70
+ else
71
+ @attributes ||= {}
72
+ @attributes[key] = value
73
+ trim_span_attributes(@attributes)
74
+ @total_recorded_attributes += 1
75
+ end
76
+ end
77
+ self
78
+ end
79
+
80
+ # Add an Event to a {Span}. This can be accomplished eagerly or lazily.
81
+ # Lazy evaluation is useful when the event attributes are expensive to
82
+ # build and where the cost can be avoided for an unsampled {Span}.
83
+ #
84
+ # Eager example:
85
+ #
86
+ # span.add_event(name: 'event', attributes: {'eager' => true})
87
+ #
88
+ # Lazy example:
89
+ #
90
+ # span.add_event { OpenTelemetry::Trace::Event.new(name: 'event', attributes: {'eager' => false}) }
91
+ #
92
+ # Note that the OpenTelemetry project
93
+ # {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
94
+ # documents} certain "standard event names and keys" which have
95
+ # prescribed semantic meanings.
96
+ #
97
+ # @param [optional String] name Optional name of the event. This is
98
+ # required if a block is not given.
99
+ # @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
100
+ # One or more key:value pairs, where the keys must be strings and the
101
+ # values may be string, boolean or numeric type. This argument should
102
+ # only be used when passing in a name.
103
+ # @param [optional Time] timestamp Optional timestamp for the event.
104
+ # This argument should only be used when passing in a name.
105
+ #
106
+ # @return [self] returns itself
107
+ def add_event(name: nil, attributes: nil, timestamp: nil)
108
+ super
109
+ event = block_given? ? yield : OpenTelemetry::Trace::Event.new(name: name, attributes: attributes, timestamp: timestamp || Time.now)
110
+
111
+ @mutex.synchronize do
112
+ if @ended
113
+ OpenTelemetry.logger.warn('Calling add_event on an ended Span.')
114
+ else
115
+ @events ||= []
116
+ @events = append_event(@events, event)
117
+ @total_recorded_events += 1
118
+ end
119
+ end
120
+ self
121
+ end
122
+
123
+ # Record an error during the execution of this span. Multiple errors
124
+ # can be recorded on a span.
125
+ #
126
+ # @param [Exception] error The error to be recorded
127
+ #
128
+ # @return [void]
129
+ def record_error(error)
130
+ add_event(name: 'error',
131
+ attributes: {
132
+ 'error.type' => error.class.to_s,
133
+ 'error.message' => error.message,
134
+ 'error.stack' => error.backtrace.join("\n")
135
+ })
136
+ end
137
+
138
+ # Sets the Status to the Span
139
+ #
140
+ # If used, this will override the default Span status. Default is OK.
141
+ #
142
+ # Only the value of the last call will be recorded, and implementations
143
+ # are free to ignore previous calls.
144
+ #
145
+ # @param [Status] status The new status, which overrides the default Span
146
+ # status, which is OK.
147
+ #
148
+ # @return [void]
149
+ def status=(status)
150
+ super
151
+ @mutex.synchronize do
152
+ if @ended
153
+ OpenTelemetry.logger.warn('Calling status= on an ended Span.')
154
+ else
155
+ @status = status
156
+ end
157
+ end
158
+ end
159
+
160
+ # Updates the Span name
161
+ #
162
+ # Upon this update, any sampling behavior based on Span name will depend
163
+ # on the implementation.
164
+ #
165
+ # @param [String] new_name The new operation name, which supersedes
166
+ # whatever was passed in when the Span was started
167
+ #
168
+ # @return [void]
169
+ def name=(new_name)
170
+ super
171
+ @mutex.synchronize do
172
+ if @ended
173
+ OpenTelemetry.logger.warn('Calling name= on an ended Span.')
174
+ else
175
+ @name = new_name
176
+ end
177
+ end
178
+ end
179
+
180
+ # Finishes the Span
181
+ #
182
+ # Implementations MUST ignore all subsequent calls to {#finish} (there
183
+ # might be exceptions when Tracer is streaming event and has no mutable
184
+ # state associated with the Span).
185
+ #
186
+ # Call to {#finish} MUST not have any effects on child spans. Those may
187
+ # still be running and can be ended later.
188
+ #
189
+ # This API MUST be non-blocking*.
190
+ #
191
+ # (*) not actually non-blocking. In particular, it synchronizes on an
192
+ # internal mutex, which will typically be uncontended, and
193
+ # {Export::BatchSpanProcessor} will also synchronize on a mutex, if that
194
+ # processor is used.
195
+ #
196
+ # @param [Time] end_timestamp optional end timestamp for the span.
197
+ #
198
+ # @return [self] returns itself
199
+ def finish(end_timestamp: nil)
200
+ @mutex.synchronize do
201
+ if @ended
202
+ OpenTelemetry.logger.warn('Calling finish on an ended Span.')
203
+ return self
204
+ end
205
+ @end_timestamp = end_timestamp || Time.now
206
+ @attributes.freeze
207
+ @events.freeze
208
+ @ended = true
209
+ end
210
+ @span_processor.on_finish(self)
211
+ self
212
+ end
213
+
214
+ # @api private
215
+ #
216
+ # Returns a SpanData containing a snapshot of the Span fields. It is
217
+ # assumed that the Span has been finished, and that no further
218
+ # modifications will be made to the Span.
219
+ #
220
+ # This method should be called *only* from a SpanProcessor prior to
221
+ # calling the SpanExporter.
222
+ #
223
+ # @return [SpanData]
224
+ def to_span_data
225
+ SpanData.new(
226
+ @name,
227
+ @kind,
228
+ @status,
229
+ @parent_span_id,
230
+ @child_count,
231
+ @total_recorded_attributes,
232
+ @total_recorded_events,
233
+ @total_recorded_links,
234
+ @start_timestamp,
235
+ @end_timestamp,
236
+ @attributes,
237
+ @links,
238
+ @events,
239
+ @library_resource,
240
+ @instrumentation_library,
241
+ context.span_id,
242
+ context.trace_id,
243
+ context.trace_flags,
244
+ context.tracestate
245
+ )
246
+ end
247
+
248
+ # @api private
249
+ def initialize(context, name, kind, parent_span_id, trace_config, span_processor, attributes, links, start_timestamp, library_resource, instrumentation_library) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
250
+ super(span_context: context)
251
+ @mutex = Mutex.new
252
+ @name = name
253
+ @kind = kind
254
+ @parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID
255
+ @trace_config = trace_config
256
+ @span_processor = span_processor
257
+ @library_resource = library_resource
258
+ @instrumentation_library = instrumentation_library
259
+ @ended = false
260
+ @status = nil
261
+ @child_count = 0
262
+ @total_recorded_events = 0
263
+ @total_recorded_links = links&.size || 0
264
+ @total_recorded_attributes = attributes&.size || 0
265
+ @start_timestamp = start_timestamp
266
+ @end_timestamp = nil
267
+ @attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes.
268
+ trim_span_attributes(@attributes)
269
+ @events = nil
270
+ @links = trim_links(links, trace_config.max_links_count, trace_config.max_attributes_per_link)
271
+ @span_processor.on_start(self)
272
+ end
273
+
274
+ # TODO: Java implementation overrides finalize to log if a span isn't finished.
275
+
276
+ private
277
+
278
+ def trim_span_attributes(attrs)
279
+ return if attrs.nil?
280
+
281
+ excess = attrs.size - @trace_config.max_attributes_count
282
+ # TODO: with Ruby 2.5, replace with the more efficient
283
+ # attrs.shift(excess) if excess.positive?
284
+ excess.times { attrs.shift } if excess.positive?
285
+ nil
286
+ end
287
+
288
+ def trim_links(links, max_links_count, max_attributes_per_link) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
289
+ # Fast path (likely) common cases.
290
+ return nil if links.nil?
291
+
292
+ if links.size <= max_links_count &&
293
+ links.all? { |link| link.attributes.size <= max_attributes_per_link && Internal.valid_attributes?(link.attributes) }
294
+ return links.frozen? ? links : links.clone.freeze
295
+ end
296
+
297
+ # Slow path: trim attributes for each Link.
298
+ links.last(max_links_count).map! do |link|
299
+ attrs = Hash[link.attributes] # link.attributes is frozen, so we need an unfrozen copy to adjust.
300
+ attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
301
+ excess = attrs.size - max_attributes_per_link
302
+ excess.times { attrs.shift } if excess.positive?
303
+ OpenTelemetry::Trace::Link.new(link.context, attrs)
304
+ end.freeze
305
+ end
306
+
307
+ def append_event(events, event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
308
+ max_events_count = @trace_config.max_events_count
309
+ max_attributes_per_event = @trace_config.max_attributes_per_event
310
+
311
+ # Fast path (likely) common case.
312
+ if events.size < max_events_count &&
313
+ event.attributes.size <= max_attributes_per_event &&
314
+ Internal.valid_attributes?(event.attributes)
315
+ return events << event
316
+ end
317
+
318
+ # Slow path.
319
+ excess = events.size + 1 - max_events_count
320
+ events.shift(excess) if excess.positive?
321
+
322
+ excess = event.attributes.size - max_attributes_per_event
323
+ if excess.positive? || !Internal.valid_attributes?(event.attributes)
324
+ attrs = Hash[event.attributes] # event.attributes is frozen, so we need an unfrozen copy to adjust.
325
+ attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
326
+ excess = attrs.size - max_attributes_per_event
327
+ excess.times { attrs.shift } if excess.positive?
328
+ event = OpenTelemetry::Trace::Event.new(name: event.name, attributes: attrs, timestamp: event.timestamp)
329
+ end
330
+ events << event
331
+ end
332
+ end
333
+ # rubocop:enable Metrics/ClassLength
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2019 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ # The Trace module contains the OpenTelemetry tracing reference
10
+ # implementation.
11
+ module Trace
12
+ # SpanData is a Struct containing {Span} data for export.
13
+ SpanData = Struct.new(:name,
14
+ :kind,
15
+ :status,
16
+ :parent_span_id,
17
+ :child_count,
18
+ :total_recorded_attributes,
19
+ :total_recorded_events,
20
+ :total_recorded_links,
21
+ :start_timestamp,
22
+ :end_timestamp,
23
+ :attributes,
24
+ :links,
25
+ :events,
26
+ :library_resource,
27
+ :instrumentation_library,
28
+ :span_id,
29
+ :trace_id,
30
+ :trace_flags,
31
+ :tracestate)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Trace
10
+ # {Tracer} is the SDK implementation of {OpenTelemetry::Trace::Tracer}.
11
+ class Tracer < OpenTelemetry::Trace::Tracer
12
+ attr_reader :name
13
+ attr_reader :version
14
+ attr_reader :tracer_provider
15
+
16
+ # @api private
17
+ #
18
+ # Returns a new {Tracer} instance.
19
+ #
20
+ # @param [String] name Instrumentation package name
21
+ # @param [String] version Instrumentation package version
22
+ # @param [TracerProvider] tracer_provider TracerProvider that initialized the tracer
23
+ #
24
+ # @return [Tracer]
25
+ def initialize(name, version, tracer_provider)
26
+ @name = name
27
+ @version = version
28
+ @instrumentation_library = InstrumentationLibrary.new(name, version)
29
+ @tracer_provider = tracer_provider
30
+ end
31
+
32
+ def start_root_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
33
+ start_span(name, with_parent_context: Context.empty, attributes: attributes, links: links, start_timestamp: start_timestamp, kind: kind)
34
+ end
35
+
36
+ def start_span(name, with_parent: nil, with_parent_context: nil, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
37
+ name ||= 'empty'
38
+
39
+ parent_span_context = with_parent&.context || active_span_context(with_parent_context)
40
+ parent_span_context = nil unless parent_span_context.valid?
41
+ parent_span_id = parent_span_context&.span_id
42
+ tracestate = parent_span_context&.tracestate
43
+ trace_id = parent_span_context&.trace_id
44
+ trace_id ||= OpenTelemetry::Trace.generate_trace_id
45
+ span_id = OpenTelemetry::Trace.generate_span_id
46
+ sampler = tracer_provider.active_trace_config.sampler
47
+ result = sampler.should_sample?(trace_id: trace_id, parent_context: parent_span_context, links: links, name: name, kind: kind, attributes: attributes)
48
+ internal_create_span(result, name, kind, trace_id, span_id, parent_span_id, attributes, links, start_timestamp, tracestate)
49
+ end
50
+
51
+ private
52
+
53
+ def internal_create_span(result, name, kind, trace_id, span_id, parent_span_id, attributes, links, start_timestamp, tracestate) # rubocop:disable Metrics/AbcSize
54
+ if result.recording? && !tracer_provider.stopped?
55
+ trace_flags = result.sampled? ? OpenTelemetry::Trace::TraceFlags::SAMPLED : OpenTelemetry::Trace::TraceFlags::DEFAULT
56
+ context = OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id, trace_flags: trace_flags, tracestate: tracestate)
57
+ attributes = attributes&.merge(result.attributes) || result.attributes
58
+ Span.new(
59
+ context,
60
+ name,
61
+ kind,
62
+ parent_span_id,
63
+ tracer_provider.active_trace_config,
64
+ tracer_provider.active_span_processor,
65
+ attributes,
66
+ links,
67
+ start_timestamp || Time.now,
68
+ tracer_provider.resource,
69
+ @instrumentation_library
70
+ )
71
+ else
72
+ OpenTelemetry::Trace::Span.new(span_context: OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id))
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end