opentelemetry-api 0.16.0 → 0.17.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/lib/opentelemetry.rb +23 -23
  4. data/lib/opentelemetry/baggage.rb +4 -1
  5. data/lib/opentelemetry/baggage/builder.rb +30 -4
  6. data/lib/opentelemetry/baggage/entry.rb +20 -0
  7. data/lib/opentelemetry/baggage/manager.rb +76 -13
  8. data/lib/opentelemetry/baggage/noop_builder.rb +18 -0
  9. data/lib/opentelemetry/baggage/noop_manager.rb +45 -0
  10. data/lib/opentelemetry/baggage/propagation.rb +9 -18
  11. data/lib/opentelemetry/baggage/propagation/text_map_propagator.rb +109 -0
  12. data/lib/opentelemetry/context/propagation.rb +35 -5
  13. data/lib/opentelemetry/context/propagation/composite_text_map_propagator.rb +105 -0
  14. data/lib/opentelemetry/context/propagation/noop_text_map_propagator.rb +51 -0
  15. data/lib/opentelemetry/context/propagation/{propagator.rb → text_map_propagator.rb} +23 -16
  16. data/lib/opentelemetry/internal.rb +17 -0
  17. data/lib/opentelemetry/internal/proxy_tracer.rb +38 -0
  18. data/lib/opentelemetry/internal/proxy_tracer_provider.rb +59 -0
  19. data/lib/opentelemetry/trace/propagation/trace_context.rb +7 -18
  20. data/lib/opentelemetry/trace/propagation/trace_context/text_map_propagator.rb +73 -0
  21. data/lib/opentelemetry/version.rb +1 -1
  22. metadata +16 -21
  23. data/lib/opentelemetry/baggage/propagation/text_map_extractor.rb +0 -57
  24. data/lib/opentelemetry/baggage/propagation/text_map_injector.rb +0 -52
  25. data/lib/opentelemetry/context/propagation/composite_propagator.rb +0 -72
  26. data/lib/opentelemetry/context/propagation/noop_extractor.rb +0 -26
  27. data/lib/opentelemetry/context/propagation/noop_injector.rb +0 -26
  28. data/lib/opentelemetry/instrumentation.rb +0 -15
  29. data/lib/opentelemetry/instrumentation/base.rb +0 -307
  30. data/lib/opentelemetry/instrumentation/registry.rb +0 -86
  31. data/lib/opentelemetry/metrics.rb +0 -16
  32. data/lib/opentelemetry/metrics/handles.rb +0 -44
  33. data/lib/opentelemetry/metrics/instruments.rb +0 -105
  34. data/lib/opentelemetry/metrics/meter.rb +0 -72
  35. data/lib/opentelemetry/metrics/meter_provider.rb +0 -22
  36. data/lib/opentelemetry/trace/propagation/trace_context/text_map_extractor.rb +0 -52
  37. data/lib/opentelemetry/trace/propagation/trace_context/text_map_injector.rb +0 -49
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module Trace
9
+ module Propagation
10
+ module TraceContext
11
+ # Propagates baggage using the W3C Trace Context format
12
+ class TextMapPropagator
13
+ TRACEPARENT_KEY = 'traceparent'
14
+ TRACESTATE_KEY = 'tracestate'
15
+ FIELDS = [TRACEPARENT_KEY, TRACESTATE_KEY].freeze
16
+
17
+ private_constant :TRACEPARENT_KEY, :TRACESTATE_KEY, :FIELDS
18
+
19
+ # Inject trace context into the supplied carrier.
20
+ #
21
+ # @param [Carrier] carrier The mutable carrier to inject trace context into
22
+ # @param [Context] context The context to read trace context from
23
+ # @param [optional Setter] setter If the optional setter is provided, it
24
+ # will be used to write context into the carrier, otherwise the default
25
+ # text map setter will be used.
26
+ def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter)
27
+ span_context = Trace.current_span(context).context
28
+ return unless span_context.valid?
29
+
30
+ setter.set(carrier, TRACEPARENT_KEY, TraceParent.from_span_context(span_context).to_s)
31
+ setter.set(carrier, TRACESTATE_KEY, span_context.tracestate.to_s) unless span_context.tracestate.empty?
32
+ nil
33
+ end
34
+
35
+ # Extract trace context from the supplied carrier.
36
+ # If extraction fails, the original context will be returned
37
+ #
38
+ # @param [Carrier] carrier The carrier to get the header from
39
+ # @param [optional Context] context Context to be updated with the trace context
40
+ # extracted from the carrier. Defaults to +Context.current+.
41
+ # @param [optional Getter] getter If the optional getter is provided, it
42
+ # will be used to read the header from the carrier, otherwise the default
43
+ # text map getter will be used.
44
+ #
45
+ # @return [Context] context updated with extracted baggage, or the original context
46
+ # if extraction fails
47
+ def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
48
+ tp = TraceParent.from_string(getter.get(carrier, TRACEPARENT_KEY))
49
+ tracestate = Tracestate.from_string(getter.get(carrier, TRACESTATE_KEY))
50
+
51
+ span_context = Trace::SpanContext.new(trace_id: tp.trace_id,
52
+ span_id: tp.span_id,
53
+ trace_flags: tp.flags,
54
+ tracestate: tracestate,
55
+ remote: true)
56
+ span = Trace::Span.new(span_context: span_context)
57
+ OpenTelemetry::Trace.context_with_span(span)
58
+ rescue OpenTelemetry::Error
59
+ context
60
+ end
61
+
62
+ # Returns the predefined propagation fields. If your carrier is reused, you
63
+ # should delete the fields returned by this method before calling +inject+.
64
+ #
65
+ # @return [Array<String>] a list of fields that will be used by this propagator.
66
+ def fields
67
+ FIELDS
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,5 +6,5 @@
6
6
 
