opentelemetry-sdk 0.16.0 → 1.0.0.rc3

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +133 -61
  3. data/README.md +25 -2
  4. data/lib/opentelemetry/sdk.rb +5 -3
  5. data/lib/opentelemetry/sdk/configurator.rb +54 -43
  6. data/lib/opentelemetry/sdk/forwarding_logger.rb +69 -0
  7. data/lib/opentelemetry/sdk/internal.rb +3 -3
  8. data/lib/opentelemetry/sdk/resources.rb +0 -1
  9. data/lib/opentelemetry/sdk/resources/resource.rb +16 -9
  10. data/lib/opentelemetry/sdk/trace.rb +2 -3
  11. data/lib/opentelemetry/sdk/trace/event.rb +7 -36
  12. data/lib/opentelemetry/sdk/trace/export.rb +3 -2
  13. data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +4 -3
  14. data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +13 -4
  15. data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +2 -0
  16. data/lib/opentelemetry/sdk/trace/export/{noop_span_exporter.rb → span_exporter.rb} +8 -7
  17. data/lib/opentelemetry/sdk/trace/span.rb +55 -39
  18. data/lib/opentelemetry/sdk/trace/span_data.rb +25 -18
  19. data/lib/opentelemetry/sdk/trace/span_limits.rb +60 -0
  20. data/lib/opentelemetry/sdk/trace/{noop_span_processor.rb → span_processor.rb} +5 -8
  21. data/lib/opentelemetry/sdk/trace/tracer.rb +1 -37
  22. data/lib/opentelemetry/sdk/trace/tracer_provider.rb +89 -19
  23. data/lib/opentelemetry/sdk/version.rb +1 -1
  24. metadata +74 -24
  25. data/lib/opentelemetry/sdk/baggage.rb +0 -16
  26. data/lib/opentelemetry/sdk/baggage/builder.rb +0 -40
  27. data/lib/opentelemetry/sdk/baggage/manager.rb +0 -97
  28. data/lib/opentelemetry/sdk/resources/constants.rb +0 -205
  29. data/lib/opentelemetry/sdk/trace/config.rb +0 -18
  30. data/lib/opentelemetry/sdk/trace/config/trace_config.rb +0 -79
  31. data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +0 -76
  32. data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +0 -86
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module OpenTelemetry
6
+ module SDK
7
+ # The ForwardingLogger provides a wrapper to control the OpenTelemetry
8
+ # log level, while respecting the configured level of the supplied logger.
9
+ # If the OTEL_LOG_LEVEL is set to debug, and the supplied logger is configured
10
+ # with an ERROR log level, only OpenTelemetry logs at the ERROR level or higher
11
+ # will be emitted.
12
+ class ForwardingLogger
13
+ def initialize(logger, level:) # rubocop:disable Metrics/CyclomaticComplexity
14
+ @logger = logger
15
+
16
+ if level.is_a?(Integer)
17
+ @level = level
18
+ else
19
+ case level.to_s.downcase
20
+ when 'debug'
21
+ @level = Logger::DEBUG
22
+ when 'info'
23
+ @level = Logger::INFO
24
+ when 'warn'
25
+ @level = Logger::WARN
26
+ when 'error'
27
+ @level = Logger::ERROR
28
+ when 'fatal'
29
+ @level = Logger::FATAL
30
+ when 'unknown'
31
+ @level = Logger::UNKNOWN
32
+ else
33
+ raise ArgumentError, "invalid log level: #{level}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def add(severity, message = nil, progname = nil)
39
+ return true if severity < @level
40
+
41
+ @logger.add(severity, message, progname)
42
+ end
43
+
44
+ def debug(progname = nil, &block)
45
+ add(Logger::DEBUG, nil, progname, &block)
46
+ end
47
+
48
+ def info(progname = nil, &block)
49
+ add(Logger::INFO, nil, progname, &block)
50
+ end
51
+
52
+ def warn(progname = nil, &block)
53
+ add(Logger::WARN, nil, progname, &block)
54
+ end
55
+
56
+ def error(progname = nil, &block)
57
+ add(Logger::ERROR, nil, progname, &block)
58
+ end
59
+
60
+ def fatal(progname = nil, &block)
61
+ add(Logger::FATAL, nil, progname, &block)
62
+ end
63
+
64
+ def unknown(progname = nil, &block)
65
+ add(Logger::UNKNOWN, nil, progname, &block)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -44,13 +44,13 @@ module OpenTelemetry
44
44
  valid_simple_value?(value) || valid_array_value?(value)
