opentelemetry-sdk 1.0.0.rc1 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5e0b06b7a072f462eeef975e4342a9e9800fb85794e2625f454d17eb20ef2be
4
- data.tar.gz: a42a11084a981f147bdc5a3c5d21f1a4a1752d5a971819658573cb4964d43f49
3
+ metadata.gz: 0b0251902390864d89d8f00496f9ae36adcf3a52d1a84c574a73a107ccea70f0
4
+ data.tar.gz: ed15513dcd3d4decc53a0923db83c8e10d9b65a86247775c05d354212c1439c8
5
5
  SHA512:
6
- metadata.gz: 670059ca8ef7d7aeba579c6b70a8217ac5e4fa879b39afee715b4ff1852e8643bf87581943b70d949480a728c531d20efc8ed42fa878f0a5291d55f31cc22607
7
- data.tar.gz: 604feaa8d9952cce4a0eff45b743601b85ea2a5f7b03af316c5ae45c6df2fd57a2712d43d417a30a2df44b493fd6c8bc03f8924014a75d2fd29b9bfe513c7adf
6
+ metadata.gz: e3954c44377c4250f6dd2dcba2f88a781aa34acec642f19996014014c9d49ce3733846956043f09ce548ceee0362befa9ff9af660f894c39a446bdca5e0d7fb3
7
+ data.tar.gz: f8ddde936a93b87682a2f85a0d3a800ed0de25643bce04e420d01c8565c2ed33548dda6174c213d868aab82fe5398868144051690601d2b49015ea48bd999b9f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Release History: opentelemetry-sdk
2
2
 
