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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/opentelemetry.rb +23 -23
- data/lib/opentelemetry/baggage.rb +4 -1
- data/lib/opentelemetry/baggage/builder.rb +30 -4
- data/lib/opentelemetry/baggage/entry.rb +20 -0
- data/lib/opentelemetry/baggage/manager.rb +76 -13
- data/lib/opentelemetry/baggage/noop_builder.rb +18 -0
- data/lib/opentelemetry/baggage/noop_manager.rb +45 -0
- data/lib/opentelemetry/baggage/propagation.rb +9 -18
- data/lib/opentelemetry/baggage/propagation/text_map_propagator.rb +109 -0
- data/lib/opentelemetry/context/propagation.rb +35 -5
- data/lib/opentelemetry/context/propagation/composite_text_map_propagator.rb +105 -0
- data/lib/opentelemetry/context/propagation/noop_text_map_propagator.rb +51 -0
- data/lib/opentelemetry/context/propagation/{propagator.rb → text_map_propagator.rb} +23 -16
- data/lib/opentelemetry/internal.rb +17 -0
- data/lib/opentelemetry/internal/proxy_tracer.rb +38 -0
- data/lib/opentelemetry/internal/proxy_tracer_provider.rb +59 -0
- data/lib/opentelemetry/trace/propagation/trace_context.rb +7 -18
- data/lib/opentelemetry/trace/propagation/trace_context/text_map_propagator.rb +73 -0
- data/lib/opentelemetry/version.rb +1 -1
- metadata +16 -21
- data/lib/opentelemetry/baggage/propagation/text_map_extractor.rb +0 -57
- data/lib/opentelemetry/baggage/propagation/text_map_injector.rb +0 -52
- data/lib/opentelemetry/context/propagation/composite_propagator.rb +0 -72
- data/lib/opentelemetry/context/propagation/noop_extractor.rb +0 -26
- data/lib/opentelemetry/context/propagation/noop_injector.rb +0 -26
- data/lib/opentelemetry/instrumentation.rb +0 -15
- data/lib/opentelemetry/instrumentation/base.rb +0 -307
- data/lib/opentelemetry/instrumentation/registry.rb +0 -86
- data/lib/opentelemetry/metrics.rb +0 -16
- data/lib/opentelemetry/metrics/handles.rb +0 -44
- data/lib/opentelemetry/metrics/instruments.rb +0 -105
- data/lib/opentelemetry/metrics/meter.rb +0 -72
- data/lib/opentelemetry/metrics/meter_provider.rb +0 -22
- data/lib/opentelemetry/trace/propagation/trace_context/text_map_extractor.rb +0 -52
- 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
|
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.
|
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-
|
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/
|
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/
|
163
|
-
- lib/opentelemetry/context/propagation/
|
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/
|
171
|
-
- lib/opentelemetry/
|
172
|
-
- lib/opentelemetry/
|
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/
|
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.
|
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.
|
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.
|
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
|