opentelemetry-sdk 1.0.0.rc1 → 1.0.1

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