opentelemetry-sdk 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,35 @@
|
|
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
|
+
# The Export module contains the built-in exporters for the OpenTelemetry
|
11
|
+
# reference implementation.
|
12
|
+
module Export
|
13
|
+
# Result codes for the SpanExporter#export method.
|
14
|
+
|
15
|
+
# The export operation finished successfully.
|
16
|
+
SUCCESS = 0
|
17
|
+
|
18
|
+
# The export operation finished with an error, but retrying may
|
19
|
+
# succeed.
|
20
|
+
FAILED_RETRYABLE = 1
|
21
|
+
|
22
|
+
# The export operation finished with an error, the caller should not
|
23
|
+
# try to export the same data again.
|
24
|
+
FAILED_NOT_RETRYABLE = 2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'opentelemetry/sdk/trace/export/batch_span_processor'
|
31
|
+
require 'opentelemetry/sdk/trace/export/console_span_exporter'
|
32
|
+
require 'opentelemetry/sdk/trace/export/in_memory_span_exporter'
|
33
|
+
require 'opentelemetry/sdk/trace/export/multi_span_exporter'
|
34
|
+
require 'opentelemetry/sdk/trace/export/noop_span_exporter'
|
35
|
+
require 'opentelemetry/sdk/trace/export/simple_span_processor'
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'timeout'
|
8
|
+
|
9
|
+
module OpenTelemetry
|
10
|
+
module SDK
|
11
|
+
module Trace
|
12
|
+
module Export
|
13
|
+
# Implementation of the duck type SpanProcessor that batches spans
|
14
|
+
# exported by the SDK then pushes them to the exporter pipeline.
|
15
|
+
#
|
16
|
+
# All spans reported by the SDK implementation are first added to a
|
17
|
+
# synchronized queue (with a {max_queue_size} maximum size, after the
|
18
|
+
# size is reached spans are dropped) and exported every
|
19
|
+
# schedule_delay_millis to the exporter pipeline in batches of
|
20
|
+
# max_export_batch_size.
|
21
|
+
#
|
22
|
+
# If the queue gets half full a preemptive notification is sent to the
|
23
|
+
# worker thread that exports the spans to wake up and start a new
|
24
|
+
# export cycle.
|
25
|
+
#
|
26
|
+
# max_export_attempts attempts are made to export each batch, while
|
27
|
+
# export fails with {FAILED_RETRYABLE}, backing off linearly in 100ms
|
28
|
+
# increments.
|
29
|
+
class BatchSpanProcessor
|
30
|
+
EXPORTER_TIMEOUT_MILLIS = 30_000
|
31
|
+
SCHEDULE_DELAY_MILLIS = 5_000
|
32
|
+
MAX_QUEUE_SIZE = 2048
|
33
|
+
MAX_EXPORT_BATCH_SIZE = 512
|
34
|
+
MAX_EXPORT_ATTEMPTS = 5
|
35
|
+
private_constant(:SCHEDULE_DELAY_MILLIS, :MAX_QUEUE_SIZE, :MAX_EXPORT_BATCH_SIZE, :MAX_EXPORT_ATTEMPTS)
|
36
|
+
|
37
|
+
def initialize(exporter:,
|
38
|
+
exporter_timeout_millis: EXPORTER_TIMEOUT_MILLIS,
|
39
|
+
schedule_delay_millis: SCHEDULE_DELAY_MILLIS,
|
40
|
+
max_queue_size: MAX_QUEUE_SIZE,
|
41
|
+
max_export_batch_size: MAX_EXPORT_BATCH_SIZE,
|
42
|
+
max_export_attempts: MAX_EXPORT_ATTEMPTS)
|
43
|
+
raise ArgumentError if max_export_batch_size > max_queue_size
|
44
|
+
|
45
|
+
@exporter = exporter
|
46
|
+
@exporter_timeout_seconds = exporter_timeout_millis / 1000.0
|
47
|
+
@mutex = Mutex.new
|
48
|
+
@condition = ConditionVariable.new
|
49
|
+
@keep_running = true
|
50
|
+
@delay_seconds = schedule_delay_millis / 1000.0
|
51
|
+
@max_queue_size = max_queue_size
|
52
|
+
@batch_size = max_export_batch_size
|
53
|
+
@export_attempts = max_export_attempts
|
54
|
+
@spans = []
|
55
|
+
@thread = Thread.new { work }
|
56
|
+
end
|
57
|
+
|
58
|
+
# does nothing for this processor
|
59
|
+
def on_start(span)
|
60
|
+
# noop
|
61
|
+
end
|
62
|
+
|
63
|
+
# adds a span to the batcher, threadsafe may block on lock
|
64
|
+
def on_finish(span) # rubocop:disable Metrics/AbcSize
|
65
|
+
return unless span.context.trace_flags.sampled?
|
66
|
+
|
67
|
+
lock do
|
68
|
+
n = spans.size + 1 - max_queue_size
|
69
|
+
spans.shift(n) if n.positive?
|
70
|
+
spans << span
|
71
|
+
@condition.signal if spans.size > max_queue_size / 2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# shuts the consumer thread down and flushes the current accumulated buffer
|
76
|
+
# will block until the thread is finished
|
77
|
+
def shutdown
|
78
|
+
lock do
|
79
|
+
@keep_running = false
|
80
|
+
@condition.signal
|
81
|
+
end
|
82
|
+
|
83
|
+
@thread.join
|
84
|
+
flush
|
85
|
+
@exporter.shutdown
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
attr_reader :spans, :max_queue_size, :batch_size
|
91
|
+
|
92
|
+
def work
|
93
|
+
loop do
|
94
|
+
batch = lock do
|
95
|
+
@condition.wait(@mutex, @delay_seconds) if spans.size < batch_size && @keep_running
|
96
|
+
@condition.wait(@mutex, @delay_seconds) while spans.empty? && @keep_running
|
97
|
+
return unless @keep_running
|
98
|
+
|
99
|
+
fetch_batch
|
100
|
+
end
|
101
|
+
|
102
|
+
export_batch(batch)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def export_batch(batch)
|
107
|
+
result_code = nil
|
108
|
+
@export_attempts.times do |attempts|
|
109
|
+
result_code = export_with_timeout(batch)
|
110
|
+
break unless result_code == FAILED_RETRYABLE
|
111
|
+
|
112
|
+
sleep(0.1 * attempts)
|
113
|
+
end
|
114
|
+
report_result(result_code, batch)
|
115
|
+
end
|
116
|
+
|
117
|
+
def export_with_timeout(batch)
|
118
|
+
Timeout.timeout(@exporter_timeout_seconds) { @exporter.export(batch) }
|
119
|
+
rescue Timeout::Error
|
120
|
+
FAILED_NOT_RETRYABLE
|
121
|
+
end
|
122
|
+
|
123
|
+
def report_result(result_code, batch)
|
124
|
+
OpenTelemetry.logger.error("Unable to export #{batch.size} spans") unless result_code == SUCCESS
|
125
|
+
end
|
126
|
+
|
127
|
+
def flush
|
128
|
+
snapshot = lock { spans.shift(spans.size) }
|
129
|
+
until snapshot.empty?
|
130
|
+
batch = snapshot.shift(@batch_size).map!(&:to_span_data)
|
131
|
+
result_code = @exporter.export(batch)
|
132
|
+
report_result(result_code, batch)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_batch
|
137
|
+
spans.shift(@batch_size).map!(&:to_span_data)
|
138
|
+
end
|
139
|
+
|
140
|
+
def lock
|
141
|
+
@mutex.synchronize do
|
142
|
+
yield
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 OpenTelemetry Authors
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
6
|
+
|
7
|
+
require 'pp'
|
8
|
+
|
9
|
+
module OpenTelemetry
|
10
|
+
module SDK
|
11
|
+
module Trace
|
12
|
+
module Export
|
13
|
+
# Outputs {SpanData} to the console.
|
14
|
+
#
|
15
|
+
# Potentially useful for exploratory purposes.
|
16
|
+
class ConsoleSpanExporter
|
17
|
+
ResultCodes = OpenTelemetry::SDK::Trace::Export
|
18
|
+
|
19
|
+
private_constant(:ResultCodes)
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@stopped = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def export(spans)
|
26
|
+
return ResultCodes::FAILED_NOT_RETRYABLE if @stopped
|
27
|
+
|
28
|
+
Array(spans).each { |s| pp s }
|
29
|
+
|
30
|
+
ResultCodes::SUCCESS
|
31
|
+
end
|
32
|
+
|
33
|
+
def shutdown
|
34
|
+
@stopped = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,86 @@
|
|
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 Export
|
11
|
+
# A SpanExporter implementation that can be used to test OpenTelemetry integration.
|
12
|
+
#
|
13
|
+
# Example usage:
|
14
|
+
#
|
15
|
+
# class MyClassTest
|
16
|
+
# def setup
|
17
|
+
# @tracer_factory = TracerFactory.new
|
18
|
+
# @exporter = InMemorySpanExporter.new
|
19
|
+
# @tracer_factory.add_span_processor(SimpleSampledSpansProcessor.new(@exporter))
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def test_finished_spans
|
23
|
+
# @tracer_factory.tracer.in_span("span") {}
|
24
|
+
#
|
25
|
+
# spans = @exporter.finished_spans
|
26
|
+
# spans.wont_be_nil
|
27
|
+
# spans.size.must_equal(1)
|
28
|
+
# spans[0].name.must_equal("span")
|
29
|
+
# end
|
30
|
+
class InMemorySpanExporter
|
31
|
+
# Returns a new instance of the {InMemorySpanExporter}.
|
32
|
+
#
|
33
|
+
# @return a new instance of the {InMemorySpanExporter}.
|
34
|
+
def initialize
|
35
|
+
@finished_spans = []
|
36
|
+
@stopped = false
|
37
|
+
@mutex = Mutex.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a frozen array of the finished {SpanData}s, represented by
|
41
|
+
# {io.opentelemetry.proto.trace.v1.Span}.
|
42
|
+
#
|
43
|
+
# @return [Array<SpanData>] a frozen array of the finished {SpanData}s.
|
44
|
+
def finished_spans
|
45
|
+
@mutex.synchronize do
|
46
|
+
@finished_spans.clone.freeze
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Clears the internal collection of finished {Span}s.
|
51
|
+
#
|
52
|
+
# Does not reset the state of this exporter if already shutdown.
|
53
|
+
def reset
|
54
|
+
@mutex.synchronize do
|
55
|
+
@finished_spans.clear
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Called to export sampled {SpanData}s.
|
60
|
+
#
|
61
|
+
# @param [Enumerable<SpanData>] span_datas the list of sampled {SpanData}s to be
|
62
|
+
# exported.
|
63
|
+
# @return [Integer] the result of the export, SUCCESS or
|
64
|
+
# FAILED_NOT_RETRYABLE
|
65
|
+
def export(span_datas)
|
66
|
+
@mutex.synchronize do
|
67
|
+
return FAILED_NOT_RETRYABLE if @stopped
|
68
|
+
|
69
|
+
@finished_spans.concat(span_datas.to_a)
|
70
|
+
end
|
71
|
+
SUCCESS
|
72
|
+
end
|
73
|
+
|
74
|
+
# Called when {TracerFactory#shutdown} is called, if this exporter is
|
75
|
+
# registered to a {TracerFactory} object.
|
76
|
+
def shutdown
|
77
|
+
@mutex.synchronize do
|
78
|
+
@finished_spans.clear
|
79
|
+
@stopped = true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,64 @@
|
|
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 Export
|
11
|
+
# Implementation of the SpanExporter duck type that simply forwards all
|
12
|
+
# received spans to a collection of SpanExporters.
|
13
|
+
#
|
14
|
+
# Can be used to export to multiple backends using the same
|
15
|
+
# SpanProcessor like a {SimpleSpanProcessor} or a
|
16
|
+
# {BatchSpanProcessor}.
|
17
|
+
class MultiSpanExporter
|
18
|
+
def initialize(span_exporters)
|
19
|
+
@span_exporters = span_exporters.clone.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called to export sampled {Span}s.
|
23
|
+
#
|
24
|
+
# @param [Enumerable<Span>] spans the list of sampled {Span}s to be
|
25
|
+
# exported.
|
26
|
+
# @return [Integer] the result of the export.
|
27
|
+
def export(spans)
|
28
|
+
@span_exporters.inject(SUCCESS) do |result_code, span_exporter|
|
29
|
+
begin
|
30
|
+
merge_result_code(result_code, span_exporter.export(spans))
|
31
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
32
|
+
OpenTelemetry.logger.warn("exception raised by export - #{e}")
|
33
|
+
FAILED_NOT_RETRYABLE
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Called when {TracerFactory#shutdown} is called, if this exporter is
|
39
|
+
# registered to a {TracerFactory} object.
|
40
|
+
def shutdown
|
41
|
+
@span_exporters.each(&:shutdown)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Returns a merged error code, see the rules in the code.
|
47
|
+
def merge_result_code(result_code, new_result_code)
|
48
|
+
if result_code == SUCCESS && new_result_code == SUCCESS
|
49
|
+
# If both errors are success then return success.
|
50
|
+
SUCCESS
|
51
|
+
elsif result_code == FAILED_NOT_RETRYABLE || new_result_code == FAILED_NOT_RETRYABLE
|
52
|
+
# If any of the codes is not retryable then return not_retryable.
|
53
|
+
FAILED_NOT_RETRYABLE
|
54
|
+
else
|
55
|
+
# At this point at least one of the code is FAILED_RETRYABLE and
|
56
|
+
# none are FAILED_NOT_RETRYABLE, so return FAILED_RETRYABLE.
|
57
|
+
FAILED_RETRYABLE
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,42 @@
|
|
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 Export
|
11
|
+
# A noop exporter that demonstrates and documents the SpanExporter
|
12
|
+
# duck type. SpanExporter allows different tracing services to export
|
13
|
+
# recorded data for sampled spans in their own format.
|
14
|
+
#
|
15
|
+
# To export data an exporter MUST be registered to the {TracerFactory} using
|
16
|
+
# a {SimpleSpanProcessor} or a {BatchSpanProcessor}.
|
17
|
+
class NoopSpanExporter
|
18
|
+
def initialize
|
19
|
+
@stopped = false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called to export sampled {Span}s.
|
23
|
+
#
|
24
|
+
# @param [Enumerable<Span>] spans the list of sampled {Span}s to be
|
25
|
+
# exported.
|
26
|
+
# @return [Integer] the result of the export.
|
27
|
+
def export(spans)
|
28
|
+
return SUCCESS unless @stopped
|
29
|
+
|
30
|
+
FAILED_NOT_RETRYABLE
|
31
|
+
end
|
32
|
+
|
33
|
+
# Called when {TracerFactory#shutdown} is called, if this exporter is
|
34
|
+
# registered to a {TracerFactory} object.
|
35
|
+
def shutdown
|
36
|
+
@stopped = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
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 Export
|
11
|
+
# An implementation of the duck type SpanProcessor that converts the
|
12
|
+
# {Span} to {io.opentelemetry.proto.trace.v1.Span} and passes it to the
|
13
|
+
# configured exporter.
|
14
|
+
#
|
15
|
+
# Only spans that are recorded are converted, {OpenTelemetry::Trace::Span#is_recording?} must
|
16
|
+
# return true.
|
17
|
+
class SimpleSpanProcessor
|
18
|
+
# Returns a new {SimpleSpanProcessor} that converts spans to
|
19
|
+
# proto and forwards them to the given span_exporter.
|
20
|
+
#
|
21
|
+
# @param span_exporter the (duck type) SpanExporter to where the
|
22
|
+
# recorded Spans are pushed.
|
23
|
+
# @return [SimpleSpanProcessor]
|
24
|
+
# @raise ArgumentError if the span_exporter is nil.
|
25
|
+
def initialize(span_exporter)
|
26
|
+
@span_exporter = span_exporter
|
27
|
+
end
|
28
|
+
|
29
|
+
# Called when a {Span} is started, if the {Span#recording?}
|
30
|
+
# returns true.
|
31
|
+
#
|
32
|
+
# This method is called synchronously on the execution thread, should
|
33
|
+
# not throw or block the execution thread.
|
34
|
+
#
|
35
|
+
# @param [Span] span the {Span} that just started.
|
36
|
+
def on_start(span)
|
37
|
+
# Do nothing.
|
38
|
+
end
|
39
|
+
|
40
|
+
# Called when a {Span} is ended, if the {Span#recording?}
|
41
|
+
# returns true.
|
42
|
+
#
|
43
|
+
# This method is called synchronously on the execution thread, should
|
44
|
+
# not throw or block the execution thread.
|
45
|
+
#
|
46
|
+
# @param [Span] span the {Span} that just ended.
|
47
|
+
def on_finish(span)
|
48
|
+
return unless span.context.trace_flags.sampled?
|
49
|
+
|
50
|
+
@span_exporter&.export([span.to_span_data])
|
51
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
52
|
+
OpenTelemetry.logger.error("unexpected error in span.on_finish - #{e}")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Called when {TracerFactory#shutdown} is called.
|
56
|
+
def shutdown
|
57
|
+
@span_exporter&.shutdown
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|