45
45
  end
46
46
 
47
- def valid_attributes?(attrs)
47
+ def valid_attributes?(owner, kind, attrs)
48
48
  attrs.nil? || attrs.all? do |k, v|
49
49
  if !valid_key?(k)
50
- OpenTelemetry.handle_error(message: "invalid attribute key type #{v.class}")
50
+ OpenTelemetry.handle_error(message: "invalid #{kind} attribute key type #{k.class} on span '#{owner}'")
51
51
  false
52
52
  elsif !valid_value?(v)
53
- OpenTelemetry.handle_error(message: "invalid attribute value type #{v.class}")
53
+ OpenTelemetry.handle_error(message: "invalid #{kind} attribute value type #{v.class} for key '#{k}' on span '#{owner}'")
54
54
  false
55
55
  else
56
56
  true
@@ -13,4 +13,3 @@ module OpenTelemetry
13
13
  end
14
14
 
15
15
  require 'opentelemetry/sdk/resources/resource'
16
- require 'opentelemetry/sdk/resources/constants'
@@ -31,14 +31,14 @@ module OpenTelemetry
31
31
  end
32
32
 
33
33
  def default
34
- @default ||= create(Constants::SERVICE_RESOURCE[:name] => 'unknown_service').merge(process).merge(telemetry_sdk)
34
+ @default ||= create(SemanticConventions::Resource::SERVICE_NAME => 'unknown_service').merge(process).merge(telemetry_sdk).merge(service_name_from_env)
35
35
  end
36
36
 
37
37
  def telemetry_sdk
38
38
  resource_attributes = {
39
- Constants::TELEMETRY_SDK_RESOURCE[:name] => 'opentelemetry',
40
- Constants::TELEMETRY_SDK_RESOURCE[:language] => 'ruby',
41
- Constants::TELEMETRY_SDK_RESOURCE[:version] => OpenTelemetry::SDK::VERSION
39
+ SemanticConventions::Resource::TELEMETRY_SDK_NAME => 'opentelemetry',
40
+ SemanticConventions::Resource::TELEMETRY_SDK_LANGUAGE => 'ruby',
41
+ SemanticConventions::Resource::TELEMETRY_SDK_VERSION => OpenTelemetry::SDK::VERSION
42
42
  }
43
43
 
44
44
  resource_pairs = ENV['OTEL_RESOURCE_ATTRIBUTES']
@@ -55,15 +55,22 @@ module OpenTelemetry
55
55
 
56
56
  def process
57
57
  resource_attributes = {
58
- Constants::PROCESS_RESOURCE[:pid] => Process.pid,
59
- Constants::PROCESS_RESOURCE[:command] => $PROGRAM_NAME,
60
- Constants::PROCESS_RUNTIME_RESOURCE[:name] => RUBY_ENGINE,
61
- Constants::PROCESS_RUNTIME_RESOURCE[:version] => RUBY_VERSION,
62
- Constants::PROCESS_RUNTIME_RESOURCE[:description] => RUBY_DESCRIPTION
58
+ SemanticConventions::Resource::PROCESS_PID => Process.pid,
59
+ SemanticConventions::Resource::PROCESS_COMMAND => $PROGRAM_NAME,
60
+ SemanticConventions::Resource::PROCESS_RUNTIME_NAME => RUBY_ENGINE,
61
+ SemanticConventions::Resource::PROCESS_RUNTIME_VERSION => RUBY_VERSION,
62
+ SemanticConventions::Resource::PROCESS_RUNTIME_DESCRIPTION => RUBY_DESCRIPTION
63
63
  }
64
64
 
65
65
  create(resource_attributes)
66
66
  end
67
+
68
+ private
69
+
70
+ def service_name_from_env
71
+ service_name = ENV['OTEL_SERVICE_NAME']
72
+ create(SemanticConventions::Resource::SERVICE_NAME => service_name) unless service_name.nil?
73
+ end
67
74
  end
68
75
 
69
76
  # @api private
@@ -14,12 +14,11 @@ module OpenTelemetry
14
14
  end
15
15
 
16
16
  require 'opentelemetry/sdk/trace/samplers'
