opentelemetry-sdk 0.2.0 → 0.6.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +23 -0
  4. data/LICENSE +1 -1
  5. data/README.md +73 -0
  6. data/lib/opentelemetry-sdk.rb +7 -0
  7. data/lib/opentelemetry/sdk.rb +51 -0
  8. data/lib/opentelemetry/sdk/baggage.rb +16 -0
  9. data/lib/opentelemetry/sdk/baggage/builder.rb +40 -0
  10. data/lib/opentelemetry/sdk/baggage/manager.rb +97 -0
  11. data/lib/opentelemetry/sdk/configurator.rb +172 -0
  12. data/lib/opentelemetry/sdk/instrumentation_library.rb +13 -0
  13. data/lib/opentelemetry/sdk/internal.rb +21 -1
  14. data/lib/opentelemetry/sdk/resources.rb +1 -0
  15. data/lib/opentelemetry/sdk/resources/constants.rb +124 -0
  16. data/lib/opentelemetry/sdk/resources/resource.rb +39 -19
  17. data/lib/opentelemetry/sdk/trace.rb +2 -1
  18. data/lib/opentelemetry/sdk/trace/config/trace_config.rb +3 -3
  19. data/lib/opentelemetry/sdk/trace/event.rb +48 -0
  20. data/lib/opentelemetry/sdk/trace/export.rb +2 -7
  21. data/lib/opentelemetry/sdk/trace/export/batch_span_processor.rb +41 -35
  22. data/lib/opentelemetry/sdk/trace/export/console_span_exporter.rb +1 -1
  23. data/lib/opentelemetry/sdk/trace/export/in_memory_span_exporter.rb +7 -7
  24. data/lib/opentelemetry/sdk/trace/export/multi_span_exporter.rb +8 -14
  25. data/lib/opentelemetry/sdk/trace/export/noop_span_exporter.rb +4 -4
  26. data/lib/opentelemetry/sdk/trace/export/simple_span_processor.rb +10 -1
  27. data/lib/opentelemetry/sdk/trace/multi_span_processor.rb +12 -1
  28. data/lib/opentelemetry/sdk/trace/noop_span_processor.rb +10 -1
  29. data/lib/opentelemetry/sdk/trace/samplers.rb +48 -57
  30. data/lib/opentelemetry/sdk/trace/samplers/constant_sampler.rb +33 -0
  31. data/lib/opentelemetry/sdk/trace/samplers/decision.rb +3 -3
  32. data/lib/opentelemetry/sdk/trace/samplers/parent_based.rb +53 -0
  33. data/lib/opentelemetry/sdk/trace/samplers/result.rb +4 -3
  34. data/lib/opentelemetry/sdk/trace/samplers/trace_id_ratio_based.rb +45 -0
  35. data/lib/opentelemetry/sdk/trace/span.rb +39 -28
  36. data/lib/opentelemetry/sdk/trace/span_data.rb +18 -2
  37. data/lib/opentelemetry/sdk/trace/tracer.rb +26 -15
  38. data/lib/opentelemetry/sdk/trace/{tracer_factory.rb → tracer_provider.rb} +9 -9
  39. data/lib/opentelemetry/sdk/version.rb +1 -1
  40. metadata +20 -8
  41. data/lib/opentelemetry/sdk/trace/samplers/probability_sampler.rb +0 -74
@@ -0,0 +1,13 @@
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
+ # InstrumentationLibrary is a struct containing library information for export.
10
+ InstrumentationLibrary = Struct.new(:name,
11
+ :version)
12
+ end
13
+ end
@@ -20,10 +20,30 @@ module OpenTelemetry
20
20
  key.instance_of?(String)
21
21
  end
22
22
 
23
- def valid_value?(value)
23
+ def valid_simple_value?(value)
24
24
  value.instance_of?(String) || value == false || value == true || value.is_a?(Numeric)
25
25
  end
26
26
 