3
+ ### v1.0.1 / 2021-10-29
4
+
5
+ * FIXED: Add unexpected error handlign in BSP and OTLP exporter (#995)
6
+
7
+ ### v1.0.0 / 2021-09-29
8
+
9
+ * (No significant changes)
10
+
11
+ ### v1.0.0.rc3 / 2021-08-12
12
+
13
+ * BREAKING CHANGE: Remove optional parent_context from in_span
14
+ * BREAKING CHANGE: Replace Time.now with Process.clock_gettime
15
+ * BREAKING CHANGE: Refactor Baggage to remove Noop*
16
+ * BREAKING CHANGE: Remove unnecessary readers from SDK Tracer
17
+ * BREAKING CHANGE: Total order constraint on span.status=
18
+ * BREAKING CHANGE: Use auto-generated resource constants in sdk and resource_detectors
19
+ * BREAKING CHANGE: Span limits env vars
20
+
21
+ * ADDED: Add Tracer.non_recording_span to API
22
+ * ADDED: Add unnamed tracer warning message
23
+ * ADDED: Allow disabling of install messages
24
+ * ADDED: Make API's NoopTextMapPropagator private
25
+ * ADDED: Use auto-generated resource constants in sdk and resource_detectors
26
+ * ADDED: Allow selecting multiple exporter
27
+ * ADDED: Add explicit BSP export error
28
+ * FIXED: Remove optional parent_context from in_span
29
+ * FIXED: Replace Time.now with Process.clock_gettime
30
+ * FIXED: Rename cloud.zone to cloud.availability_zone
31
+ * FIXED: Improve attribute error messages
32
+ * FIXED: Refactor Baggage to remove Noop*
33
+ * FIXED: Support OTEL_SERVICE_NAME env var
34
+ * FIXED: Remove unnecessary readers from SDK Tracer
35
+ * FIXED: Total order constraint on span.status=
36
+ * FIXED: Flakey tracer provider test
37
+ * FIXED: Split lock in TracerProvider
38
+ * FIXED: Span limits env vars
39
+ * FIXED: Prune invalid links
40
+ * DOCS: Update docs to rely more on environment variable configuration
41
+
42
+ ### v1.0.0.rc2 / 2021-06-23
43
+
44
+ * BREAKING CHANGE: Remove optional parent_context from in_span [729](https://github.com/open-telemetry/opentelemetry-ruby/pull/729)
45
+ * BREAKING CHANGE: Replace Time.now with Process.clock_gettime [717](https://github.com/open-telemetry/opentelemetry-ruby/pull/717)
46
+ * BREAKING CHANGE: Refactor Baggage to remove Noop* [800](https://github.com/open-telemetry/opentelemetry-ruby/pull/800)
47
+ * BREAKING CHANGE: Remove unnecessary readers from SDK Tracer [820](https://github.com/open-telemetry/opentelemetry-ruby/pull/820)
48
+ - Tracer no longer surfaces attribute readers for the name, version, or tracer_provider
49
+ * BREAKING CHANGE: Total order constraint on span.status= [805](https://github.com/open-telemetry/opentelemetry-ruby/pull/805)
50
+
51
+ * ADDED: Add Tracer.non_recording_span to API [799](https://github.com/open-telemetry/opentelemetry-ruby/pull/799)
52
+ * ADDED: Add unnamed tracer warning message [830](https://github.com/open-telemetry/opentelemetry-ruby/pull/830)
53
+ * ADDED: Allow disabling of install messages [831](https://github.com/open-telemetry/opentelemetry-ruby/pull/831)
54
+ * FIXED: Rename cloud.zone to cloud.availability_zone [734](https://github.com/open-telemetry/opentelemetry-ruby/pull/734)
55
+ * FIXED: Improve attribute error messages [742](https://github.com/open-telemetry/opentelemetry-ruby/pull/742)
56
+ * FIXED: Support OTEL_SERVICE_NAME env var [806]https://github.com/open-telemetry/opentelemetry-ruby/pull/806
57
+ * FIXED: Flakey tracer provider test
58
+
3
59
  ### v1.0.0.rc1 / 2021-05-21
4
60
 
5
61
  * BREAKING CHANGE: Remove optional parent_context from in_span
data/README.md CHANGED
@@ -29,10 +29,33 @@ Then, configure the SDK according to your desired handling of telemetry data, an
29
29
  ```ruby
30
30
  require 'opentelemetry/sdk'
31
31
 
32
- # Configure the sdk with default export and context propagation formats
33
- # see SDK#configure for customizing the setup
32
+ # Configure the sdk with default export and context propagation formats.
34
33
  OpenTelemetry::SDK.configure
35
34
 
35
+ # Many configuration options may be set via the environment. To use them,
36
+ # set the appropriate variable before calling configure. For example:
37
+ #
38
+ # ENV['OTEL_TRACES_EXPORTER'] = 'console'
39
+ # ENV['OTEL_PROPAGATORS'] = 'ottrace'
40
+ # OpenTelemetry::SDK.configure
41
+ #
42
+ # You may also configure the SDK programmatically, for advanced usage or to
43
+ # enable auto-instrumentation. For example:
44
+ #
45
+ # OpenTelemetry::SDK.configure do |c|
46
+ # c.service_name = something_calculated_dynamically
47
+ # c.add_span_processor(
48
+ # OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
49
+ # OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
50
+ # )
51
+ # )
52
+ #
53
+ # c.use 'OpenTelemetry::Instrumentation::Net::HTTP'
54
+ # end
55
+ #
56
+ # Note that the SimpleSpanExporter is not recommended for use in production.
57
+
58
+
36
59
  # To start a trace you need to get a Tracer from the TracerProvider
37
60
  tracer = OpenTelemetry.tracer_provider.tracer('my_app_or_gem', '0.1.0')
38
61
 
@@ -9,13 +9,29 @@ module OpenTelemetry
9
9
  # The configurator provides defaults and facilitates configuring the
10
10
  # SDK for use.
11
11
  class Configurator # rubocop:disable Metrics/ClassLength
12
+ # @api private
13
+ class NoopTextMapPropagator
14
+ EMPTY_LIST = [].freeze
15
+ private_constant(:EMPTY_LIST)
16
+
17
+ def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter); end
18
+
19
+ def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
20
+ context
21
+ end
22
+
23
+ def fields
24
+ EMPTY_LIST
25
+ end
26
+ end
27
+
12
28
  USE_MODE_UNSPECIFIED = 0
13
29
  USE_MODE_ONE = 1
14
30
  USE_MODE_ALL = 2
15
31
 
16
32
  private_constant :USE_MODE_UNSPECIFIED, :USE_MODE_ONE, :USE_MODE_ALL
17
33
 
18
- attr_writer :logger, :propagators, :error_handler, :id_generator
34
+ attr_writer :propagators, :error_handler, :id_generator
19
35
 
20
36
  def initialize
21
37
  @instrumentation_names = []
@@ -31,6 +47,15 @@ module OpenTelemetry
31
47
  @logger ||= OpenTelemetry.logger
32
48
  end
33
49
 
50
+ # Accepts a logger and wraps it in the {ForwardingLogger} which allows
51
+ # for controlling the severity level emitted by the OpenTelemetry.logger
52
+ # independently of the supplied logger.
53
+ #
54
+ # @param [Logger] new_logger The logger for OpenTelemetry to use
55
+ def logger=(new_logger)
56
+ @logger = ForwardingLogger.new(new_logger, level: ENV['OTEL_LOG_LEVEL'] || Logger::INFO)
57
+ end
58
+
34
59
  def error_handler
35
60
  @error_handler ||= OpenTelemetry.error_handler
36
61
  end
@@ -51,7 +76,7 @@ module OpenTelemetry
51
76
  # @param [String] service_name The value to be used as the service name
52
77
  def service_name=(service_name)
53
78
  self.resource = OpenTelemetry::SDK::Resources::Resource.create(
54
- OpenTelemetry::SDK::Resources::Constants::SERVICE_RESOURCE[:name] => service_name
79
+ OpenTelemetry::SemanticConventions::Resource::SERVICE_NAME => service_name
55
80
  )
56
81
  end
57
82
 
@@ -61,7 +86,7 @@ module OpenTelemetry
61
86
  # @param [String] service_version The value to be used as the service version
62
87
  def service_version=(service_version)
63
88
  self.resource = OpenTelemetry::SDK::Resources::Resource.create(
64
- OpenTelemetry::SDK::Resources::Constants::SERVICE_RESOURCE[:version] => service_version
89
+ OpenTelemetry::SemanticConventions::Resource::SERVICE_VERSION => service_version
65
90
  )
66
91
  end
67
92
 
@@ -112,7 +137,6 @@ module OpenTelemetry
112
137
  def configure
113
138
  OpenTelemetry.logger = logger
114
139
  OpenTelemetry.error_handler = error_handler
115
- OpenTelemetry.baggage = Baggage::Manager.new
116
140
  configure_propagation
117
141
  configure_span_processors
118
142
  tracer_provider.id_generator = @id_generator
@@ -123,7 +147,7 @@ module OpenTelemetry
123
147
  private
124
148
 
125
149
  def tracer_provider
126
- @tracer_provider ||= Trace::TracerProvider.new(@resource)
150
+ @tracer_provider ||= Trace::TracerProvider.new(resource: @resource)
127
151
  end
128
152
 
129
153
  def check_use_mode!(mode)
@@ -141,21 +165,23 @@ module OpenTelemetry
141
165
  end
142
166
 
143
167
  def configure_span_processors
144
- processors = @span_processors.empty? ? [wrapped_exporter_from_env].compact : @span_processors
168
+ processors = @span_processors.empty? ? wrapped_exporters_from_env.compact : @span_processors
145
169
  processors.each { |p| tracer_provider.add_span_processor(p) }
146
170
  end
147
171
 
148
- def wrapped_exporter_from_env
149
- exporter = ENV.fetch('OTEL_TRACES_EXPORTER', 'otlp')
150
- case exporter
151
- when 'none' then nil
152
- when 'otlp' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::OTLP::Exporter')
153
- when 'jaeger' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::Jaeger::CollectorExporter')
154
- when 'zipkin' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::Zipkin::Exporter')
155
- when 'console' then Trace::Export::SimpleSpanProcessor.new(Trace::Export::ConsoleSpanExporter.new)
156
- else
157
- OpenTelemetry.logger.warn "The #{exporter} exporter is unknown and cannot be configured, spans will not be exported"
158
- nil
172
+ def wrapped_exporters_from_env
173
+ exporters = ENV.fetch('OTEL_TRACES_EXPORTER', 'otlp')
174
+ exporters.split(',').map do |exporter|
175
+ case exporter.strip
176
+ when 'none' then nil
177
+ when 'otlp' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::OTLP::Exporter')
178
+ when 'jaeger' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::Jaeger::CollectorExporter')
179
+ when 'zipkin' then fetch_exporter(exporter, 'OpenTelemetry::Exporter::Zipkin::Exporter')
180
+ when 'console' then Trace::Export::SimpleSpanProcessor.new(Trace::Export::ConsoleSpanExporter.new)
181
+ else
182
+ OpenTelemetry.logger.warn "The #{exporter} exporter is unknown and cannot be configured, spans will not be exported"
183
+ nil
184
+ end
159
185
  end
160
186
  end
161
187
 
@@ -171,7 +197,7 @@ module OpenTelemetry
171
197
  when 'ottrace' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::OTTrace')
172
198
  else
173
199
  OpenTelemetry.logger.warn "The #{propagator} propagator is unknown and cannot be configured"
174
- Context::Propagation::NoopTextMapPropagator.new
200
+ NoopTextMapPropagator.new
175
201
  end
176
202
  end
177
203
  OpenTelemetry.propagation = Context::Propagation::CompositeTextMapPropagator.compose_propagators((@propagators || propagators).compact)
@@ -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
@@ -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
@@ -13,4 +13,3 @@ module OpenTelemetry
13
13
  end
14
14
 
15
15
  require 'opentelemetry/sdk/resources/resource'
16
- require 'opentelemetry/sdk/resources/constants'
@@ -146,9 +146,9 @@ module OpenTelemetry
146
146
 
147
147
  thread&.join(timeout)
148
148
  force_flush(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
149
- @exporter.shutdown(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
150
149
  dropped_spans = lock { spans.size }
151
150
  report_dropped_spans(dropped_spans, reason: 'terminating') if dropped_spans.positive?
151
+ @exporter.shutdown(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
152
152
  end
153
153
 
154
154
  private
@@ -187,6 +187,10 @@ module OpenTelemetry
187
187
  result_code = @export_mutex.synchronize { @exporter.export(batch, timeout: timeout) }
188
188
  report_result(result_code, batch)
189
189
  result_code
190
+ rescue StandardError => e
191
+ report_result(FAILURE, batch)
192
+ @metrics_reporter.add_to_counter('otel.bsp.error', labels: { 'reason' => e.class.to_s })
193
+ FAILURE
190
194
  end
191
195
 
192
196
  def report_result(result_code, batch)
@@ -194,7 +198,7 @@ module OpenTelemetry
194
198
  @metrics_reporter.add_to_counter('otel.bsp.export.success')
195
199
  @metrics_reporter.add_to_counter('otel.bsp.exported_spans', increment: batch.size)
196
200
  else
197
- OpenTelemetry.handle_error(message: "Unable to export #{batch.size} spans")
201
+ OpenTelemetry.handle_error(exception: ExportError.new("Unable to export #{batch.size} spans"))
198
202
  @metrics_reporter.add_to_counter('otel.bsp.export.failure')
199
203
  report_dropped_spans(batch.size, reason: 'export-failure')
200
204
  end
@@ -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,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'
@@ -10,13 +10,13 @@ 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
20
  EMPTY_ATTRIBUTES = {}.freeze
21
21
 
22
22
  private_constant :DEFAULT_STATUS, :EMPTY_ATTRIBUTES
@@ -159,7 +159,7 @@ module OpenTelemetry
159
159
  event_attributes = {
160
160
  'exception.type' => exception.class.to_s,
161
161
  'exception.message' => exception.message,
162
- '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: '�')
163
163
  }
164
164
  event_attributes.merge!(attributes) unless attributes.nil?
165
165
  add_event('exception', attributes: event_attributes)
@@ -167,20 +167,23 @@ module OpenTelemetry
167
167
 
168
168
  # Sets the Status to the Span
169
169
  #
170
- # 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.
171
171
  #
172
- # Only the value of the last call will be recorded, and implementations
173
- # 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.
174
175
  #
175
176
  # @param [Status] status The new status, which overrides the default Span
176
- # status, which is OK.
177
+ # status, which has code = Status::UNSET.
177
178
  #
178
179
  # @return [void]
179
180
  def status=(status)
181
+ return if status.code == OpenTelemetry::Trace::Status::UNSET
182
+
180
183
  @mutex.synchronize do
181
184
  if @ended
182
185
  OpenTelemetry.logger.warn('Calling status= on an ended Span.')
183
- else
186
+ elsif @status.code != OpenTelemetry::Trace::Status::OK
184
187
  @status = status
185
188
  end
186
189
  end
@@ -235,7 +238,7 @@ module OpenTelemetry
235
238
  @events.freeze
236
239
  @ended = true
237
240
  end
238
- @span_processor.on_finish(self)
241
+ @span_processors.each { |processor| processor.on_finish(self) }
239
242
  self
240
243
  end
241
244
 
@@ -273,14 +276,14 @@ module OpenTelemetry
273
276
  end
274
277
 
275
278
  # @api private
276
- 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
277
280
  super(span_context: context)
278
281
  @mutex = Mutex.new
279
282
  @name = name
280
283
  @kind = kind
281
284
  @parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID
282
- @trace_config = trace_config
283
- @span_processor = span_processor
285
+ @span_limits = span_limits
286
+ @span_processors = span_processors
284
287
  @resource = resource
285
288
  @instrumentation_library = instrumentation_library
286
289
  @ended = false
@@ -293,8 +296,8 @@ module OpenTelemetry
293
296
  @attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes.
294
297
  trim_span_attributes(@attributes)
295
298
  @events = nil
296
- @links = trim_links(links, trace_config.max_links_count, trace_config.max_attributes_per_link)
297
- @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) }
298
301
  end
299
302
 
300
303
  # TODO: Java implementation overrides finalize to log if a span isn't finished.
@@ -310,7 +313,7 @@ module OpenTelemetry
310
313
  def trim_span_attributes(attrs)
311
314
  return if attrs.nil?
312
315
 
313
- excess = attrs.size - @trace_config.max_attributes_count
316
+ excess = attrs.size - @span_limits.attribute_count_limit
314
317
  excess.times { attrs.shift } if excess.positive?
315
318
  truncate_attribute_values(attrs)
316
319
  nil
@@ -319,51 +322,54 @@ module OpenTelemetry
319
322
  def truncate_attribute_values(attrs)
320
323
  return EMPTY_ATTRIBUTES if attrs.nil?
321
324
 
322
- max_attributes_length = @trace_config.max_attributes_length
323
- attrs.each { |key, value| attrs[key] = OpenTelemetry::Common::Utilities.truncate(value, max_attributes_length) } if max_attributes_length
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
324
327
  attrs
325
328
  end
326
329
 
327
- def trim_links(links, max_links_count, max_attributes_per_link) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
330
+ def trim_links(links, link_count_limit, link_attribute_count_limit) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
328
331
  # Fast path (likely) common cases.
329
332
  return nil if links.nil?
330
333
 
331
- if links.size <= max_links_count &&
332
- links.all? { |link| link.attributes.size <= max_attributes_per_link && Internal.valid_attributes?(name, 'link', 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) }
333
336
  return links.frozen? ? links : links.clone.freeze
334
337
  end
335
338
 
336
339
  # Slow path: trim attributes for each Link.
337
- 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|
338
344
  attrs = Hash[link.attributes] # link.attributes is frozen, so we need an unfrozen copy to adjust.
339
345
  attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
340
- excess = attrs.size - max_attributes_per_link
346
+ excess = attrs.size - link_attribute_count_limit
341
347
  excess.times { attrs.shift } if excess.positive?
342
348
  OpenTelemetry::Trace::Link.new(link.span_context, attrs)
343
349
  end.freeze
344
350
  end
345
351
 
346
352
  def append_event(events, event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
347
- max_events_count = @trace_config.max_events_count
348
- max_attributes_per_event = @trace_config.max_attributes_per_event
353
+ event_count_limit = @span_limits.event_count_limit
354
+ event_attribute_count_limit = @span_limits.event_attribute_count_limit
349
355
  valid_attributes = Internal.valid_attributes?(name, 'event', event.attributes)
350
356
 
351
357
  # Fast path (likely) common case.
352
- if events.size < max_events_count &&
353
- event.attributes.size <= max_attributes_per_event &&
358
+ if events.size < event_count_limit &&
359
+ event.attributes.size <= event_attribute_count_limit &&
354
360
  valid_attributes
355
361
  return events << event
356
362
  end
357
363
 
358
364
  # Slow path.
359
- excess = events.size + 1 - max_events_count
365
+ excess = events.size + 1 - event_count_limit
360
366
  events.shift(excess) if excess.positive?
361
367
 
362
- excess = event.attributes.size - max_attributes_per_event
368
+ excess = event.attributes.size - event_attribute_count_limit
363
369
  if excess.positive? || !valid_attributes
364
370
  attrs = Hash[event.attributes] # event.attributes is frozen, so we need an unfrozen copy to adjust.
365
371
  attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
366
- excess = attrs.size - max_attributes_per_event
372
+ excess = attrs.size - event_attribute_count_limit
367
373
  excess.times { attrs.shift } if excess.positive?
368
374
  event = Event.new(event.name, attrs.freeze, event.timestamp)
369
375
  end