7
7
  module OpenTelemetry
8
8
  ## Current OpenTelemetry version
9
- VERSION = '0.16.0'
9
+ VERSION = '0.17.0'
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentelemetry-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenTelemetry Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-22 00:00:00.000000000 Z
11
+ date: 2021-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ipsa
@@ -151,36 +151,31 @@ files:
151
151
  - lib/opentelemetry.rb
152
152
  - lib/opentelemetry/baggage.rb
153
153
  - lib/opentelemetry/baggage/builder.rb
154
+ - lib/opentelemetry/baggage/entry.rb
154
155
  - lib/opentelemetry/baggage/manager.rb
156
+ - lib/opentelemetry/baggage/noop_builder.rb
157
+ - lib/opentelemetry/baggage/noop_manager.rb
155
158
  - lib/opentelemetry/baggage/propagation.rb
156
159
  - lib/opentelemetry/baggage/propagation/context_keys.rb
157
- - lib/opentelemetry/baggage/propagation/text_map_extractor.rb
158
- - lib/opentelemetry/baggage/propagation/text_map_injector.rb
160
+ - lib/opentelemetry/baggage/propagation/text_map_propagator.rb
159
161
  - lib/opentelemetry/context.rb
160
162
  - lib/opentelemetry/context/key.rb
161
163
  - lib/opentelemetry/context/propagation.rb
162
- - lib/opentelemetry/context/propagation/composite_propagator.rb
163
- - lib/opentelemetry/context/propagation/noop_extractor.rb
164
- - lib/opentelemetry/context/propagation/noop_injector.rb
165
- - lib/opentelemetry/context/propagation/propagator.rb
164
+ - lib/opentelemetry/context/propagation/composite_text_map_propagator.rb
165
+ - lib/opentelemetry/context/propagation/noop_text_map_propagator.rb
166
166
  - lib/opentelemetry/context/propagation/rack_env_getter.rb
167
167
  - lib/opentelemetry/context/propagation/text_map_getter.rb
168
+ - lib/opentelemetry/context/propagation/text_map_propagator.rb
168
169
  - lib/opentelemetry/context/propagation/text_map_setter.rb
169
170
  - lib/opentelemetry/error.rb
170
- - lib/opentelemetry/instrumentation.rb
171
- - lib/opentelemetry/instrumentation/base.rb
172
- - lib/opentelemetry/instrumentation/registry.rb
173
- - lib/opentelemetry/metrics.rb
174
- - lib/opentelemetry/metrics/handles.rb
175
- - lib/opentelemetry/metrics/instruments.rb
176
- - lib/opentelemetry/metrics/meter.rb
177
- - lib/opentelemetry/metrics/meter_provider.rb
171
+ - lib/opentelemetry/internal.rb
172
+ - lib/opentelemetry/internal/proxy_tracer.rb
173
+ - lib/opentelemetry/internal/proxy_tracer_provider.rb
178
174
  - lib/opentelemetry/trace.rb