27
+ def valid_array_value?(value)
28
+ return false unless value.is_a?(Array)
29
+ return true if value.empty?
30
+
31
+ case value.first
32
+ when String
33
+ value.all? { |v| v.instance_of?(String) }
34
+ when TrueClass, FalseClass
35
+ value.all? { |v| boolean?(v) }
36
+ when Numeric
37
+ value.all? { |v| v.is_a?(Numeric) }
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def valid_value?(value)
44
+ valid_simple_value?(value) || valid_array_value?(value)
45
+ end
46
+
27
47
  def valid_attributes?(attrs)
28
48
  attrs.nil? || attrs.all? { |k, v| valid_key?(k) && valid_value?(v) }
29
49
  end
@@ -13,3 +13,4 @@ module OpenTelemetry
13
13
  end
14
14
 
15
15
  require 'opentelemetry/sdk/resources/resource'
16
+ require 'opentelemetry/sdk/resources/constants'
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module SDK
9
+ module Resources
10
+ module Constants
11
+ # Attributes describing a service instance.
12
+ SERVICE_RESOURCE = {
13
+ # Logical name of the service.
14
+ name: 'service.name',
15
+
16
+ # A namespace for `service.name`.
17
+ namespace: 'service.namespace',
18
+
19
+ # The string ID of the service instance.
20
+ instance_id: 'service.instance.id',
21
+
22
+ # The version string of the service API or implementation.
23
+ version: 'service.version'
24
+ }.freeze
25
+
26
+ # Attributes describing the telemetry library.
27
+ TELEMETRY_SDK_RESOURCE = {
28
+ # The name of the telemetry library.
29
+ name: 'telemetry.sdk.name',
30
+
31
+ # The language of the telemetry library and of the code instrumented with it.
32
+ language: 'telemetry.sdk.language',
33
+
34
+ # The version string of the telemetry library
35
+ version: 'telemetry.sdk.version'
36
+ }.freeze
37
+
38
+ # Attributes defining a compute unit (e.g. Container, Process, Lambda
39
+ # Function).
40
+ CONTAINER_RESOURCE = {
41
+ # The container name.
42
+ name: 'container.name',
43
+
44
+ # The name of the image the container was built on.
45
+ image_name: 'container.image.name',
46
+
47
+ # The container image tag.
48
+ image_tag: 'container.image.tag'
49
+ }.freeze
50
+
51
+ FAAS_RESOURCE = {
52
+ # The name of the function being executed.
53
+ name: 'faas.name',
54
+
55
+ # The unique name of the function being executed.
56
+ id: 'faas.id',
57
+
58
+ # The version string of the function being executed.
59
+ version: 'faas.version',
60
+
61
+ # The execution environment ID as a string.
62
+ instance: 'faas.instance'
63
+ }.freeze
64
+
65
+ # Attributes defining a deployment service (e.g. Kubernetes).
66
+ K8S_RESOURCE = {
67
+ # The name of the cluster that the pod is running in.
68
+ cluster_name: 'k8s.cluster.name',
69
+
70
+ # The name of the namespace that the pod is running in.
71
+ namespace_name: 'k8s.namespace.name',
72
+
73
+ # The name of the pod.
74
+ pod_name: 'k8s.pod.name',
75
+
76
+ # The name of the deployment.
77
+ deployment_name: 'k8s.deployment.name'
78
+ }.freeze
79
+
80
+ # Attributes defining a computing instance (e.g. host).
81
+ HOST_RESOURCE = {
82
+ # Hostname of the host. It contains what the hostname command returns on the
83
+ # host machine.
84
+ hostname: 'host.hostname',
85
+
86
+ # Unique host id. For Cloud this must be the instance_id assigned by the
87
+ # cloud provider
88
+ id: 'host.id',
89
+
90
+ # Name of the host. It may contain what hostname returns on Unix systems,
91
+ # the fully qualified, or a name specified by the user.
92
+ name: 'host.name',
93
+
94
+ # Type of host. For Cloud this must be the machine type.
95
+ type: 'host.type',
96
+
97
+ # Name of the VM image or OS install the host was instantiated from.
98
+ image_name: 'host.image.name',
99
+
100
+ # VM image id. For Cloud, this value is from the provider.
101
+ image_id: 'host.image.id',
102
+
103
+ # The version string of the VM image.
104
+ image_version: 'host.image.version'
105
+ }.freeze
106
+
107
+ # Attributes defining a running environment (e.g. Cloud, Data Center).
108
+ CLOUD_RESOURCE = {
109
+ # Name of the cloud provider. Example values are aws, azure, gcp.
110
+ provider: 'cloud.provider',
111
+
112
+ # The cloud account id used to identify different entities.
113
+ account_id: 'cloud.account.id',
114
+
115
+ # A specific geographical location where different entities can run.
116
+ region: 'cloud.region',
117
+
118
+ # Zones are a sub set of the region connected through low-latency links.
119
+ zone: 'cloud.zone'
120
+ }.freeze
121
+ end
122
+ end
123
+ end
124
+ end
@@ -13,20 +13,40 @@ module OpenTelemetry
13
13
  class << self
