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.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +201 -0
  4. data/lib/opentelemetry/sdk.rb +18 -0
  5. data/lib/opentelemetry/sdk/internal.rb +32 -0
  6. data/lib/opentelemetry/sdk/resources.rb +15 -0
  7. data/lib/opentelemetry/sdk/resources/resource.rb +75 -0
  8. data/lib/opentelemetry/sdk/trace.rb +24 -0
  9. data/lib/opentelemetry/sdk/trace/config.rb +18 -0
  10. data/lib/opentelemetry/sdk/trace/config/trace_config.rb +77 -0
  11. data/lib/opentelemetry/sdk/trace/export.rb +35 -0
  12. data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +149 -0
  13. data/lib/opentelemetry/sdk/trace/export/console_span_exporter.rb +40 -0
  14. data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +86 -0
  15. data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +64 -0
  16. data/lib/opentelemetry/sdk/trace/export/noop_span_exporter.rb +42 -0
  17. data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +63 -0
  18. data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +51 -0
  19. data/lib/opentelemetry/sdk/trace/noop_span_processor.rb +41 -0
  20. data/lib/opentelemetry/sdk/trace/samplers.rb +106 -0
  21. data/lib/opentelemetry/sdk/trace/samplers/decision.rb +26 -0
  22. data/lib/opentelemetry/sdk/trace/samplers/probability_sampler.rb +74 -0
  23. data/lib/opentelemetry/sdk/trace/samplers/result.rb +54 -0
  24. data/lib/opentelemetry/sdk/trace/span.rb +317 -0
  25. data/lib/opentelemetry/sdk/trace/span_data.rb +32 -0
  26. data/lib/opentelemetry/sdk/trace/tracer.rb +67 -0
  27. data/lib/opentelemetry/sdk/trace/tracer_factory.rb +84 -0
  28. data/lib/opentelemetry/sdk/version.rb +12 -0
  29. 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