opentelemetry-sdk 0.14.0 → 1.0.0.rc2
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 +4 -4
- data/CHANGELOG.md +110 -44
- data/README.md +2 -2
- data/lib/opentelemetry/sdk.rb +4 -3
- data/lib/opentelemetry/sdk/configurator.rb +52 -33
- data/lib/opentelemetry/sdk/forwarding_logger.rb +69 -0
- data/lib/opentelemetry/sdk/internal.rb +3 -3
- data/lib/opentelemetry/sdk/resources/constants.rb +48 -3
- data/lib/opentelemetry/sdk/resources/resource.rb +8 -1
- data/lib/opentelemetry/sdk/trace.rb +2 -3
- data/lib/opentelemetry/sdk/trace/event.rb +7 -36
- data/lib/opentelemetry/sdk/trace/export.rb +1 -2
- data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +10 -5
- data/lib/opentelemetry/sdk/trace/export/console_span_exporter.rb +4 -0
- data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +23 -4
- data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +4 -2
- data/lib/opentelemetry/sdk/trace/export/{noop_span_exporter.rb → span_exporter.rb} +18 -7
- data/lib/opentelemetry/sdk/trace/span.rb +83 -39
- data/lib/opentelemetry/sdk/trace/span_data.rb +25 -18
- data/lib/opentelemetry/sdk/trace/span_limits.rb +60 -0
- data/lib/opentelemetry/sdk/trace/{noop_span_processor.rb → span_processor.rb} +5 -8
- data/lib/opentelemetry/sdk/trace/tracer.rb +1 -37
- data/lib/opentelemetry/sdk/trace/tracer_provider.rb +106 -19
- data/lib/opentelemetry/sdk/version.rb +1 -1
- metadata +43 -20
- data/lib/opentelemetry/sdk/baggage.rb +0 -16
- data/lib/opentelemetry/sdk/baggage/builder.rb +0 -40
- data/lib/opentelemetry/sdk/baggage/manager.rb +0 -97
- data/lib/opentelemetry/sdk/trace/config.rb +0 -18
- data/lib/opentelemetry/sdk/trace/config/trace_config.rb +0 -79
- data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +0 -59
- data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +0 -86
@@ -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 #{
|
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
|
@@ -67,14 +67,59 @@ module OpenTelemetry
|
|
67
67
|
# The name of the cluster that the pod is running in.
|
68
68
|
cluster_name: 'k8s.cluster.name',
|
69
69
|
|
70
|
+
# The name of the Node.
|
71
|
+
node_name: 'k8s.node.name',
|
72
|
+
|
73
|
+
# The UID of the Node.
|
74
|
+
node_uid: 'k8s.node.uid',
|
75
|
+
|
70
76
|
# The name of the namespace that the pod is running in.
|
71
77
|
namespace_name: 'k8s.namespace.name',
|
72
78
|
|
73
79
|
# The name of the pod.
|
74
80
|
pod_name: 'k8s.pod.name',
|
75
81
|
|
82
|
+
# The UID of the Pod.
|
83
|
+
pod_uid: 'k8s.pod.uid',
|
84
|
+
|
85
|
+
# The name of the Container in a Pod template.
|
86
|
+
container_name: 'k8s.container.name',
|
87
|
+
|
88
|
+
# The UID of the ReplicaSet.
|
89
|
+
replicaset_uid: 'k8s.replicaset.uid',
|
90
|
+
|
91
|
+
# The name of the ReplicaSet.
|
92
|
+
replicaset_name: 'k8s.replicaset.name',
|
93
|
+
|
94
|
+
# The UID of the Deployment.
|
95
|
+
deployment_uid: 'k8s.deployment.uid',
|
96
|
+
|
76
97
|
# The name of the deployment.
|
77
|
-
deployment_name: 'k8s.deployment.name'
|
98
|
+
deployment_name: 'k8s.deployment.name',
|
99
|
+
|
100
|
+
# The UID of the StatefulSet.
|
101
|
+
statefulset_uid: 'k8s.statefulset.uid',
|
102
|
+
|
103
|
+
# The name of the StatefulSet.
|
104
|
+
statefulset_name: 'k8s.statefulset.name',
|
105
|
+
|
106
|
+
# The UID of the DaemonSet.
|
107
|
+
daemonset_uid: 'k8s.daemonset.uid',
|
108
|
+
|
109
|
+
# The name of the DaemonSet.
|
110
|
+
daemonset_name: 'k8s.daemonset.name',
|
111
|
+
|
112
|
+
# The UID of the Job.
|
113
|
+
job_uid: 'k8s.job.uid',
|
114
|
+
|
115
|
+
# The name of the Job.
|
116
|
+
job_name: 'k8s.job.name',
|
117
|
+
|
118
|
+
# The UID of the CronJob.
|
119
|
+
cronjob_uid: 'k8s.cronjob.uid',
|
120
|
+
|
121
|
+
# The name of the CronJob.
|
122
|
+
cronjob_name: 'k8s.cronjob.name'
|
78
123
|
}.freeze
|
79
124
|
|
80
125
|
# Attributes defining an operating system process.
|
@@ -151,8 +196,8 @@ module OpenTelemetry
|
|
151
196
|
# A specific geographical location where different entities can run.
|
152
197
|
region: 'cloud.region',
|
153
198
|
|
154
|
-
#
|
155
|
-
|
199
|
+
# Availability zones are a sub set of the region connected through low-latency links.
|
200
|
+
availability_zone: 'cloud.availability_zone'
|
156
201
|
}.freeze
|
157
202
|
end
|
158
203
|
end
|
@@ -31,7 +31,7 @@ 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(Constants::SERVICE_RESOURCE[:name] => 'unknown_service').merge(process).merge(telemetry_sdk).merge(service_name_from_env)
|
35
35
|
end
|
36
36
|
|
37
37
|
def telemetry_sdk
|
@@ -64,6 +64,13 @@ module OpenTelemetry
|
|
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(Constants::SERVICE_RESOURCE[: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/
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
@@ -31,6 +31,5 @@ require 'opentelemetry/sdk/trace/export/batch_span_processor'
|
|
31
31
|
require 'opentelemetry/sdk/trace/export/console_span_exporter'
|
32
32
|
require 'opentelemetry/sdk/trace/export/in_memory_span_exporter'
|
33
33
|
require 'opentelemetry/sdk/trace/export/metrics_reporter'
|
34
|
-
require 'opentelemetry/sdk/trace/export/
|
35
|
-
require 'opentelemetry/sdk/trace/export/noop_span_exporter'
|
34
|
+
require 'opentelemetry/sdk/trace/export/span_exporter'
|
36
35
|
require 'opentelemetry/sdk/trace/export/simple_span_processor'
|
@@ -28,7 +28,8 @@ module OpenTelemetry
|
|
28
28
|
class BatchSpanProcessor # rubocop:disable Metrics/ClassLength
|
29
29
|
# Returns a new instance of the {BatchSpanProcessor}.
|
30
30
|
#
|
31
|
-
# @param [SpanExporter] exporter
|
31
|
+
# @param [SpanExporter] exporter the (duck type) SpanExporter to where the
|
32
|
+
# recorded Spans are pushed after batching.
|
32
33
|
# @param [Numeric] exporter_timeout the delay interval between two
|
33
34
|
# consecutive exports. Defaults to the value of the OTEL_BSP_EXPORT_TIMEOUT
|
34
35
|
# environment variable, if set, or 30,000 (30 seconds).
|
@@ -43,7 +44,7 @@ module OpenTelemetry
|
|
43
44
|
# variable, if set, or 512.
|
44
45
|
#
|
45
46
|
# @return a new instance of the {BatchSpanProcessor}.
|
46
|
-
def initialize(exporter
|
47
|
+
def initialize(exporter,
|
47
48
|
exporter_timeout: Float(ENV.fetch('OTEL_BSP_EXPORT_TIMEOUT', 30_000)),
|
48
49
|
schedule_delay: Float(ENV.fetch('OTEL_BSP_SCHEDULE_DELAY', 5_000)),
|
49
50
|
max_queue_size: Integer(ENV.fetch('OTEL_BSP_MAX_QUEUE_SIZE', 2048)),
|
@@ -51,6 +52,7 @@ module OpenTelemetry
|
|
51
52
|
start_thread_on_boot: String(ENV['OTEL_RUBY_BSP_START_THREAD_ON_BOOT']) !~ /false/i,
|
52
53
|
metrics_reporter: nil)
|
53
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)
|
54
56
|
|
55
57
|
@exporter = exporter
|
56
58
|
@exporter_timeout_seconds = exporter_timeout / 1000.0
|
@@ -99,7 +101,7 @@ module OpenTelemetry
|
|
99
101
|
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
100
102
|
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
101
103
|
def force_flush(timeout: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
102
|
-
start_time =
|
104
|
+
start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
|
103
105
|
snapshot = lock do
|
104
106
|
reset_on_fork if @keep_running
|
105
107
|
spans.shift(spans.size)
|
@@ -113,7 +115,7 @@ module OpenTelemetry
|
|
113
115
|
return result_code unless result_code == SUCCESS
|
114
116
|
end
|
115
117
|
|
116
|
-
|
118
|
+
@exporter.force_flush(timeout: OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time))
|
117
119
|
ensure
|
118
120
|
# Unshift the remaining spans if we timed out. We drop excess spans from
|
119
121
|
# the snapshot because they're older than any spans in the spans buffer.
|
@@ -135,7 +137,7 @@ module OpenTelemetry
|
|
135
137
|
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
136
138
|
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
137
139
|
def shutdown(timeout: nil)
|
138
|
-
start_time =
|
140
|
+
start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
|
139
141
|
thread = lock do
|
140
142
|
@keep_running = false
|
141
143
|
@condition.signal
|
@@ -176,6 +178,9 @@ module OpenTelemetry
|
|
176
178
|
@pid = pid
|
177
179
|
spans.clear
|
178
180
|
@thread = restart_thread ? Thread.new { work } : nil
|
181
|
+
rescue ThreadError => e
|
182
|
+
@metrics_reporter.add_to_counter('otel.bsp.error', labels: { 'reason' => 'ThreadError' })
|
183
|
+
OpenTelemetry.handle_error(exception: e, message: 'unexpected error in BatchSpanProcessor#reset_on_fork')
|
179
184
|
end
|
180
185
|
|
181
186
|
def export_batch(batch, timeout: @exporter_timeout_seconds)
|
@@ -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
|
-
#
|
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,11 +76,21 @@ 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
|
74
83
|
|
84
|
+
# Called when {TracerProvider#force_flush} is called, if this exporter is
|
85
|
+
# registered to a {TracerProvider} object.
|
86
|
+
#
|
87
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
88
|
+
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
89
|
+
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
90
|
+
def force_flush(timeout: nil)
|
91
|
+
SUCCESS
|
92
|
+
end
|
93
|
+
|
75
94
|
# Called when {TracerProvider#shutdown} is called, if this exporter is
|
76
95
|
# registered to a {TracerProvider} object.
|
77
96
|
#
|
@@ -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
|
|
@@ -61,7 +63,7 @@ module OpenTelemetry
|
|
61
63
|
end
|
62
64
|
|
63
65
|
# Export all ended spans to the configured `Exporter` that have not yet
|
64
|
-
# been exported.
|
66
|
+
# been exported, then call {Exporter#force_flush}.
|
65
67
|
#
|
66
68
|
# This method should only be called in cases where it is absolutely
|
67
69
|
# necessary, such as when using some FaaS providers that may suspend
|
@@ -72,7 +74,7 @@ module OpenTelemetry
|
|
72
74
|
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
73
75
|
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
74
76
|
def force_flush(timeout: nil)
|
75
|
-
SUCCESS
|
77
|
+
@span_exporter&.force_flush(timeout: timeout) || SUCCESS
|
76
78
|
end
|
77
79
|
|
78
80
|
# Called when {TracerProvider#shutdown} is called.
|
@@ -8,29 +8,40 @@ module OpenTelemetry
|
|
8
8
|
module SDK
|
9
9
|
module Trace
|
10
10
|
module Export
|
11
|
-
#
|
12
|
-
#
|
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 {
|
17
|
-
class
|
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 {
|
23
|
+
# Called to export sampled {SpanData}s.
|
23
24
|
#
|
24
|
-
# @param [Enumerable<
|
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(
|
29
|
+
def export(span_data, timeout: nil)
|
29
30
|
return SUCCESS unless @stopped
|
30
31
|
|
31
32
|
FAILURE
|
32
33
|
end
|
33
34
|
|
35
|
+
# Called when {TracerProvider#force_flush} is called, if this exporter is
|
36
|
+
# registered to a {TracerProvider} object.
|
37
|
+
#
|
38
|
+
# @param [optional Numeric] timeout An optional timeout in seconds.
|
39
|
+
# @return [Integer] SUCCESS if no error occurred, FAILURE if a
|
40
|
+
# non-specific failure occurred, TIMEOUT if a timeout occurred.
|
41
|
+
def force_flush(timeout: nil)
|
42
|
+
SUCCESS
|
43
|
+
end
|
44
|
+
|
34
45
|
# Called when {TracerProvider#shutdown} is called, if this exporter is
|
35
46
|
# registered to a {TracerProvider} object.
|
36
47
|
#
|
@@ -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
|
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.
|
19
|
+
DEFAULT_STATUS = OpenTelemetry::Trace::Status.unset
|
20
|
+
EMPTY_ATTRIBUTES = {}.freeze
|
20
21
|
|
21
|
-
private_constant
|
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.
|
@@ -64,10 +65,12 @@ module OpenTelemetry
|
|
64
65
|
#
|
65
66
|
# @param [String] key
|
66
67
|
# @param [String, Boolean, Numeric, Array<String, Numeric, Boolean>] value
|
68
|
+
# Values must be non-nil and (array of) string, boolean or numeric type.
|
69
|
+
# Array values must not contain nil elements and all elements must be of
|
70
|
+
# the same basic type (string, numeric, boolean).
|
67
71
|
#
|
68
72
|
# @return [self] returns itself
|
69
73
|
def set_attribute(key, value)
|
70
|
-
super
|
71
74
|
@mutex.synchronize do
|
72
75
|
if @ended
|
73
76
|
OpenTelemetry.logger.warn('Calling set_attribute on an ended Span.')
|
@@ -82,6 +85,33 @@ module OpenTelemetry
|
|
82
85
|
end
|
83
86
|
alias []= set_attribute
|
84
87
|
|
88
|
+
# Add attributes
|
89
|
+
#
|
90
|
+
# Note that the OpenTelemetry project
|
91
|
+
# {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
|
92
|
+
# documents} certain "standard attributes" that have prescribed semantic
|
93
|
+
# meanings.
|
94
|
+
#
|
95
|
+
# @param [Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
|
96
|
+
# Values must be non-nil and (array of) string, boolean or numeric type.
|
97
|
+
# Array values must not contain nil elements and all elements must be of
|
98
|
+
# the same basic type (string, numeric, boolean).
|
99
|
+
#
|
100
|
+
# @return [self] returns itself
|
101
|
+
def add_attributes(attributes)
|
102
|
+
@mutex.synchronize do
|
103
|
+
if @ended
|
104
|
+
OpenTelemetry.logger.warn('Calling add_attributes on an ended Span.')
|
105
|
+
else
|
106
|
+
@attributes ||= {}
|
107
|
+
@attributes.merge!(attributes)
|
108
|
+
trim_span_attributes(@attributes)
|
109
|
+
@total_recorded_attributes += attributes.size
|
110
|
+
end
|
111
|
+
end
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
85
115
|
# Add an Event to a {Span}.
|
86
116
|
#
|
87
117
|
# Example:
|
@@ -96,13 +126,12 @@ module OpenTelemetry
|
|
96
126
|
# @param [String] name Name of the event.
|
97
127
|
# @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}] attributes
|
98
128
|
# One or more key:value pairs, where the keys must be strings and the
|
99
|
-
# values may be string, boolean or numeric type.
|
129
|
+
# values may be (array of) string, boolean or numeric type.
|
100
130
|
# @param [optional Time] timestamp Optional timestamp for the event.
|
101
131
|
#
|
102
132
|
# @return [self] returns itself
|
103
133
|
def add_event(name, attributes: nil, timestamp: nil)
|
104
|
-
|
105
|
-
event = Event.new(name: name, attributes: attributes, timestamp: timestamp || Time.now)
|
134
|
+
event = Event.new(name, truncate_attribute_values(attributes), wall_clock(timestamp))
|
106
135
|
|
107
136
|
@mutex.synchronize do
|
108
137
|
if @ended
|
@@ -130,7 +159,7 @@ module OpenTelemetry
|
|
130
159
|
event_attributes = {
|
131
160
|
'exception.type' => exception.class.to_s,
|
132
161
|
'exception.message' => exception.message,
|
133
|
-
'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: '�')
|
134
163
|
}
|
135
164
|
event_attributes.merge!(attributes) unless attributes.nil?
|
136
165
|
add_event('exception', attributes: event_attributes)
|
@@ -138,21 +167,23 @@ module OpenTelemetry
|
|
138
167
|
|
139
168
|
# Sets the Status to the Span
|
140
169
|
#
|
141
|
-
# If used, this will override the default Span status. Default
|
170
|
+
# If used, this will override the default Span status. Default has code = Status::UNSET.
|
142
171
|
#
|
143
|
-
#
|
144
|
-
#
|
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.
|
145
175
|
#
|
146
176
|
# @param [Status] status The new status, which overrides the default Span
|
147
|
-
# status, which
|
177
|
+
# status, which has code = Status::UNSET.
|
148
178
|
#
|
149
179
|
# @return [void]
|
150
180
|
def status=(status)
|
151
|
-
|
181
|
+
return if status.code == OpenTelemetry::Trace::Status::UNSET
|
182
|
+
|
152
183
|
@mutex.synchronize do
|
153
184
|
if @ended
|
154
185
|
OpenTelemetry.logger.warn('Calling status= on an ended Span.')
|
155
|
-
|
186
|
+
elsif @status.code != OpenTelemetry::Trace::Status::OK
|
156
187
|
@status = status
|
157
188
|
end
|
158
189
|
end
|
@@ -168,7 +199,6 @@ module OpenTelemetry
|
|
168
199
|
#
|
169
200
|
# @return [void]
|
170
201
|
def name=(new_name)
|
171
|
-
super
|
172
202
|
@mutex.synchronize do
|
173
203
|
if @ended
|
174
204
|
OpenTelemetry.logger.warn('Calling name= on an ended Span.')
|
@@ -203,12 +233,12 @@ module OpenTelemetry
|
|
203
233
|
OpenTelemetry.logger.warn('Calling finish on an ended Span.')
|
204
234
|
return self
|
205
235
|
end
|
206
|
-
@end_timestamp = end_timestamp
|
236
|
+
@end_timestamp = wall_clock(end_timestamp)
|
207
237
|
@attributes = validated_attributes(@attributes).freeze
|
208
238
|
@events.freeze
|
209
239
|
@ended = true
|
210
240
|
end
|
211
|
-
@
|
241
|
+
@span_processors.each { |processor| processor.on_finish(self) }
|
212
242
|
self
|
213
243
|
end
|
214
244
|
|
@@ -246,14 +276,14 @@ module OpenTelemetry
|
|
246
276
|
end
|
247
277
|
|
248
278
|
# @api private
|
249
|
-
def initialize(context, parent_context, name, kind, parent_span_id,
|
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
|
250
280
|
super(span_context: context)
|
251
281
|
@mutex = Mutex.new
|
252
282
|
@name = name
|
253
283
|
@kind = kind
|
254
284
|
@parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID
|
255
|
-
@
|
256
|
-
@
|
285
|
+
@span_limits = span_limits
|
286
|
+
@span_processors = span_processors
|
257
287
|
@resource = resource
|
258
288
|
@instrumentation_library = instrumentation_library
|
259
289
|
@ended = false
|
@@ -261,13 +291,13 @@ module OpenTelemetry
|
|
261
291
|
@total_recorded_events = 0
|
262
292
|
@total_recorded_links = links&.size || 0
|
263
293
|
@total_recorded_attributes = attributes&.size || 0
|
264
|
-
@start_timestamp = start_timestamp
|
294
|
+
@start_timestamp = wall_clock(start_timestamp)
|
265
295
|
@end_timestamp = nil
|
266
296
|
@attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes.
|
267
297
|
trim_span_attributes(@attributes)
|
268
298
|
@events = nil
|
269
|
-
@links = trim_links(links,
|
270
|
-
@
|
299
|
+
@links = trim_links(links, span_limits.link_count_limit, span_limits.attribute_per_link_count_limit)
|
300
|
+
@span_processors.each { |processor| processor.on_start(self, parent_context) }
|
271
301
|
end
|
272
302
|
|
273
303
|
# TODO: Java implementation overrides finalize to log if a span isn't finished.
|
@@ -275,7 +305,7 @@ module OpenTelemetry
|
|
275
305
|
private
|
276
306
|
|
277
307
|
def validated_attributes(attrs)
|
278
|
-
return attrs if Internal.valid_attributes?(attrs)
|
308
|
+
return attrs if Internal.valid_attributes?(name, 'span', attrs)
|
279
309
|
|
280
310
|
attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
|
281
311
|
end
|
@@ -283,56 +313,70 @@ module OpenTelemetry
|
|
283
313
|
def trim_span_attributes(attrs)
|
284
314
|
return if attrs.nil?
|
285
315
|
|
286
|
-
excess = attrs.size - @
|
316
|
+
excess = attrs.size - @span_limits.attribute_count_limit
|
287
317
|
excess.times { attrs.shift } if excess.positive?
|
318
|
+
truncate_attribute_values(attrs)
|
288
319
|
nil
|
289
320
|
end
|
290
321
|
|
291
|
-
def
|
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, attribute_per_link_count_limit) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
292
331
|
# Fast path (likely) common cases.
|
293
332
|
return nil if links.nil?
|
294
333
|
|
295
|
-
if links.size <=
|
296
|
-
links.all? { |link| link.attributes.size <=
|
334
|
+
if links.size <= link_count_limit &&
|
335
|
+
links.all? { |link| link.attributes.size <= attribute_per_link_count_limit && Internal.valid_attributes?(name, 'link', link.attributes) }
|
297
336
|
return links.frozen? ? links : links.clone.freeze
|
298
337
|
end
|
299
338
|
|
300
339
|
# Slow path: trim attributes for each Link.
|
301
|
-
links.last(
|
340
|
+
links.last(link_count_limit).map! do |link|
|
302
341
|
attrs = Hash[link.attributes] # link.attributes is frozen, so we need an unfrozen copy to adjust.
|
303
342
|
attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
|
304
|
-
excess = attrs.size -
|
343
|
+
excess = attrs.size - attribute_per_link_count_limit
|
305
344
|
excess.times { attrs.shift } if excess.positive?
|
306
345
|
OpenTelemetry::Trace::Link.new(link.span_context, attrs)
|
307
346
|
end.freeze
|
308
347
|
end
|
309
348
|
|
310
349
|
def append_event(events, event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
311
|
-
|
312
|
-
|
313
|
-
valid_attributes = Internal.valid_attributes?(event.attributes)
|
350
|
+
event_count_limit = @span_limits.event_count_limit
|
351
|
+
attribute_per_event_count_limit = @span_limits.attribute_per_event_count_limit
|
352
|
+
valid_attributes = Internal.valid_attributes?(name, 'event', event.attributes)
|
314
353
|
|
315
354
|
# Fast path (likely) common case.
|
316
|
-
if events.size <
|
317
|
-
event.attributes.size <=
|
355
|
+
if events.size < event_count_limit &&
|
356
|
+
event.attributes.size <= attribute_per_event_count_limit &&
|
318
357
|
valid_attributes
|
319
358
|
return events << event
|
320
359
|
end
|
321
360
|
|
322
361
|
# Slow path.
|
323
|
-
excess = events.size + 1 -
|
362
|
+
excess = events.size + 1 - event_count_limit
|
324
363
|
events.shift(excess) if excess.positive?
|
325
364
|
|
326
|
-
excess = event.attributes.size -
|
365
|
+
excess = event.attributes.size - attribute_per_event_count_limit
|
327
366
|
if excess.positive? || !valid_attributes
|
328
367
|
attrs = Hash[event.attributes] # event.attributes is frozen, so we need an unfrozen copy to adjust.
|
329
368
|
attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
|
330
|
-
excess = attrs.size -
|
369
|
+
excess = attrs.size - attribute_per_event_count_limit
|
331
370
|
excess.times { attrs.shift } if excess.positive?
|
332
|
-
event = Event.new(
|
371
|
+
event = Event.new(event.name, attrs.freeze, event.timestamp)
|
333
372
|
end
|
334
373
|
events << event
|
335
374
|
end
|
375
|
+
|
376
|
+
def wall_clock(timestamp)
|
377
|
+
timestamp = (timestamp.to_r * 1_000_000_000).to_i unless timestamp.nil?
|
378
|
+
timestamp || Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
|
379
|
+
end
|
336
380
|
end
|
337
381
|
# rubocop:enable Metrics/ClassLength
|
338
382
|
end
|