14
14
  private :new # rubocop:disable Style/AccessModifierDeclarations
15
15
 
16
- # Returns a newly created {Resource} with the specified labels
16
+ # Returns a newly created {Resource} with the specified attributes
17
17
  #
18
- # @param [Hash<String, String>] labels Hash of key-value pairs to be used
19
- # as labels for this resource
20
- # @raise [ArgumentError] If label keys and values are not strings
18
+ # @param [Hash{String => String, Numeric, Boolean} attributes Hash of key-value pairs to be used
19
+ # as attributes for this resource
20
+ # @raise [ArgumentError] If attribute keys and values are not strings
21
21
  # @return [Resource]
22
- def create(labels = {})
23
- frozen_labels = labels.each_with_object({}) do |(k, v), memo|
24
- raise ArgumentError, 'label keys and values must be strings' unless k.is_a?(String) && v.is_a?(String)
22
+ def create(attributes = {})
23
+ frozen_attributes = attributes.each_with_object({}) do |(k, v), memo|
24
+ raise ArgumentError, 'attribute keys must be strings' unless k.is_a?(String)
25
+ raise ArgumentError, 'attribute values must be strings, integers, floats, or booleans' unless Internal.valid_value?(v)
25
26
 
26
- memo[-k] = -v
27
+ memo[-k] = v.freeze
27
28
  end.freeze
28
29
 
29
- new(frozen_labels)
30
+ new(frozen_attributes)
31
+ end
32
+
33
+ def telemetry_sdk
34
+ resource_attributes = {
35
+ Constants::TELEMETRY_SDK_RESOURCE[:name] => 'opentelemetry',
36
+ Constants::TELEMETRY_SDK_RESOURCE[:language] => 'ruby',
37
+ Constants::TELEMETRY_SDK_RESOURCE[:version] => OpenTelemetry::SDK::VERSION
38
+ }
39
+
40
+ resource_pairs = ENV['OTEL_RESOURCE_ATTRIBUTES']
41
+ return create(resource_attributes) unless resource_pairs.is_a?(String)
42
+
43
+ resource_pairs.split(',').each do |pair|
44
+ key, value = pair.split('=')
45
+ resource_attributes[key] = value
46
+ end
47
+
48
+ resource_attributes.delete_if { |_key, value| value.nil? || value.empty? }
49
+ create(resource_attributes)
30
50
  end
31
51
  end
32
52
 
@@ -35,18 +55,18 @@ module OpenTelemetry
35
55
  # Users should use the {create} factory method to obtain a {Resource}
36
56
  # instance.
37
57
  #
38
- # @param [Hash<String, String>] frozen_labels Frozen-hash of frozen-string
39
- # key-value pairs to be used as labels for this resource
58
+ # @param [Hash<String, String>] frozen_attributes Frozen-hash of frozen-string
59
+ # key-value pairs to be used as attributes for this resource
40
60
  # @return [Resource]
41
- def initialize(frozen_labels)
42
- @labels = frozen_labels
61
+ def initialize(frozen_attributes)
62
+ @attributes = frozen_attributes
43
63
  end
44
64
 
45
- # Returns an enumerator for labels of this {Resource}
65
+ # Returns an enumerator for attributes of this {Resource}
46
66
  #
47
67
  # @return [Enumerator]
48
- def label_enumerator
49
- @label_enumerator ||= labels.to_enum
68
+ def attribute_enumerator
69
+ @attribute_enumerator ||= attributes.to_enum
50
70
  end
51
71
 
