opentelemetry-sdk 0.2.0

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