opentelemetry-sdk 0.2.0
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 +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +201 -0
- data/lib/opentelemetry/sdk.rb +18 -0
- data/lib/opentelemetry/sdk/internal.rb +32 -0
- data/lib/opentelemetry/sdk/resources.rb +15 -0
- data/lib/opentelemetry/sdk/resources/resource.rb +75 -0
- data/lib/opentelemetry/sdk/trace.rb +24 -0
- data/lib/opentelemetry/sdk/trace/config.rb +18 -0
- data/lib/opentelemetry/sdk/trace/config/trace_config.rb +77 -0
- data/lib/opentelemetry/sdk/trace/export.rb +35 -0
- data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +149 -0
- data/lib/opentelemetry/sdk/trace/export/console_span_exporter.rb +40 -0
- data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +86 -0
- data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +64 -0
- data/lib/opentelemetry/sdk/trace/export/noop_span_exporter.rb +42 -0
- data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +63 -0
- data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +51 -0
- data/lib/opentelemetry/sdk/trace/noop_span_processor.rb +41 -0
- data/lib/opentelemetry/sdk/trace/samplers.rb +106 -0
- data/lib/opentelemetry/sdk/trace/samplers/decision.rb +26 -0
- data/lib/opentelemetry/sdk/trace/samplers/probability_sampler.rb +74 -0
- data/lib/opentelemetry/sdk/trace/samplers/result.rb +54 -0
- data/lib/opentelemetry/sdk/trace/span.rb +317 -0
- data/lib/opentelemetry/sdk/trace/span_data.rb +32 -0
- data/lib/opentelemetry/sdk/trace/tracer.rb +67 -0
- data/lib/opentelemetry/sdk/trace/tracer_factory.rb +84 -0
- data/lib/opentelemetry/sdk/version.rb +12 -0
- metadata +196 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module SDK
|
9
|
+
module Trace
|
10
|
+
# Implementation of the SpanProcessor duck type that simply forwards all
|
11
|
+
# received events to a list of SpanProcessors.
|
12
|
+
class MultiSpanProcessor
|
13
|
+
# Creates a new {MultiSpanProcessor}.
|
14
|
+
#
|
15
|
+
# @param [Enumerable<SpanProcessor>] span_processors a collection of
|
16
|
+
# SpanProcessors.
|
17
|
+
# @return [MultiSpanProcessor]
|
18
|
+
def initialize(span_processors)
|
19
|
+
@span_processors = span_processors.to_a.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called when a {Span} is started, if the {Span#recording?}
|
23
|
+
# returns true.
|
24
|
+
#
|
25
|
+
# This method is called synchronously on the execution thread, should
|
26
|
+
# not throw or block the execution thread.
|
27
|
+
#
|
28
|
+
# @param [Span] span the {Span} that just started.
|
29
|
+
def on_start(span)
|
30
|
+
@span_processors.each { |processor| processor.on_start(span) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Called when a {Span} is ended, if the {Span#recording?}
|
34
|
+
# returns true.
|
35
|
+
#
|
36
|
+
# This method is called synchronously on the execution thread, should
|
37
|
+
# not throw or block the execution thread.
|
38
|
+
#
|
39
|
+
# @param [Span] span the {Span} that just ended.
|
40
|
+
def on_finish(span)
|
41
|
+
@span_processors.each { |processor| processor.on_finish(span) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Called when {TracerFactory#shutdown} is called.
|
45
|
+
def shutdown
|
46
|
+
@span_processors.each(&:shutdown)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'singleton'
|
8
|
+
|
9
|
+
module OpenTelemetry
|
10
|
+
module SDK
|
11
|
+
module Trace
|
12
|
+
# NoopSpanProcessor is a singleton implementation of the duck type
|
13
|
+
# SpanProcessor that provides synchronous no-op hooks for when a
|
14
|
+
# {Span} is started or when a {Span} is ended.
|
15
|
+
class NoopSpanProcessor
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
# Called when a {Span} is started, if the {Span#recording?}
|
19
|
+
# returns true.
|
20
|
+
#
|
21
|
+
# This method is called synchronously on the execution thread, should
|
22
|
+
# not throw or block the execution thread.
|
23
|
+
#
|
24
|
+
# @param [Span] span the {Span} that just started.
|
25
|
+
def on_start(span); end
|
26
|
+
|
27
|
+
# Called when a {Span} is ended, if the {Span#recording?}
|
28
|
+
# returns true.
|
29
|
+
#
|
30
|
+
# This method is called synchronously on the execution thread, should
|
31
|
+
# not throw or block the execution thread.
|
32
|
+
#
|
33
|
+
# @param [Span] span the {Span} that just ended.
|
34
|
+
def on_finish(span); end
|
35
|
+
|
36
|
+
# Called when {TracerFactory#shutdown} is called.
|
37
|
+
def shutdown; end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'opentelemetry/sdk/trace/samplers/decision'
|
8
|
+
require 'opentelemetry/sdk/trace/samplers/result'
|
9
|
+
require 'opentelemetry/sdk/trace/samplers/probability_sampler'
|
10
|
+
|
11
|
+
module OpenTelemetry
|
12
|
+
module SDK
|
13
|
+
module Trace
|
14
|
+
# The Samplers module contains the sampling logic for OpenTelemetry. The
|
15
|
+
# reference implementation provides a {ProbabilitySampler}, {ALWAYS_ON},
|
16
|
+
# {ALWAYS_OFF}, and {ALWAYS_PARENT}.
|
17
|
+
#
|
18
|
+
# Custom samplers can be provided by SDK users. The required interface is
|
19
|
+
# a callable with the signature:
|
20
|
+
#
|
21
|
+
# (trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:) -> Result
|
22
|
+
#
|
23
|
+
# Where:
|
24
|
+
#
|
25
|
+
# @param [String] trace_id The trace_id of the {Span} to be created.
|
26
|
+
# @param [String] span_id The span_id of the {Span} to be created.
|
27
|
+
# @param [OpenTelemetry::Trace::SpanContext] parent_context The
|
28
|
+
# {OpenTelemetry::Trace::SpanContext} of a parent span, typically
|
29
|
+
# extracted from the wire. Can be nil for a root span.
|
30
|
+
# @param [Symbol] hint A {OpenTelemetry::Trace::SamplingHint} about
|
31
|
+
# whether the {Span} should be sampled and/or record events.
|
32
|
+
# @param [Enumerable<Link>] links A collection of links to be associated
|
33
|
+
# with the {Span} to be created. Can be nil.
|
34
|
+
# @param [String] name Name of the {Span} to be created.
|
35
|
+
# @param [Symbol] kind The {OpenTelemetry::Trace::SpanKind} of the {Span}
|
36
|
+
# to be created. Can be nil.
|
37
|
+
# @param [Hash<String, Object>] attributes Attributes to be attached
|
38
|
+
# to the {Span} to be created. Can be nil.
|
39
|
+
# @return [Result] The sampling result.
|
40
|
+
module Samplers
|
41
|
+
RECORD_AND_SAMPLED = Result.new(decision: Decision::RECORD_AND_SAMPLED)
|
42
|
+
NOT_RECORD = Result.new(decision: Decision::NOT_RECORD)
|
43
|
+
RECORD = Result.new(decision: Decision::RECORD)
|
44
|
+
SAMPLING_HINTS = [Decision::NOT_RECORD, Decision::RECORD, Decision::RECORD_AND_SAMPLED].freeze
|
45
|
+
APPLY_PROBABILITY_TO_SYMBOLS = %i[root_spans root_spans_and_remote_parent all_spans].freeze
|
46
|
+
|
47
|
+
private_constant(:RECORD_AND_SAMPLED, :NOT_RECORD, :RECORD, :SAMPLING_HINTS, :APPLY_PROBABILITY_TO_SYMBOLS)
|
48
|
+
|
49
|
+
# rubocop:disable Lint/UnusedBlockArgument
|
50
|
+
|
51
|
+
# Ignores all values in hint and returns a {Result} with
|
52
|
+
# {Decision::RECORD_AND_SAMPLED}.
|
53
|
+
ALWAYS_ON = ->(trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:) { RECORD_AND_SAMPLED }
|
54
|
+
|
55
|
+
# Ignores all values in hint and returns a {Result} with
|
56
|
+
# {Decision::NOT_RECORD}.
|
57
|
+
ALWAYS_OFF = ->(trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:) { NOT_RECORD }
|
58
|
+
|
59
|
+
# Ignores all values in hint and returns a {Result} with
|
60
|
+
# {Decision::RECORD_AND_SAMPLED} if the parent context is sampled or
|
61
|
+
# {Decision::NOT_RECORD} otherwise, or if there is no parent context.
|
62
|
+
# rubocop:disable Style/Lambda
|
63
|
+
ALWAYS_PARENT = ->(trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:) do
|
64
|
+
if parent_context&.trace_flags&.sampled?
|
65
|
+
RECORD_AND_SAMPLED
|
66
|
+
else
|
67
|
+
NOT_RECORD
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# rubocop:enable Style/Lambda
|
71
|
+
# rubocop:enable Lint/UnusedBlockArgument
|
72
|
+
|
73
|
+
# Returns a new sampler. The probability of sampling a trace is equal
|
74
|
+
# to that of the specified probability.
|
75
|
+
#
|
76
|
+
# @param [Numeric] probability The desired probability of sampling.
|
77
|
+
# Must be within [0.0, 1.0].
|
78
|
+
# @param [optional Enumerable<Symbol>] ignore_hints Sampling hints to
|
79
|
+
# ignore. Defaults to ignore {OpenTelemetry::Trace::SamplingHint::RECORD}.
|
80
|
+
# @param [optional Boolean] ignore_parent Whether to ignore parent
|
81
|
+
# sampling. Defaults to not ignore parent sampling.
|
82
|
+
# @param [optional Symbol] apply_probability_to Whether to apply
|
83
|
+
# probability sampling to root spans, root spans and remote parents,
|
84
|
+
# or all spans. Allowed values include :root_spans, :root_spans_and_remote_parent,
|
85
|
+
# and :all_spans. Defaults to :root_spans_and_remote_parent.
|
86
|
+
# @raise [ArgumentError] if probability is out of range
|
87
|
+
# @raise [ArgumentError] if ignore_hints contains invalid hints
|
88
|
+
# @raise [ArgumentError] if apply_probability_to is not one of the allowed symbols
|
89
|
+
def self.probability(probability,
|
90
|
+
ignore_hints: [OpenTelemetry::Trace::SamplingHint::RECORD],
|
91
|
+
ignore_parent: false,
|
92
|
+
apply_probability_to: :root_spans_and_remote_parent)
|
93
|
+
raise ArgumentError, 'probability must be in range [0.0, 1.0]' unless (0.0..1.0).include?(probability)
|
94
|
+
raise ArgumentError, 'ignore_hints' unless (ignore_hints.to_a - SAMPLING_HINTS).empty?
|
95
|
+
raise ArgumentError, 'apply_probability_to' unless APPLY_PROBABILITY_TO_SYMBOLS.include?(apply_probability_to)
|
96
|
+
|
97
|
+
ProbabilitySampler.new(probability,
|
98
|
+
ignore_hints: ignore_hints.to_a,
|
99
|
+
ignore_parent: ignore_parent,
|
100
|
+
apply_to_remote_parent: apply_probability_to != :root_spans,
|
101
|
+
apply_to_all_spans: apply_probability_to == :all_spans)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module SDK
|
9
|
+
module Trace
|
10
|
+
module Samplers
|
11
|
+
# The Decision module contains a set of constants to be used in the
|
12
|
+
# decision part of a sampling {Result}.
|
13
|
+
module Decision
|
14
|
+
# Decision to not record events and not sample.
|
15
|
+
NOT_RECORD = OpenTelemetry::Trace::SamplingHint::NOT_RECORD
|
16
|
+
|
17
|
+
# Decision to record events and not sample.
|
18
|
+
RECORD = OpenTelemetry::Trace::SamplingHint::RECORD
|
19
|
+
|
20
|
+
# Decision to record events and sample.
|
21
|
+
RECORD_AND_SAMPLED = OpenTelemetry::Trace::SamplingHint::RECORD_AND_SAMPLED
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module SDK
|
9
|
+
module Trace
|
10
|
+
module Samplers
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
# Implements sampling based on a probability.
|
14
|
+
class ProbabilitySampler
|
15
|
+
HINT_RECORD_AND_SAMPLED = OpenTelemetry::Trace::SamplingHint::RECORD_AND_SAMPLED
|
16
|
+
HINT_RECORD = OpenTelemetry::Trace::SamplingHint::RECORD
|
17
|
+
|
18
|
+
private_constant(:HINT_RECORD_AND_SAMPLED, :HINT_RECORD)
|
19
|
+
|
20
|
+
def initialize(probability, ignore_hints:, ignore_parent:, apply_to_remote_parent:, apply_to_all_spans:)
|
21
|
+
@probability = probability
|
22
|
+
@id_upper_bound = format('%016x', (probability * (2**64 - 1)).ceil)
|
23
|
+
@ignored_hints = ignore_hints
|
24
|
+
@use_parent_sampled_flag = !ignore_parent
|
25
|
+
@apply_to_remote_parent = apply_to_remote_parent
|
26
|
+
@apply_to_all_spans = apply_to_all_spans
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
# Callable interface for probability sampler. See {Samplers}.
|
32
|
+
def call(trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:)
|
33
|
+
# Ignored for sampling decision: links, name, kind, attributes.
|
34
|
+
|
35
|
+
hint = nil if @ignored_hints.include?(hint)
|
36
|
+
|
37
|
+
sampled = sample?(hint, trace_id, parent_context)
|
38
|
+
recording = hint == HINT_RECORD || sampled
|
39
|
+
|
40
|
+
if sampled && recording
|
41
|
+
RECORD_AND_SAMPLED
|
42
|
+
elsif recording
|
43
|
+
RECORD
|
44
|
+
else
|
45
|
+
NOT_RECORD
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def sample?(hint, trace_id, parent_context)
|
52
|
+
if parent_context.nil?
|
53
|
+
hint == HINT_RECORD_AND_SAMPLED || sample_trace_id?(trace_id)
|
54
|
+
else
|
55
|
+
parent_sampled?(parent_context) || hint == HINT_RECORD_AND_SAMPLED || sample_trace_id_for_child?(parent_context, trace_id)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parent_sampled?(parent_context)
|
60
|
+
@use_parent_sampled_flag && parent_context.trace_flags.sampled?
|
61
|
+
end
|
62
|
+
|
63
|
+
def sample_trace_id_for_child?(parent_context, trace_id)
|
64
|
+
(@apply_to_all_spans || (@apply_to_remote_parent && parent_context.remote?)) && sample_trace_id?(trace_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def sample_trace_id?(trace_id)
|
68
|
+
@probability == 1.0 || trace_id[16, 16] < @id_upper_bound
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module SDK
|
9
|
+
module Trace
|
10
|
+
module Samplers
|
11
|
+
# The Result class represents an arbitrary sampling result. It has
|
12
|
+
# boolean values for the sampling decision and whether to record
|
13
|
+
# events, and a collection of attributes to be attached to a sampled
|
14
|
+
# root span.
|
15
|
+
class Result
|
16
|
+
EMPTY_HASH = {}.freeze
|
17
|
+
DECISIONS = [Decision::RECORD, Decision::NOT_RECORD, Decision::RECORD_AND_SAMPLED].freeze
|
18
|
+
private_constant(:EMPTY_HASH, :DECISIONS)
|
19
|
+
|
20
|
+
# Returns a frozen hash of attributes to be attached span.
|
21
|
+
#
|
22
|
+
# @return [Hash<String, Object>]
|
23
|
+
attr_reader :attributes
|
24
|
+
|
25
|
+
# Returns a new sampling result with the specified decision and
|
26
|
+
# attributes.
|
27
|
+
#
|
28
|
+
# @param [Symbol] decision Whether or not a span should be sampled
|
29
|
+
# and/or record events.
|
30
|
+
# @param [optional Hash<String, Object>] attributes A frozen or freezable hash
|
31
|
+
# containing attributes to be attached to the span.
|
32
|
+
def initialize(decision:, attributes: nil)
|
33
|
+
@decision = decision
|
34
|
+
@attributes = attributes.freeze || EMPTY_HASH
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns true if this span should be sampled.
|
38
|
+
#
|
39
|
+
# @return [Boolean] sampling decision
|
40
|
+
def sampled?
|
41
|
+
@decision == Decision::RECORD_AND_SAMPLED
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns true if this span should record events, attributes, status, etc.
|
45
|
+
#
|
46
|
+
# @return [Boolean] recording decision
|
47
|
+
def recording?
|
48
|
+
@decision != Decision::NOT_RECORD
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
module OpenTelemetry
|
8
|
+
module SDK
|
9
|
+
module Trace
|
10
|
+
# Implementation of {OpenTelemetry::Trace::Span} that records trace events.
|
11
|
+
#
|
12
|
+
# This implementation includes reader methods intended to allow access to
|
13
|
+
# internal state by SpanProcessors (see {NoopSpanProcessor} for the interface).
|
14
|
+
# Instrumentation should use the API provided by {OpenTelemetry::Trace::Span}
|
15
|
+
# and should consider {Span} to be write-only.
|
16
|
+
#
|
17
|
+
# rubocop:disable Metrics/ClassLength
|
18
|
+
class Span < OpenTelemetry::Trace::Span
|
19
|
+
# The following readers are intended for the use of SpanProcessors and
|
20
|
+
# should not be considered part of the public interface for instrumentation.
|
21
|
+
attr_reader :name, :status, :kind, :parent_span_id, :start_timestamp, :end_timestamp, :links, :library_resource
|
22
|
+
|
23
|
+
# Return a frozen copy of the current attributes. This is intended for
|
24
|
+
# use of SpanProcesses and should not be considered part of the public
|
25
|
+
# interface for instrumentation.
|
26
|
+
#
|
27
|
+
# @return [Hash<String, Object>] may be nil.
|
28
|
+
def attributes
|
29
|
+
# Don't bother synchronizing. Access by SpanProcessors is expected to
|
30
|
+
# be serialized.
|
31
|
+
@attributes&.clone.freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return a frozen copy of the current events. This is intended for use
|
35
|
+
# of SpanProcessors and should not be considered part of the public
|
36
|
+
# interface for instrumentation.
|
37
|
+
#
|
38
|
+
# @return [Array<Event>] may be nil.
|
39
|
+
def events
|
40
|
+
# Don't bother synchronizing. Access by SpanProcessors is expected to
|
41
|
+
# be serialized.
|
42
|
+
@events&.clone.freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the flag whether this span is recording events
|
46
|
+
#
|
47
|
+
# @return [Boolean] true if this Span is active and recording information
|
48
|
+
# like events with the #add_event operation and attributes using
|
49
|
+
# #set_attribute.
|
50
|
+
def recording?
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set attribute
|
55
|
+
#
|
56
|
+
# Note that the OpenTelemetry project
|
57
|
+
# {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
|
58
|
+
# documents} certain "standard attributes" that have prescribed semantic
|
59
|
+
# meanings.
|
60
|
+
#
|
61
|
+
# @param [String] key
|
62
|
+
# @param [String, Boolean, Numeric] value
|
63
|
+
#
|
64
|
+
# @return [self] returns itself
|
65
|
+
def set_attribute(key, value)
|
66
|
+
super
|
67
|
+
@mutex.synchronize do
|
68
|
+
if @ended
|
69
|
+
OpenTelemetry.logger.warn('Calling set_attribute on an ended Span.')
|
70
|
+
else
|
71
|
+
@attributes ||= {}
|
72
|
+
@attributes[key] = value
|
73
|
+
trim_span_attributes(@attributes)
|
74
|
+
@total_recorded_attributes += 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add an Event to a {Span}. This can be accomplished eagerly or lazily.
|
81
|
+
# Lazy evaluation is useful when the event attributes are expensive to
|
82
|
+
# build and where the cost can be avoided for an unsampled {Span}.
|
83
|
+
#
|
84
|
+
# Eager example:
|
85
|
+
#
|
86
|
+
# span.add_event(name: 'event', attributes: {'eager' => true})
|
87
|
+
#
|
88
|
+
# Lazy example:
|
89
|
+
#
|
90
|
+
# span.add_event { OpenTelemetry::Trace::Event.new(name: 'event', attributes: {'eager' => false}) }
|
91
|
+
#
|
92
|
+
# Note that the OpenTelemetry project
|
93
|
+
# {https://github.com/open-telemetry/opentelemetry-specification/blob/master/semantic-conventions.md
|
94
|
+
# documents} certain "standard event names and keys" which have
|
95
|
+
# prescribed semantic meanings.
|
96
|
+
#
|
97
|
+
# @param [optional String] name Optional name of the event. This is
|
98
|
+
# required if a block is not given.
|
99
|
+
# @param [optional Hash<String, Object>] attributes One or more key:value
|
100
|
+
# pairs, where the keys must be strings and the values may be string,
|
101
|
+
# boolean or numeric type. This argument should only be used when
|
102
|
+
# passing in a name.
|
103
|
+
# @param [optional Time] timestamp Optional timestamp for the event.
|
104
|
+
# This argument should only be used when passing in a name.
|
105
|
+
#
|
106
|
+
# @return [self] returns itself
|
107
|
+
def add_event(name: nil, attributes: nil, timestamp: nil)
|
108
|
+
super
|
109
|
+
event = block_given? ? yield : OpenTelemetry::Trace::Event.new(name: name, attributes: attributes, timestamp: timestamp || Time.now)
|
110
|
+
@mutex.synchronize do
|
111
|
+
if @ended
|
112
|
+
OpenTelemetry.logger.warn('Calling add_event on an ended Span.')
|
113
|
+
else
|
114
|
+
@events ||= []
|
115
|
+
@events = append_event(@events, event)
|
116
|
+
@total_recorded_events += 1
|
117
|
+
end
|
118
|
+
end
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sets the Status to the Span
|
123
|
+
#
|
124
|
+
# If used, this will override the default Span status. Default is OK.
|
125
|
+
#
|
126
|
+
# Only the value of the last call will be recorded, and implementations
|
127
|
+
# are free to ignore previous calls.
|
128
|
+
#
|
129
|
+
# @param [Status] status The new status, which overrides the default Span
|
130
|
+
# status, which is OK.
|
131
|
+
#
|
132
|
+
# @return [void]
|
133
|
+
def status=(status)
|
134
|
+
super
|
135
|
+
@mutex.synchronize do
|
136
|
+
if @ended
|
137
|
+
OpenTelemetry.logger.warn('Calling status= on an ended Span.')
|
138
|
+
else
|
139
|
+
@status = status
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Updates the Span name
|
145
|
+
#
|
146
|
+
# Upon this update, any sampling behavior based on Span name will depend
|
147
|
+
# on the implementation.
|
148
|
+
#
|
149
|
+
# @param [String] new_name The new operation name, which supersedes
|
150
|
+
# whatever was passed in when the Span was started
|
151
|
+
#
|
152
|
+
# @return [void]
|
153
|
+
def name=(new_name)
|
154
|
+
super
|
155
|
+
@mutex.synchronize do
|
156
|
+
if @ended
|
157
|
+
OpenTelemetry.logger.warn('Calling name= on an ended Span.')
|
158
|
+
else
|
159
|
+
@name = new_name
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Finishes the Span
|
165
|
+
#
|
166
|
+
# Implementations MUST ignore all subsequent calls to {#finish} (there
|
167
|
+
# might be exceptions when Tracer is streaming event and has no mutable
|
168
|
+
# state associated with the Span).
|
169
|
+
#
|
170
|
+
# Call to {#finish} MUST not have any effects on child spans. Those may
|
171
|
+
# still be running and can be ended later.
|
172
|
+
#
|
173
|
+
# This API MUST be non-blocking*.
|
174
|
+
#
|
175
|
+
# (*) not actually non-blocking. In particular, it synchronizes on an
|
176
|
+
# internal mutex, which will typically be uncontended, and
|
177
|
+
# {Export::BatchSpanProcessor} will also synchronize on a mutex, if that
|
178
|
+
# processor is used.
|
179
|
+
#
|
180
|
+
# @param [Time] end_timestamp optional end timestamp for the span.
|
181
|
+
#
|
182
|
+
# @return [self] returns itself
|
183
|
+
def finish(end_timestamp: nil)
|
184
|
+
@mutex.synchronize do
|
185
|
+
if @ended
|
186
|
+
OpenTelemetry.logger.warn('Calling finish on an ended Span.')
|
187
|
+
return self
|
188
|
+
end
|
189
|
+
@end_timestamp = end_timestamp || Time.now
|
190
|
+
@attributes.freeze
|
191
|
+
@events.freeze
|
192
|
+
@ended = true
|
193
|
+
end
|
194
|
+
@span_processor.on_finish(self)
|
195
|
+
self
|
196
|
+
end
|
197
|
+
|
198
|
+
# @api private
|
199
|
+
#
|
200
|
+
# Returns a SpanData containing a snapshot of the Span fields. It is
|
201
|
+
# assumed that the Span has been finished, and that no further
|
202
|
+
# modifications will be made to the Span.
|
203
|
+
#
|
204
|
+
# This method should be called *only* from a SpanProcessor prior to
|
205
|
+
# calling the SpanExporter.
|
206
|
+
#
|
207
|
+
# @return [SpanData]
|
208
|
+
def to_span_data
|
209
|
+
SpanData.new(
|
210
|
+
@name,
|
211
|
+
@kind,
|
212
|
+
@status,
|
213
|
+
@parent_span_id,
|
214
|
+
@child_count,
|
215
|
+
@total_recorded_attributes,
|
216
|
+
@total_recorded_events,
|
217
|
+
@total_recorded_links,
|
218
|
+
@start_timestamp,
|
219
|
+
@end_timestamp,
|
220
|
+
@attributes,
|
221
|
+
@links,
|
222
|
+
@events,
|
223
|
+
@library_resource,
|
224
|
+
context.span_id,
|
225
|
+
context.trace_id,
|
226
|
+
context.trace_flags
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
230
|
+
# @api private
|
231
|
+
def initialize(context, name, kind, parent_span_id, trace_config, span_processor, attributes, links, start_timestamp, library_resource) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
232
|
+
super(span_context: context)
|
233
|
+
@mutex = Mutex.new
|
234
|
+
@name = name
|
235
|
+
@kind = kind
|
236
|
+
@parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID
|
237
|
+
@trace_config = trace_config
|
238
|
+
@span_processor = span_processor
|
239
|
+
@library_resource = library_resource
|
240
|
+
@ended = false
|
241
|
+
@status = nil
|
242
|
+
@child_count = 0
|
243
|
+
@total_recorded_events = 0
|
244
|
+
@total_recorded_links = links&.size || 0
|
245
|
+
@total_recorded_attributes = attributes&.size || 0
|
246
|
+
@start_timestamp = start_timestamp
|
247
|
+
@end_timestamp = nil
|
248
|
+
@attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes.
|
249
|
+
trim_span_attributes(@attributes)
|
250
|
+
@events = nil
|
251
|
+
@links = trim_links(links, trace_config.max_links_count, trace_config.max_attributes_per_link)
|
252
|
+
@span_processor.on_start(self)
|
253
|
+
end
|
254
|
+
|
255
|
+
# TODO: Java implementation overrides finalize to log if a span isn't finished.
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def trim_span_attributes(attrs)
|
260
|
+
return if attrs.nil?
|
261
|
+
|
262
|
+
excess = attrs.size - @trace_config.max_attributes_count
|
263
|
+
# TODO: with Ruby 2.5, replace with the more efficient
|
264
|
+
# attrs.shift(excess) if excess.positive?
|
265
|
+
excess.times { attrs.shift } if excess.positive?
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
def trim_links(links, max_links_count, max_attributes_per_link) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
270
|
+
# Fast path (likely) common cases.
|
271
|
+
return nil if links.nil?
|
272
|
+
|
273
|
+
if links.size <= max_links_count &&
|
274
|
+
links.all? { |link| link.attributes.size <= max_attributes_per_link && Internal.valid_attributes?(link.attributes) }
|
275
|
+
return links.frozen? ? links : links.clone.freeze
|
276
|
+
end
|
277
|
+
|
278
|
+
# Slow path: trim attributes for each Link.
|
279
|
+
links.last(max_links_count).map! do |link|
|
280
|
+
attrs = Hash[link.attributes] # link.attributes is frozen, so we need an unfrozen copy to adjust.
|
281
|
+
attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
|
282
|
+
excess = attrs.size - max_attributes_per_link
|
283
|
+
excess.times { attrs.shift } if excess.positive?
|
284
|
+
OpenTelemetry::Trace::Link.new(link.context, attrs)
|
285
|
+
end.freeze
|
286
|
+
end
|
287
|
+
|
288
|
+
def append_event(events, event) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
289
|
+
max_events_count = @trace_config.max_events_count
|
290
|
+
max_attributes_per_event = @trace_config.max_attributes_per_event
|
291
|
+
|
292
|
+
# Fast path (likely) common case.
|
293
|
+
if events.size < max_events_count &&
|
294
|
+
event.attributes.size <= max_attributes_per_event &&
|
295
|
+
Internal.valid_attributes?(event.attributes)
|
296
|
+
return events << event
|
297
|
+
end
|
298
|
+
|
299
|
+
# Slow path.
|
300
|
+
excess = events.size + 1 - max_events_count
|
301
|
+
events.shift(excess) if excess.positive?
|
302
|
+
|
303
|
+
excess = event.attributes.size - max_attributes_per_event
|
304
|
+
if excess.positive? || !Internal.valid_attributes?(event.attributes)
|
305
|
+
attrs = Hash[event.attributes] # event.attributes is frozen, so we need an unfrozen copy to adjust.
|
306
|
+
attrs.keep_if { |key, value| Internal.valid_key?(key) && Internal.valid_value?(value) }
|
307
|
+
excess = attrs.size - max_attributes_per_event
|
308
|
+
excess.times { attrs.shift } if excess.positive?
|
309
|
+
event = OpenTelemetry::Trace::Event.new(name: event.name, attributes: attrs, timestamp: event.timestamp)
|
310
|
+
end
|
311
|
+
events << event
|
312
|
+
end
|
313
|
+
end
|
314
|
+
# rubocop:enable Metrics/ClassLength
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|