52
72
  # Returns a new, merged {Resource} by merging the current {Resource} with
@@ -59,16 +79,16 @@ module OpenTelemetry
59
79
  def merge(other)
60
80
  return self unless other.is_a?(Resource)
61
81
 
62
- merged_labels = labels.merge(other.labels) do |_, old_v, new_v|
82
+ merged_attributes = attributes.merge(other.attributes) do |_, old_v, new_v|
63
83
  old_v.empty? ? new_v : old_v
64
84
  end
65
85
 
66
- self.class.send(:new, merged_labels.freeze)
86
+ self.class.send(:new, merged_attributes.freeze)
67
87
  end
68
88
 
69
89
  protected
70
90
 
71
- attr_reader :labels
91
+ attr_reader :attributes
72
92
  end
73
93
  end
74
94
  end
@@ -15,10 +15,11 @@ end
15
15
 
16
16
  require 'opentelemetry/sdk/trace/samplers'
17
17
  require 'opentelemetry/sdk/trace/config'
18
+ require 'opentelemetry/sdk/trace/event'
18
19
  require 'opentelemetry/sdk/trace/export'
19
20
  require 'opentelemetry/sdk/trace/multi_span_processor'
20
21
  require 'opentelemetry/sdk/trace/noop_span_processor'
21
22
  require 'opentelemetry/sdk/trace/span_data'
22
23
  require 'opentelemetry/sdk/trace/span'
23
24
  require 'opentelemetry/sdk/trace/tracer'
24
- require 'opentelemetry/sdk/trace/tracer_factory'
25
+ require 'opentelemetry/sdk/trace/tracer_provider'
@@ -10,7 +10,7 @@ module OpenTelemetry
10
10
  module Config
11
11
  # Class that holds global trace parameters.
12
12
  class TraceConfig
13
- DEFAULT_SAMPLER = Samplers::ALWAYS_ON
13
+ DEFAULT_SAMPLER = Samplers.parent_based(root: Samplers::ALWAYS_ON)
14
14
  DEFAULT_MAX_ATTRIBUTES_COUNT = 32
15
15
  DEFAULT_MAX_EVENTS_COUNT = 128
16
16
  DEFAULT_MAX_LINKS_COUNT = 32
@@ -30,13 +30,13 @@ module OpenTelemetry
30
30
  # The global default max number of attributes per {Span}.
31
31
  attr_reader :max_attributes_count
32
32
 
33
- # The global default max number of {OpenTelemetry::Trace::Event}s per {Span}.
33
+ # The global default max number of {OpenTelemetry::SDK::Trace::Event}s per {Span}.
34
34
  attr_reader :max_events_count
35
35
 
36
36
  # The global default max number of {OpenTelemetry::Trace::Link} entries per {Span}.
37
37
  attr_reader :max_links_count
38
38
 
39
- # The global default max number of attributes per {OpenTelemetry::Trace::Event}.
39
+ # The global default max number of attributes per {OpenTelemetry::SDK::Trace::Event}.
40
40
  attr_reader :max_attributes_per_event
41
41
 
42
42
  # The global default max number of attributes per {OpenTelemetry::Trace::Link}.
@@ -0,0 +1,48 @@
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
+ # A text annotation with a set of attributes and a timestamp.
11
+ class Event
12
+ EMPTY_ATTRIBUTES = {}.freeze
13
+
14
+ private_constant :EMPTY_ATTRIBUTES
15
+
16
+ # Returns the name of this event
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
46
+ end
47
+ end
48
+ end
@@ -15,13 +15,8 @@ module OpenTelemetry
15
15
  # The export operation finished successfully.
16
16
  SUCCESS = 0
17
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
18
+ # The export operation finished with an error.
19
+ FAILURE = 1
25
20
  end
26
21
  end
27
22
  end
@@ -22,24 +22,29 @@ module OpenTelemetry
22
22
  # If the queue gets half full a preemptive notification is sent to the
23
23
  # worker thread that exports the spans to wake up and start a new
24
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
25
  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