17
- require 'opentelemetry/sdk/trace/config'
17
+ require 'opentelemetry/sdk/trace/span_limits'
18
18
  require 'opentelemetry/sdk/trace/event'
19
19
  require 'opentelemetry/sdk/trace/export'
20
- require 'opentelemetry/sdk/trace/multi_span_processor'
21
- require 'opentelemetry/sdk/trace/noop_span_processor'
22
20
  require 'opentelemetry/sdk/trace/span_data'
21
+ require 'opentelemetry/sdk/trace/span_processor'
23
22
  require 'opentelemetry/sdk/trace/span'
24
23
  require 'opentelemetry/sdk/trace/tracer'
25
24
  require 'opentelemetry/sdk/trace/tracer_provider'
@@ -7,42 +7,13 @@
7
7
  module OpenTelemetry
8
8
  module SDK
9
9
  module Trace
10
- # A text annotation with a set of attributes and a timestamp.
11
- class Event
12
- EMPTY_ATTRIBUTES = {}.freeze
13
-
14
- private_constant :EMPTY_ATTRIBUTES
15
-
16
- # Returns the name of this event
17
- #
18
- # @return [String]
19
- attr_reader :name
20
-
21
- # Returns the frozen attributes for this event
22
- #
23
- # @return [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
24
- attr_reader :attributes
25
-
26
- # Returns the timestamp for this event
27
- #
28
- # @return [Time]
29
- attr_reader :timestamp
30
-
31
- # Returns a new immutable {Event}.
32
- #
33
- # @param [String] name The name of this event
34
- # @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
35
- # attributes A hash of attributes for this event. Attributes will be
36
- # frozen during Event initialization.
37
- # @param [optional Time] timestamp The timestamp for this event.
38
- # Defaults to Time.now.
39
- # @return [Event]
40
- def initialize(name:, attributes: nil, timestamp: nil)
41
- @name = name
42
- @attributes = attributes.freeze || EMPTY_ATTRIBUTES
43
- @timestamp = timestamp || Time.now
44
- end
45
- end
10
+ # A text annotation with a set of attributes and a timestamp for export.
11
+ #
12
+ # Field types are as follows:
13
+ # name: String
14
+ # attributes: frozen Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}
15
+ # timestamp: Integer nanoseconds since Epoch
16
+ Event = Struct.new(:name, :attributes, :timestamp)
46
17
  end
47
18
  end
48
19
  end
@@ -10,6 +10,8 @@ module OpenTelemetry
10
10
  # The Export module contains the built-in exporters and span processors for the OpenTelemetry
11
11
  # reference implementation.
12
12
  module Export
13
+ ExportError = Class.new(OpenTelemetry::Error)
14
+
13
15
  # Result codes for the SpanExporter#export method and the SpanProcessor#force_flush and SpanProcessor#shutdown methods.
14
16
 
15
17
  # The operation finished successfully.
@@ -31,6 +33,5 @@ require 'opentelemetry/sdk/trace/export/batch_span_processor'
31
33
  require 'opentelemetry/sdk/trace/export/console_span_exporter'
32
34
  require 'opentelemetry/sdk/trace/export/in_memory_span_exporter'
33
35
  require 'opentelemetry/sdk/trace/export/metrics_reporter'
34
- require 'opentelemetry/sdk/trace/export/multi_span_exporter'
35
- require 'opentelemetry/sdk/trace/export/noop_span_exporter'
36
+ require 'opentelemetry/sdk/trace/export/span_exporter'
36
37
  require 'opentelemetry/sdk/trace/export/simple_span_processor'
@@ -52,6 +52,7 @@ module OpenTelemetry
52
52
  start_thread_on_boot: String(ENV['OTEL_RUBY_BSP_START_THREAD_ON_BOOT']) !~ /false/i,
53
53
  metrics_reporter: nil)
54
54
  raise ArgumentError if max_export_batch_size > max_queue_size
55
+ raise ArgumentError, "exporter #{exporter.inspect} does not appear to be a valid exporter" unless Common::Utilities.valid_exporter?(exporter)
55
56
 
56
57
  @exporter = exporter
57
58
  @exporter_timeout_seconds = exporter_timeout / 1000.0
@@ -100,7 +101,7 @@ module OpenTelemetry
100
101
  # @return [Integer] SUCCESS if no error occurred, FAILURE if a
101
102
  # non-specific failure occurred, TIMEOUT if a timeout occurred.
