opentelemetry-sdk 0.5.1

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 (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