179
175
  - lib/opentelemetry/trace/link.rb
180
176
  - lib/opentelemetry/trace/propagation.rb
181
177
  - lib/opentelemetry/trace/propagation/trace_context.rb
182
- - lib/opentelemetry/trace/propagation/trace_context/text_map_extractor.rb
183
- - lib/opentelemetry/trace/propagation/trace_context/text_map_injector.rb
178
+ - lib/opentelemetry/trace/propagation/trace_context/text_map_propagator.rb
184
179
  - lib/opentelemetry/trace/propagation/trace_context/trace_parent.rb
185
180
  - lib/opentelemetry/trace/span.rb
186
181
  - lib/opentelemetry/trace/span_context.rb
@@ -196,10 +191,10 @@ homepage: https://github.com/open-telemetry/opentelemetry-ruby
196
191
  licenses:
197
192
  - Apache-2.0
198
193
  metadata:
199
- changelog_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-api/v0.16.0/file.CHANGELOG.html
194
+ changelog_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-api/v0.17.0/file.CHANGELOG.html
200
195
  source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby/tree/main/api
201
196
  bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby/issues
202
- documentation_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-api/v0.16.0
197
+ documentation_uri: https://open-telemetry.github.io/opentelemetry-ruby/opentelemetry-api/v0.17.0
203
198
  post_install_message:
204
199
  rdoc_options: []
205
200
  require_paths:
@@ -215,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
210
  - !ruby/object:Gem::Version
216
211
  version: '0'
217
212
  requirements: []
218
- rubygems_version: 3.1.4
213
+ rubygems_version: 3.1.6
219
214
  signing_key:
220
215
  specification_version: 4