102
103
  def force_flush(timeout: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
103
- start_time = Time.now
104
+ start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
104
105
  snapshot = lock do
105
106
  reset_on_fork if @keep_running
106
107
  spans.shift(spans.size)
@@ -136,7 +137,7 @@ module OpenTelemetry
136
137
  # @return [Integer] SUCCESS if no error occurred, FAILURE if a
137
138
  # non-specific failure occurred, TIMEOUT if a timeout occurred.
138
139
  def shutdown(timeout: nil)
139
- start_time = Time.now
140
+ start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
140
141
  thread = lock do
141
142
  @keep_running = false
142
143
  @condition.signal
@@ -193,7 +194,7 @@ module OpenTelemetry
193
194
  @metrics_reporter.add_to_counter('otel.bsp.export.success')
194
195
  @metrics_reporter.add_to_counter('otel.bsp.exported_spans', increment: batch.size)
195
196
  else
196
- OpenTelemetry.handle_error(message: "Unable to export #{batch.size} spans")
197
+ OpenTelemetry.handle_error(exception: ExportError.new("Unable to export #{batch.size} spans"))
197
198
  @metrics_reporter.add_to_counter('otel.bsp.export.failure')
198
199
  report_dropped_spans(batch.size, reason: 'export-failure')
199
200
  end
@@ -10,30 +10,39 @@ module OpenTelemetry
10
10
  module Export
11
11
  # A SpanExporter implementation that can be used to test OpenTelemetry integration.
12
12
  #
13
- # Example usage:
13
+ # Example usage in a test suite:
14
14
  #
15
15
  # class MyClassTest
16
16
  # def setup
17
17
  # @tracer_provider = TracerProvider.new
18
- # @exporter = InMemorySpanExporter.new
18
+ # # The default is `recording: true`, which is appropriate in non-test environments.
19
+ # @exporter = InMemorySpanExporter.new(recording: false)
19
20
  # @tracer_provider.add_span_processor(SimpleSampledSpansProcessor.new(@exporter))
20
21
  # end
21
22
  #
22
23
  # def test_finished_spans
24
+ # @exporter.recording = true
23
25
  # @tracer_provider.tracer.in_span("span") {}
24
26
  #
25
27
  # spans = @exporter.finished_spans
26
28
  # spans.wont_be_nil
27
29
  # spans.size.must_equal(1)
28
30
  # spans[0].name.must_equal("span")
31
+ #
32
+ # @exporter.recording = false
29
33
  # end
30
34
  class InMemorySpanExporter
35
+ # Controls whether or not the exporter will record spans, or discard them.
36
+ # @return [Boolean] when true, the exporter is recording. By default, this is true.
37
+ attr_accessor :recording
38
+
31
39
  # Returns a new instance of the {InMemorySpanExporter}.
32
40
  #
33
41
  # @return a new instance of the {InMemorySpanExporter}.
34
- def initialize
42
+ def initialize(recording: true)
35
43
  @finished_spans = []
36
44
  @stopped = false
45
+ @recording = recording
37
46
  @mutex = Mutex.new
38
47
  end
39
48
 
@@ -67,7 +76,7 @@ module OpenTelemetry
67
76
  @mutex.synchronize do
68
77
  return FAILURE if @stopped
69
78
 
70
- @finished_spans.concat(span_datas.to_a)
79
+ @finished_spans.concat(span_datas.to_a) if @recording
71
80
  end
72
81
  SUCCESS
73
82
  end
@@ -29,6 +29,8 @@ module OpenTelemetry
29
29
  # @return [SimpleSpanProcessor]
30
30
  # @raise ArgumentError if the span_exporter is nil.
31
31
  def initialize(span_exporter)
32
+ raise ArgumentError, "exporter #{span_exporter.inspect} does not appear to be a valid exporter" unless Common::Utilities.valid_exporter?(span_exporter)
33
+
32
34
  @span_exporter = span_exporter
33
35
  end
34
36
 
@@ -8,24 +8,25 @@ module OpenTelemetry
8
8
  module SDK
9
9
  module Trace
10
10
  module Export
11
- # A noop exporter that demonstrates and documents the SpanExporter
12
- # duck type. SpanExporter allows different tracing services to export
11
+ # SpanExporter describes a duck type. It is not required to subclass this
12
+ # class to provide an implementation of SpanExporter, provided the interface is
13
+ # satisfied. SpanExporter allows different tracing services to export
13
14
  # recorded data for sampled spans in their own format.
14
15
  #
15
16
  # To export data an exporter MUST be registered to the {TracerProvider} using
16
- # a {SimpleSpanProcessor} or a {BatchSpanProcessor}.
17
- class NoopSpanExporter
17
+ # a {SpanProcessor} implementation.
18
+ class SpanExporter
18
19
  def initialize
19
20
  @stopped = false
20
21
  end
21
22
 
22
- # Called to export sampled {Span}s.
23
+ # Called to export sampled {SpanData}s.
23
24
  #
24
- # @param [Enumerable<Span>] spans the list of sampled {Span}s to be
25
+ # @param [Enumerable<SpanData>] span_data the list of sampled {SpanData} to be
25
26
  # exported.
26
27
  # @param [optional Numeric] timeout An optional timeout in seconds.
27
28
  # @return [Integer] the result of the export.
28
- def export(spans, timeout: nil)
29
+ def export(span_data, timeout: nil)
29
30
  return SUCCESS unless @stopped
30
31
 
31
32
  FAILURE
@@ -10,15 +10,16 @@ module OpenTelemetry
10
10
  # Implementation of {OpenTelemetry::Trace::Span} that records trace events.
11
11
  #
12
12
  # This implementation includes reader methods intended to allow access to
13
- # internal state by SpanProcessors (see {NoopSpanProcessor} for the interface).
13
+ # internal state by {SpanProcessor}s.
14
14
  # Instrumentation should use the API provided by {OpenTelemetry::Trace::Span}
15
15
  # and should consider {Span} to be write-only.
16
16
  #
17
17
  # rubocop:disable Metrics/ClassLength
18
18
  class Span < OpenTelemetry::Trace::Span
19
- DEFAULT_STATUS = OpenTelemetry::Trace::Status.new(OpenTelemetry::Trace::Status::UNSET)
19
+ DEFAULT_STATUS = OpenTelemetry::Trace::Status.unset
20
+ EMPTY_ATTRIBUTES = {}.freeze
20
21
 
21
- private_constant(:DEFAULT_STATUS)
22
+ private_constant :DEFAULT_STATUS, :EMPTY_ATTRIBUTES
22
23
 
23
24
  # The following readers are intended for the use of SpanProcessors and
24
25
  # should not be considered part of the public interface for instrumentation.
@@ -70,7 +71,6 @@ module OpenTelemetry
70
71
  #
71
72
  # @return [self] returns itself
72
73
  def set_attribute(key, value)
73
- super
74
74
  @mutex.synchronize do
75
75
  if @ended
76
76
  OpenTelemetry.logger.warn('Calling set_attribute on an ended Span.')
@@ -99,7 +99,6 @@ module OpenTelemetry
99
99
  #
100
100
  # @return [self] returns itself
101
101
  def add_attributes(attributes)
102
- super
103
102
  @mutex.synchronize do
104
103
  if @ended
105
104
  OpenTelemetry.logger.warn('Calling add_attributes on an ended Span.')
@@ -132,8 +131,7 @@ module OpenTelemetry
132
131
  #
133
132
  # @return [self] returns itself
134
133
  def add_event(name, attributes: nil, timestamp: nil)
135
- super
136
- event = Event.new(name: name, attributes: attributes, timestamp: timestamp || Time.now)
134
+ event = Event.new(name, truncate_attribute_values(attributes), wall_clock(timestamp))
137
135
 
138
136
  @mutex.synchronize do
139
137
  if @ended
@@ -161,7 +159,7 @@ module OpenTelemetry
161
159
  event_attributes = {
162
160
  'exception.type' => exception.class.to_s,
163
161
  'exception.message' => exception.message,
164
- 'exception.stacktrace' => exception.full_message(highlight: false, order: :top)
162
+ 'exception.stacktrace' => exception.full_message(highlight: false, order: :top).encode('UTF-8', invalid: :replace, undef: :replace, replace: '�')
165
163
  }
166
164
  event_attributes.merge!(attributes) unless attributes.nil?
167
165
  add_event('exception', attributes: event_attributes)
@@ -169,21 +167,23 @@ module OpenTelemetry
169
167
 
170
168
  # Sets the Status to the Span
171
169
  #
172
- # If used, this will override the default Span status. Default is OK.
170
+ # If used, this will override the default Span status. Default has code = Status::UNSET.
173
171
  #
174
- # Only the value of the last call will be recorded, and implementations
175
- # are free to ignore previous calls.
172
+ # An attempt to set the status with code == Status::UNSET is ignored.
173
+ # If the status is set with code == Status::OK, any further attempt to set the status
174
+ # is ignored.
176
175
  #
177
176
  # @param [Status] status The new status, which overrides the default Span
178
- # status, which is OK.
177
+ # status, which has code = Status::UNSET.
179
178
  #
180
179
  # @return [void]
181
180
  def status=(status)
182
- super
181
+ return if status.code == OpenTelemetry::Trace::Status::UNSET
182
+
183
183
  @mutex.synchronize do
184
184
  if @ended
185
185
  OpenTelemetry.logger.warn('Calling status= on an ended Span.')
186
- else
186
+ elsif @status.code != OpenTelemetry::Trace::Status::OK
187
187
  @status = status
188
188
  end
189
189
  end
@@ -199,7 +199,6 @@ module OpenTelemetry
199
199
  #
200
200
  # @return [void]
201
201
  def name=(new_name)
202
- super
203
202
  @mutex.synchronize do
204
203
  if @ended
205
204
  OpenTelemetry.logger.warn('Calling name= on an ended Span.')
@@ -234,12 +233,12 @@ module OpenTelemetry
234
233
  OpenTelemetry.logger.warn('Calling finish on an ended Span.')
235
234
  return self
236
235
  end
237
- @end_timestamp = end_timestamp || Time.now
236
+ @end_timestamp = wall_clock(end_timestamp)
238
237
  @attributes = validated_attributes(@attributes).freeze
239
238
  @events.freeze
240
239
  @ended = true
241
240
  end
242
- @span_processor.on_finish(self)
241
+ @span_processors.each { |processor| processor.on_finish(self) }
243
242
  self
244
243
  end
245
244
 
@@ -277,14 +276,14 @@ module OpenTelemetry
277
276
  end
278
277
 
279
278
  # @api private
280
- def initialize(context, parent_context, name, kind, parent_span_id, trace_config, span_processor, attributes, links, start_timestamp, resource, instrumentation_library) # rubocop:disable Metrics/AbcSize
279
+ def initialize(context, parent_context, name, kind, parent_span_id, span_limits, span_processors, attributes, links, start_timestamp, resource, instrumentation_library) # rubocop:disable Metrics/AbcSize
281
280
  super(span_context: context)
282
281
  @mutex = Mutex.new
283
282
  @name = name
284
283
  @kind = kind
285
284
  @parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID
286
- @trace_config = trace_config
287
- @span_processor = span_processor
285
+ @span_limits = span_limits
286
+ @span_processors = span_processors
288
287
  @resource = resource
289
288
  @instrumentation_library = instrumentation_library
290
289
  @ended = false
@@ -292,13 +291,13 @@ module OpenTelemetry
292
291
  @total_recorded_events = 0
293
292
  @total_recorded_links = links&.size || 0
294
293
  @total_recorded_attributes = attributes&.size || 0
295
- @start_timestamp = start_timestamp
294
+ @start_timestamp = wall_clock(start_timestamp)
296
295
  @end_timestamp = nil
297
296
  @attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes.
298
297
  trim_span_attributes(@attributes)
299
298
  @events = nil
300
- @links = trim_links(links, trace_config.max_links_count, trace_config.max_attributes_per_link)
301
- @span_processor.on_start(self, parent_context)
299
+ @links = trim_links(links, span_limits.link_count_limit, span_limits.link_attribute_count_limit)
300
+ @span_processors.each { |processor| processor.on_start(self, parent_context) }
302
301
  end
303
302
 
304
303
  # TODO: Java implementation overrides finalize to log if a span isn't finished.
@@ -306,7 +305,7 @@ module OpenTelemetry
306
305
  private
307
306
 
308
307
  def validated_attributes(attrs)
309
- return attrs if Internal.valid_attributes?(attrs)
308
+ return attrs if Internal.valid_attributes?(name, 'span', attrs)
310
309
 
311
310
  attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
312
311
  end
@@ -314,56 +313,73 @@ module OpenTelemetry
314
313
  def trim_span_attributes(attrs)
315
314
  return if attrs.nil?
316
315
 
317
- excess = attrs.size - @trace_config.max_attributes_count
316
+ excess = attrs.size - @span_limits.attribute_count_limit
318
317
  excess.times { attrs.shift } if excess.positive?
318
+ truncate_attribute_values(attrs)
319
319
  nil
320
320
  end
321
321
 
322
- def trim_links(links, max_links_count, max_attributes_per_link) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
322
+ def truncate_attribute_values(attrs)
323
+ return EMPTY_ATTRIBUTES if attrs.nil?
324
+
325
+ attribute_length_limit = @span_limits.attribute_length_limit
326
+ attrs.each { |key, value| attrs[key] = OpenTelemetry::Common::Utilities.truncate(value, attribute_length_limit) } if attribute_length_limit
327
+ attrs
328
+ end
329
+
330
+ def trim_links(links, link_count_limit, link_attribute_count_limit) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
323
331
  # Fast path (likely) common cases.
324
332
  return nil if links.nil?
325
333
 
326
- if links.size <= max_links_count &&
327
- links.all? { |link| link.attributes.size <= max_attributes_per_link && Internal.valid_attributes?(link.attributes) }
334
+ if links.size <= link_count_limit &&
335
+ links.all? { |link| link.span_context.valid? && link.attributes.size <= link_attribute_count_limit && Internal.valid_attributes?(name, 'link', link.attributes) }
328
336
  return links.frozen? ? links : links.clone.freeze
329
337
  end
330
338
 
331
339
  # Slow path: trim attributes for each Link.
332
- links.last(max_links_count).map! do |link|
340
+ valid_links = links.select { |link| link.span_context.valid? }
341
+ excess_link_count = valid_links.size - link_count_limit
342
+ valid_links.pop(excess_link_count) if excess_link_count.positive?
343
+ valid_links.map! do |link|
333
344
  attrs = Hash[link.attributes] # link.attributes is frozen, so we need an unfrozen copy to adjust.
334
345
  attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
335
- excess = attrs.size - max_attributes_per_link
346
+ excess = attrs.size - link_attribute_count_limit
336
347
  excess.times { attrs.shift } if excess.positive?
337
348
  OpenTelemetry::Trace::Link.new(link.span_context, attrs)
338
349
  end.freeze
339
350
  end
340
351
 
341
352
  def append_event(events, event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
342
- max_events_count = @trace_config.max_events_count
343
- max_attributes_per_event = @trace_config.max_attributes_per_event
344
- valid_attributes = Internal.valid_attributes?(event.attributes)
353
+ event_count_limit = @span_limits.event_count_limit
354
+ event_attribute_count_limit = @span_limits.event_attribute_count_limit
355
+ valid_attributes = Internal.valid_attributes?(name, 'event', event.attributes)
345
356
 
346
357
  # Fast path (likely) common case.
347
- if events.size < max_events_count &&
348
- event.attributes.size <= max_attributes_per_event &&
358
+ if events.size < event_count_limit &&
359
+ event.attributes.size <= event_attribute_count_limit &&
349
360
  valid_attributes
350
361
  return events << event
351
362
  end
352
363
 
353
364
  # Slow path.
354
- excess = events.size + 1 - max_events_count
365
+ excess = events.size + 1 - event_count_limit
355
366
  events.shift(excess) if excess.positive?
356
367
 
357
- excess = event.attributes.size - max_attributes_per_event
368
+ excess = event.attributes.size - event_attribute_count_limit
358
369
  if excess.positive? || !valid_attributes
359
370
  attrs = Hash[event.attributes] # event.attributes is frozen, so we need an unfrozen copy to adjust.
360
371
  attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
361
- excess = attrs.size - max_attributes_per_event
372
+ excess = attrs.size - event_attribute_count_limit
362
373
  excess.times { attrs.shift } if excess.positive?
363
- event = Event.new(name: event.name, attributes: attrs, timestamp: event.timestamp)
374
+ event = Event.new(event.name, attrs.freeze, event.timestamp)
364
375
  end
365
376
  events << event
366
377
  end
378
+
379
+ def wall_clock(timestamp)
380
+ timestamp = (timestamp.to_r * 1_000_000_000).to_i unless timestamp.nil?
381
+ timestamp || Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
382
+ end
367
383
  end
368
384
  # rubocop:enable Metrics/ClassLength
369
385
  end