-
26
+ # Returns a new instance of the {BatchSpanProcessor}.
27
+ #
28
+ # @param [SpanExporter] exporter
29
+ # @param [Numeric] exporter_timeout_millis the delay interval between two
30
+ # consecutive exports. Defaults to the value of the OTEL_BSP_EXPORT_TIMEOUT_MILLIS
31
+ # environment variable, if set, or 30,000 (30 seconds).
32
+ # @param [Numeric] schedule_delay_millis the maximum allowed time to export data.
33
+ # Defaults to the value of the OTEL_BSP_SCHEDULE_DELAY_MILLIS environment
34
+ # variable, if set, or 5,000 (5 seconds).
35
+ # @param [Integer] max_queue_size the maximum queue size in spans.
36
+ # Defaults to the value of the OTEL_BSP_MAX_QUEUE_SIZE environment
37
+ # variable, if set, or 2048.
38
+ # @param [Integer] max_export_batch_size the maximum batch size in spans.
39
+ # Defaults to the value of the OTEL_BSP_MAX_EXPORT_BATCH_SIZE environment
40
+ # variable, if set, or 512.
41
+ #
42
+ # @return a new instance of the {BatchSpanProcessor}.
37
43
  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)
44
+ exporter_timeout_millis: Float(ENV.fetch('OTEL_BSP_EXPORT_TIMEOUT_MILLIS', 30_000)),
45
+ schedule_delay_millis: Float(ENV.fetch('OTEL_BSP_SCHEDULE_DELAY_MILLIS', 5_000)),
46
+ max_queue_size: Integer(ENV.fetch('OTEL_BSP_MAX_QUEUE_SIZE', 2048)),
47
+ max_export_batch_size: Integer(ENV.fetch('OTEL_BSP_MAX_EXPORT_BATCH_SIZE', 512)))
43
48
  raise ArgumentError if max_export_batch_size > max_queue_size
44
49
 
45
50
  @exporter = exporter
@@ -50,7 +55,6 @@ module OpenTelemetry
50
55
  @delay_seconds = schedule_delay_millis / 1000.0
51
56
  @max_queue_size = max_queue_size
52
57
  @batch_size = max_export_batch_size
53
- @export_attempts = max_export_attempts
54
58
  @spans = []
55
59
  @thread = Thread.new { work }
56
60
  end
@@ -72,6 +76,23 @@ module OpenTelemetry
72
76
  end
73
77
  end
74
78
 
79
+ # TODO: test this explicitly.
80
+ # Export all ended spans to the configured `Exporter` that have not yet
81
+ # been exported.
82
+ #
83
+ # This method should only be called in cases where it is absolutely
84
+ # necessary, such as when using some FaaS providers that may suspend
85
+ # the process after an invocation, but before the `Processor` exports
86
+ # the completed spans.
87
+ def force_flush
88
+ snapshot = lock { spans.shift(spans.size) }
89
+ until snapshot.empty?
90
+ batch = snapshot.shift(@batch_size).map!(&:to_span_data)
91
+ result_code = @exporter.export(batch)
92
+ report_result(result_code, batch)
93
+ end
94
+ end
95
+
75
96
  # shuts the consumer thread down and flushes the current accumulated buffer
76
97
  # will block until the thread is finished
77
98
  def shutdown
@@ -81,7 +102,7 @@ module OpenTelemetry
81
102
  end
82
103
 
83
104
  @thread.join
84
- flush
105
+ force_flush
85
106
  @exporter.shutdown
86
107
  end
87
108
 
@@ -104,35 +125,20 @@ module OpenTelemetry
104
125
  end
105
126
 
106
127
  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
128
+ result_code = export_with_timeout(batch)
114
129
  report_result(result_code, batch)
115
130
  end
116
131
 
117
132
  def export_with_timeout(batch)
118
133
  Timeout.timeout(@exporter_timeout_seconds) { @exporter.export(batch) }
119
134
  rescue Timeout::Error
120
- FAILED_NOT_RETRYABLE
135
+ FAILURE
121
136
  end
122
137
 
123
138
  def report_result(result_code, batch)
124
139
  OpenTelemetry.logger.error("Unable to export #{batch.size} spans") unless result_code == SUCCESS
125
140
  end
126
141
 
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
142
  def fetch_batch
137
143
  spans.shift(@batch_size).map!(&:to_span_data)
138
144
  end