221
216
  summary: A stats collection and distributed tracing framework
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- require 'cgi'
8
-
9
- module OpenTelemetry
10
- module Baggage
11
- module Propagation
12
- # Extracts baggage from carriers in the W3C Baggage format
13
- class TextMapExtractor
14
- # Returns a new TextMapExtractor that extracts context using the specified
15
- # getter
16
- #
17
- # @param [optional Getter] default_getter The default getter used to read
18
- # headers from a carrier during extract. Defaults to a
19
- # {OpenTelemetry::Context::Propagation::TextMapGetter} instance.
20
- # @return [TextMapExtractor]
21
- def initialize(default_getter = Context::Propagation.text_map_getter)
22
- @default_getter = default_getter
23
- end
24
-
25
- # Extract remote baggage from the supplied carrier.
26
- # If extraction fails, the original context will be returned
27
- #
28
- # @param [Carrier] carrier The carrier to get the header from
29
- # @param [Context] context The context to be updated with extracted baggage
30
- # @param [optional Getter] getter If the optional getter is provided, it
31
- # will be used to read the header from the carrier, otherwise the default
32
- # getter will be used.
33
- # @return [Context] context updated with extracted baggage, or the original context
34
- # if extraction fails
35
- def extract(carrier, context, getter = nil)
36
- getter ||= @default_getter
37
- header = getter.get(carrier, BAGGAGE_KEY)
38
-
39
- entries = header.gsub(/\s/, '').split(',')
40
-
41
- baggage = entries.each_with_object({}) do |entry, memo|
42
- # The ignored variable below holds properties as per the W3C spec.
43
- # OTel is not using them currently, but they might be used for
44
- # metadata in the future
45
- kv, = entry.split(';', 2)
46
- k, v = kv.split('=').map!(&CGI.method(:unescape))
47
- memo[k] = v
48
- end
49
-
50
- context.set_value(ContextKeys.baggage_key, baggage)
51
- rescue StandardError
52
- context
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- require 'cgi'
8
-
9
- module OpenTelemetry
10
- module Baggage
11
- module Propagation
12
- # Injects baggage using the W3C Baggage format
13
- class TextMapInjector
14
- # Returns a new TextMapInjector that injects context using the specified
15
- # setter
16
- #
17
- # @param [optional Setter] default_setter The default setter used to
18
- # write context into a carrier during inject. Defaults to a
19
- # {OpenTelemetry::Context::Propagation::TextMapSetter} instance.
20
- # @return [TextMapInjector]
21
- def initialize(default_setter = Context::Propagation.text_map_setter)
22
- @default_setter = default_setter
23
- end
24
-
25
- # Inject in-process baggage into the supplied carrier.
26
- #
27
- # @param [Carrier] carrier The carrier to inject baggage into
28
- # @param [Context] context The context to read baggage from
29
- # @param [optional Setter] setter If the optional setter is provided, it
30
- # will be used to write context into the carrier, otherwise the default
31
- # setter will be used.
32
- # @return [Object] carrier with injected baggage
33
- def inject(carrier, context, setter = nil)
34
- return carrier unless (baggage = context[ContextKeys.baggage_key]) && !baggage.empty?
35
-
36
- setter ||= @default_setter
37
- setter.set(carrier, BAGGAGE_KEY, encode(baggage))
38
-
39
- carrier
40
- end
41
-
42
- private
43
-
44
- def encode(baggage)
45
- baggage.inject(+'') do |memo, (k, v)|
46
- memo << CGI.escape(k.to_s) << '=' << CGI.escape(v.to_s) << ','
47
- end.chop!
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- module OpenTelemetry
8
- class Context
9
- module Propagation
10
- # A composite propagator composes a list of injectors and extractors into
11
- # single interface exposing inject and extract methods. Injection and
12
- # extraction will preserve the order of the injectors and extractors
13
- # passed in during initialization.
14
- class CompositePropagator
15
- # Returns a Propagator that extracts using the provided extractors
16
- # and injectors.
17
- #
18
- # @param [Array<#inject>] injectors
19
- # @param [Array<#extract>] extractors
20
- def initialize(injectors, extractors)
21
- @injectors = injectors
22
- @extractors = extractors
23
- end
24
-
25
- # Runs injectors in order and returns a carrier. If an injection fails
26
- # a warning will be logged and remaining injectors will be executed.
27
- # Always returns a valid carrier.
28
- #
29
- # @param [Object] carrier A carrier to inject context into
30
- # context into
31
- # @param [optional Context] context Context to be injected into carrier.
32
- # Defaults to +Context.current+
33
- # @param [optional Setter] setter If the optional setter is provided, it
34
- # will be used to write context into the carrier, otherwise the default
35
- # setter will be used.
36
- #
37
- # @return [Object] carrier
38
- def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter)
39
- @injectors.inject(carrier) do |memo, injector|
40
- injector.inject(memo, context, setter)
41
- rescue => e # rubocop:disable Style/RescueStandardError
42
- OpenTelemetry.logger.warn "Error in CompositePropagator#inject #{e.message}"
43
- carrier
44
- end
45
- end
46
-
47
- # Runs extractors in order and returns a Context updated with the
48
- # results of each extraction. If an extraction fails, a warning will be
49
- # logged and remaining extractors will continue to be executed. Always
50
- # returns a valid context.
51
- #
52
- # @param [Object] carrier The carrier to extract context from
53
- # @param [optional Context] context Context to be updated with the state
54
- # extracted from the carrier. Defaults to +Context.current+
55
- # @param [optional Getter] getter If the optional getter is provided, it
56
- # will be used to read the header from the carrier, otherwise the default
57
- # getter will be used.
58
- #
59
- # @return [Context] a new context updated with state extracted from the
60
- # carrier
61
- def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
62
- @extractors.inject(context) do |ctx, extractor|
63
- extractor.extract(carrier, ctx, getter)
64
- rescue => e # rubocop:disable Style/RescueStandardError
65
- OpenTelemetry.logger.warn "Error in CompositePropagator#extract #{e.message}"
66
- ctx
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- module OpenTelemetry
8
- class Context
9
- module Propagation
10
- # A no-op extractor implementation
11
- class NoopExtractor
12
- # Extract a context from the given carrier
13
- #
14
- # @param [Object] carrier The carrier to extract the context from
15
- # @param [Context] context The context to be upated with the extracted
16
- # context
17
- # @param [optional Callable] getter An optional callable that takes a carrier and a key and
18
- # and returns the value associated with the key
19
- # @return [Context]
20
- def extract(carrier, context, getter = nil)
21
- context
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- module OpenTelemetry
8
- class Context
9
- module Propagation
10
- # A no-op injector implementation
11
- class NoopInjector
12
- # Inject the given context into the specified carrier
13
- #
14
- # @param [Object] carrier The carrier to inject the provided context
15
- # into
16
- # @param [Context] context The context to be injected
17
- # @param [optional Callable] setter An optional callable that takes a carrier and a key and
18
- # a value and assigns the key-value pair in the carrier
19
- # @return [Object] carrier
20
- def inject(carrier, context, &setter)
21
- carrier
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- require 'opentelemetry/instrumentation/registry'
8
- require 'opentelemetry/instrumentation/base'
9
-
10
- module OpenTelemetry
11
- # The instrumentation module contains functionality to register and install
12
- # instrumentation
13
- module Instrumentation
14
- end
15
- end
@@ -1,307 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright The OpenTelemetry Authors
4
- #
5
- # SPDX-License-Identifier: Apache-2.0
6
-
7
- module OpenTelemetry
8
- module Instrumentation
9
- # The Base class holds all metadata and configuration for an
10
- # instrumentation. All instrumentation packages should
11
- # include a subclass of +Instrumentation::Base+ that will register
12
- # it with +OpenTelemetry.instrumentation_registry+ and make it available for
13
- # discovery and installation by an SDK.
14
- #
15
- # A typical subclass of Base will provide an install block, a present
16
- # block, and possibly a compatible block. Below is an
17
- # example:
18
- #
19
- # module OpenTelemetry
20
- # module Instrumentation
21
- # module Sinatra
22
- # class Instrumentation < OpenTelemetry::Instrumentation::Base
23
- # install do |config|
24
- # # install instrumentation, either by library hook or applying
25
- # # a monkey patch
26
- # end
27
- #
28
- # # determine if the target library is present
29
- # present do
30
- # defined?(::Sinatra)
31
- # end
32
- #
33
- # # if the target library is present, is it compatible?
34
- # compatible do
35
- # Gem.loaded_specs['sinatra'].version > MIN_VERSION
36
- # end
37
- # end
38
- # end
39
- # end
40
- # end
41
- #
42
- # The instrumentation name and version will be inferred from the namespace of the
43
- # class. In this example, they'd be 'OpenTelemetry::Instrumentation::Sinatra' and
44
- # OpenTelemetry::Instrumentation::Sinatra::VERSION, but can be explicitly set using
45
- # the +instrumentation_name+ and +instrumetation_version+ methods if necessary.
46
- #
47
- # All subclasses of OpenTelemetry::Instrumentation::Base are automatically
48
- # registered with OpenTelemetry.instrumentation_registry which is used by
49
- # SDKs for instrumentation discovery and installation.
50
- #
51
- # Instrumentation libraries can use the instrumentation subclass to easily gain
52
- # a reference to its named tracer. For example:
53
- #
54
- # OpenTelemetry::Instrumentation::Sinatra.instance.tracer
55
- #
56
- # The instrumention class establishes a convention for disabling an instrumentation
57
- # by environment variable and local configuration. An instrumentation disabled
58
- # by environment variable will take precedence over local config. The
59
- # convention for environment variable name is the library name, upcased with
60
- # '::' replaced by underscores, OPENTELEMETRY shortened to OTEL_{LANG}, and '_ENABLED' appended.
61
- # For example: OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED = false.
62
- class Base # rubocop:disable Metrics/ClassLength
63
- class << self
64
- NAME_REGEX = /^(?:(?<namespace>[a-zA-Z0-9_:]+):{2})?(?<classname>[a-zA-Z0-9_]+)$/.freeze
65
- VALIDATORS = {
66
- array: ->(v) { v.is_a?(Array) },
67
- boolean: ->(v) { v == true || v == false }, # rubocop:disable Style/MultipleComparison
68
- callable: ->(v) { v.respond_to?(:call) },
69
- integer: ->(v) { v.is_a?(Integer) },
70
- string: ->(v) { v.is_a?(String) }
71
- }.freeze
72
-
73
- private_constant :NAME_REGEX, :VALIDATORS
74
-
75
- private :new # rubocop:disable Style/AccessModifierDeclarations
76
-
77
- def inherited(subclass)
78
- OpenTelemetry.instrumentation_registry.register(subclass)
79
- end
80
-
81
- # Optionally set the name of this instrumentation. If not
82
- # explicitly set, the name will default to the namespace of the class,
83
- # or the class name if it does not have a namespace. If there is not
84
- # a namespace, or a class name, it will default to 'unknown'.
85
- #
86
- # @param [String] instrumentation_name The full name of the instrumentation package
87
- def instrumentation_name(instrumentation_name = nil)
88
- if instrumentation_name
89
- @instrumentation_name = instrumentation_name
90
- else
91
- @instrumentation_name ||= infer_name || 'unknown'
92
- end
93
- end
94
-
95
- # Optionally set the version of this instrumentation. If not explicitly set,
96
- # the version will default to the VERSION constant under namespace of
97
- # the class, or the VERSION constant under the class name if it does not
98
- # have a namespace. If a VERSION constant cannot be found, it defaults
99
- # to '0.0.0'.
100
- #
101
- # @param [String] instrumentation_version The version of the instrumentation package
102
- def instrumentation_version(instrumentation_version = nil)
103
- if instrumentation_version
104
- @instrumentation_version = instrumentation_version
105
- else
106
- @instrumentation_version ||= infer_version || '0.0.0'
107
- end
108
- end
109
-
110
- # The install block for this instrumentation. This will be where you install
111
- # instrumentation, either by framework hook or applying a monkey patch.
112
- #
113
- # @param [Callable] blk The install block for this instrumentation
114
- # @yieldparam [Hash] config The instrumentation config will be yielded to the
115
- # install block
116
- def install(&blk)
117
- @install_blk = blk
118
- end
119
-
120
- # The present block for this instrumentation. This block is used to detect if
121
- # target library is present on the system. Typically this will involve
122
- # checking to see if the target gem spec was loaded or if expected
123
- # constants from the target library are present.
124
- #
125
- # @param [Callable] blk The present block for this instrumentation
126
- def present(&blk)
127
- @present_blk = blk
128
- end
129
-
130
- # The compatible block for this instrumentation. This check will be run if the
131
- # target library is present to determine if it's compatible. It's not
132
- # required, but a common use case will be to check to target library
133
- # version for compatibility.
134
- #
135
- # @param [Callable] blk The compatibility block for this instrumentation
136
- def compatible(&blk)
137
- @compatible_blk = blk
138
- end
139
-
140
- # The option method is used to define default configuration options
141
- # for the instrumentation library. It requires a name, default value,
142
- # and a validation callable to be provided.
143
- # @param [String] name The name of the configuration option
144
- # @param default The default value to be used, or to used if validation fails
145
- # @param [Callable, Symbol] validate Accepts a callable or a symbol that matches
146
- # a key in the VALIDATORS hash. The supported keys are, :array, :boolean,
147
- # :callable, :integer, :string.
148
- def option(name, default:, validate:)
149
- validate = VALIDATORS[validate] || validate
150
- raise ArgumentError, "validate must be #{VALIDATORS.keys.join(', ')}, or a callable" unless validate.respond_to?(:call)
151
-
152
- @options ||= []
153
- @options << { name: name, default: default, validate: validate }
154
- end
155
-
156
- def instance
157
- @instance ||= new(instrumentation_name, instrumentation_version, install_blk,
158
- present_blk, compatible_blk, options)
159
- end
160
-
161
- private
162
-
163
- attr_reader :install_blk, :present_blk, :compatible_blk, :options
164
-
165
- def infer_name
166
- @inferred_name ||= if (md = name.match(NAME_REGEX)) # rubocop:disable Naming/MemoizedInstanceVariableName
167
- md['namespace'] || md['classname']
168
- end
169
- end
170
-
171
- def infer_version
172
- return unless (inferred_name = infer_name)
173
-
174
- mod = inferred_name.split('::').map(&:to_sym).inject(Object) do |object, const|
175
- object.const_get(const)
176
- end
177
- mod.const_get(:VERSION)
178
- rescue NameError
179
- nil
180
- end
181
- end
182
-
183
- attr_reader :name, :version, :config, :installed, :tracer
184
-
185
- alias installed? installed
186
-
187
- def initialize(name, version, install_blk, present_blk,
188
- compatible_blk, options)
189
- @name = name
190
- @version = version
191
- @install_blk = install_blk
192
- @present_blk = present_blk
193
- @compatible_blk = compatible_blk
194
- @config = {}
195
- @installed = false
196
- @options = options
197
- end
198
-
199
- # Install instrumentation with the given config. The present? and compatible?
200
- # will be run first, and install will return false if either fail. Will
201
- # return true if install was completed successfully.
202
- #
203
- # @param [Hash] config The config for this instrumentation
204
- def install(config = {})
205
- return true if installed?
206
- return false unless installable?(config)
207
-
208
- @config = config_options(config)
209
- instance_exec(@config, &@install_blk)
210
- @tracer ||= OpenTelemetry.tracer_provider.tracer(name, version)
211
- @installed = true
212
- end
213
-
214
- # Whether or not this instrumentation is installable in the current process. Will
215
- # be true when the instrumentation defines an install block, is not disabled
216
- # by environment or config, and the target library present and compatible.
217
- #
218
- # @param [Hash] config The config for this instrumentation
219
- def installable?(config = {})
220
- @install_blk && enabled?(config) && present? && compatible?
221
- end
222
-
223
- # Calls the present block of the Instrumentation subclasses, if no block is provided
224
- # it's assumed the instrumentation is not present
225
- def present?
226
- return false unless @present_blk
227
-
228
- instance_exec(&@present_blk)
229
- end
230
-
231
- # Calls the compatible block of the Instrumentation subclasses, if no block is provided
232
- # it's assumed to be compatible
233
- def compatible?
234
- return true unless @compatible_blk
235
-
236
- instance_exec(&@compatible_blk)
237
- end
238
-
239
- # Whether this instrumentation is enabled. It first checks to see if it's enabled
240
- # by an environment variable and will proceed to check if it's enabled
241
- # by local config, if given.
242
- #
243
- # @param [optional Hash] config The local config
244
- def enabled?(config = nil)
245
- return false unless enabled_by_env_var?
246
- return config[:enabled] if config&.key?(:enabled)
247
-
248
- true
249
- end
250
-
251
- private
252
-
253
- # The config_options method is responsible for validating that the user supplied
254
- # config hash is valid.
255
- # Unknown configuration keys are not included in the final config hash.
256
- # Invalid configuration values are logged, and replaced by the default.
257
- #
258
- # @param [Hash] user_config The user supplied configuration hash
259
- def config_options(user_config) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
260
- @options ||= {}
261
- user_config ||= {}
262
- validated_config = @options.each_with_object({}) do |option, h|
263
- option_name = option[:name]
264
- config_value = user_config[option_name]
265
-
266
- value = if config_value.nil?
267
- option[:default]
268
- elsif option[:validate].call(config_value)
269
- config_value
270
- else
271
- OpenTelemetry.logger.warn(
272
- "Instrumentation #{name} configuration option #{option_name} value=#{config_value} " \
273
- "failed validation, falling back to default value=#{option[:default]}"
274
- )
275
- option[:default]
276
- end
277
-
278
- h[option_name] = value
279
- rescue StandardError => e
280
- OpenTelemetry.handle_error(exception: e, message: "Instrumentation #{name} unexpected configuration error")
281
- h[option_name] = option[:default]
282
- end
283
-
284
- dropped_config_keys = user_config.keys - validated_config.keys
285
- OpenTelemetry.logger.warn("Instrumentation #{name} ignored the following unknown configuration options #{dropped_config_keys}") unless dropped_config_keys.empty?
286
-
287
- validated_config
288
- end
289
-
290
- # Checks to see if this instrumentation is enabled by env var. By convention, the
291
- # environment variable will be the instrumentation name upper cased, with '::'
292
- # replaced by underscores, OPENTELEMETRY shortened to OTEL_{LANG} and _ENABLED appended.
293
- # For example, the, environment variable name for OpenTelemetry::Instrumentation::Sinatra
294
- # will be OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED. A value of 'false' will disable
295
- # the instrumentation, all other values will enable it.
296
- def enabled_by_env_var?
297
- var_name = name.dup.tap do |n|
298
- n.upcase!
299
- n.gsub!('::', '_')
300
- n.gsub!('OPENTELEMETRY_', 'OTEL_RUBY_')
301
- n << '_ENABLED'
302
- end
303
- ENV[var_name] != 'false'
304
- end
305
- end
306
- end
307
- end