datadog 2.21.0 → 2.23.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 +106 -2
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/init.c +5 -0
- data/ext/libdatadog_api/library_config.c +34 -25
- data/ext/libdatadog_api/process_discovery.c +19 -13
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- data/lib/datadog/appsec/assets/blocked.html +8 -0
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
- data/lib/datadog/appsec/event.rb +12 -14
- data/lib/datadog/appsec/metrics/collector.rb +19 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/remote.rb +29 -13
- data/lib/datadog/appsec/response.rb +18 -4
- data/lib/datadog/appsec/security_engine/result.rb +28 -9
- data/lib/datadog/appsec/security_engine/runner.rb +17 -7
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/core/configuration/components.rb +44 -9
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +14 -0
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/environment/ext.rb +6 -0
- data/lib/datadog/core/environment/process.rb +79 -0
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery.rb +4 -2
- data/lib/datadog/core/remote/client/capabilities.rb +7 -0
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/remote/transport/config.rb +2 -10
- data/lib/datadog/core/remote/transport/http/config.rb +9 -9
- data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
- data/lib/datadog/core/remote/transport/http.rb +2 -0
- data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +18 -3
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -2
- data/lib/datadog/core/telemetry/logging.rb +2 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
- data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
- data/lib/datadog/core/transport/http/client.rb +69 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/array.rb +29 -0
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once_successful.rb +6 -2
- data/lib/datadog/core/utils.rb +2 -0
- data/lib/datadog/data_streams/configuration/settings.rb +49 -0
- data/lib/datadog/data_streams/configuration.rb +11 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +509 -0
- data/lib/datadog/data_streams/transport/http/api.rb +33 -0
- data/lib/datadog/data_streams/transport/http/client.rb +21 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
- data/lib/datadog/data_streams/transport/http.rb +41 -0
- data/lib/datadog/data_streams/transport/stats.rb +60 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/component.rb +14 -16
- data/lib/datadog/di/context.rb +70 -0
- data/lib/datadog/di/el/compiler.rb +164 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +29 -0
- data/lib/datadog/di/instrumenter.rb +163 -48
- data/lib/datadog/di/probe.rb +55 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_manager.rb +13 -4
- data/lib/datadog/di/probe_notification_builder.rb +105 -67
- data/lib/datadog/di/proc_responder.rb +32 -0
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/di/transport/diagnostics.rb +2 -2
- data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
- data/lib/datadog/di/transport/http/input.rb +2 -4
- data/lib/datadog/di/transport/http.rb +6 -2
- data/lib/datadog/di/transport/input.rb +64 -4
- data/lib/datadog/open_feature/component.rb +60 -0
- data/lib/datadog/open_feature/configuration.rb +27 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +14 -0
- data/lib/datadog/open_feature/native_evaluator.rb +38 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +141 -0
- data/lib/datadog/open_feature/remote.rb +74 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +72 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
- data/lib/datadog/opentelemetry/metrics.rb +110 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +4 -0
- data/lib/datadog/profiling/tag_builder.rb +36 -3
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +9 -0
- data/lib/datadog/tracing/configuration/settings.rb +77 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +74 -44
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +9 -1
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +3 -5
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +92 -16
- data/ext/libdatadog_api/macos_development.md +0 -26
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/di/transport/http/client.rb +0 -47
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'ext'
|
|
4
|
+
require 'open_feature/sdk'
|
|
5
|
+
|
|
6
|
+
module Datadog
|
|
7
|
+
module OpenFeature
|
|
8
|
+
# OpenFeature feature flagging provider backed by Datadog Remote Configuration.
|
|
9
|
+
#
|
|
10
|
+
# Implementation follows the OpenFeature contract of Provider SDK.
|
|
11
|
+
# For details see:
|
|
12
|
+
# - https://github.com/open-feature/ruby-sdk/blob/v0.4.1/README.md#develop-a-provider
|
|
13
|
+
# - https://github.com/open-feature/ruby-sdk/blob/v0.4.1/lib/open_feature/sdk/provider/no_op_provider.rb
|
|
14
|
+
#
|
|
15
|
+
# In the example below you can see how to configure the OpenFeature SDK
|
|
16
|
+
# https://github.com/open-feature/ruby-sdk to use the Datadog feature flags provider.
|
|
17
|
+
#
|
|
18
|
+
# Example:
|
|
19
|
+
#
|
|
20
|
+
# Make sure to enable Remote Configuration and OpenFeature in the Datadog configuration.
|
|
21
|
+
#
|
|
22
|
+
# ```ruby
|
|
23
|
+
# # FILE: initializers/datadog.rb
|
|
24
|
+
# Datadog.configure do |config|
|
|
25
|
+
# config.remote.enabled = true
|
|
26
|
+
# config.open_feature.enabled = true
|
|
27
|
+
# end
|
|
28
|
+
# ```
|
|
29
|
+
#
|
|
30
|
+
# And configure the OpenFeature SDK to use the Datadog feature flagging provider.
|
|
31
|
+
#
|
|
32
|
+
# ```ruby
|
|
33
|
+
# # FILE: initializers/open_feature.rb
|
|
34
|
+
# require 'open_feature/sdk'
|
|
35
|
+
# require 'datadog/open_feature/provider'
|
|
36
|
+
#
|
|
37
|
+
# OpenFeature::SDK.configure do |config|
|
|
38
|
+
# config.set_provider(Datadog::OpenFeature::Provider.new)
|
|
39
|
+
# end
|
|
40
|
+
# ```
|
|
41
|
+
#
|
|
42
|
+
# Now you can create OpenFeature SDK client and use it to fetch feature flag values.
|
|
43
|
+
#
|
|
44
|
+
# ```ruby
|
|
45
|
+
# client = OpenFeature::SDK.build_client
|
|
46
|
+
# context = OpenFeature::SDK::EvaluationContext.new('email' => 'john.doe@gmail.com')
|
|
47
|
+
#
|
|
48
|
+
# client.fetch_string_value(
|
|
49
|
+
# flag_key: 'banner', default_value: 'Greetings!', evaluation_context: context
|
|
50
|
+
# )
|
|
51
|
+
# # => 'Welcome back!'
|
|
52
|
+
# ```
|
|
53
|
+
class Provider
|
|
54
|
+
NAME = 'Datadog Feature Flagging Provider'
|
|
55
|
+
|
|
56
|
+
attr_reader :metadata
|
|
57
|
+
|
|
58
|
+
def initialize
|
|
59
|
+
@metadata = ::OpenFeature::SDK::Provider::ProviderMetadata.new(name: NAME).freeze
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def init
|
|
63
|
+
# no-op
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def shutdown
|
|
67
|
+
# no-op
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
|
|
71
|
+
evaluate(flag_key, default_value: default_value, expected_type: :boolean, evaluation_context: evaluation_context)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def fetch_string_value(flag_key:, default_value:, evaluation_context: nil)
|
|
75
|
+
evaluate(flag_key, default_value: default_value, expected_type: :string, evaluation_context: evaluation_context)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fetch_number_value(flag_key:, default_value:, evaluation_context: nil)
|
|
79
|
+
evaluate(flag_key, default_value: default_value, expected_type: :number, evaluation_context: evaluation_context)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil)
|
|
83
|
+
evaluate(flag_key, default_value: default_value, expected_type: :integer, evaluation_context: evaluation_context)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def fetch_float_value(flag_key:, default_value:, evaluation_context: nil)
|
|
87
|
+
evaluate(flag_key, default_value: default_value, expected_type: :float, evaluation_context: evaluation_context)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
|
|
91
|
+
evaluate(flag_key, default_value: default_value, expected_type: :object, evaluation_context: evaluation_context)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def evaluate(flag_key, default_value:, expected_type:, evaluation_context:)
|
|
97
|
+
engine = OpenFeature.engine
|
|
98
|
+
return component_not_configured_default(default_value) if engine.nil?
|
|
99
|
+
|
|
100
|
+
result = engine.fetch_value(
|
|
101
|
+
flag_key,
|
|
102
|
+
default_value: default_value,
|
|
103
|
+
expected_type: expected_type,
|
|
104
|
+
evaluation_context: evaluation_context
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if result.error?
|
|
108
|
+
return ::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
109
|
+
value: default_value,
|
|
110
|
+
error_code: result.error_code,
|
|
111
|
+
error_message: result.error_message,
|
|
112
|
+
reason: result.reason
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
117
|
+
value: result.value,
|
|
118
|
+
variant: result.variant,
|
|
119
|
+
reason: result.reason,
|
|
120
|
+
flag_metadata: result.flag_metadata
|
|
121
|
+
)
|
|
122
|
+
rescue => e
|
|
123
|
+
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
124
|
+
value: default_value,
|
|
125
|
+
error_code: Ext::GENERAL,
|
|
126
|
+
error_message: "#{e.class}: #{e.message}",
|
|
127
|
+
reason: Ext::ERROR
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def component_not_configured_default(value)
|
|
132
|
+
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
133
|
+
value: value,
|
|
134
|
+
error_code: Ext::PROVIDER_FATAL,
|
|
135
|
+
error_message: "Datadog's OpenFeature component must be configured",
|
|
136
|
+
reason: Ext::ERROR
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../core/remote/dispatcher'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenFeature
|
|
7
|
+
# This module contains the remote configuration functionality for OpenFeature
|
|
8
|
+
module Remote
|
|
9
|
+
ReadError = Class.new(StandardError)
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
FFE_FLAG_CONFIGURATION_RULES = 1 << 46
|
|
13
|
+
FFE_PRODUCTS = ['FFE_FLAGS'].freeze
|
|
14
|
+
FFE_CAPABILITIES = [FFE_FLAG_CONFIGURATION_RULES].freeze
|
|
15
|
+
|
|
16
|
+
def capabilities
|
|
17
|
+
FFE_CAPABILITIES
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def products
|
|
21
|
+
FFE_PRODUCTS
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def receivers(telemetry)
|
|
25
|
+
matcher = Core::Remote::Dispatcher::Matcher::Product.new(FFE_PRODUCTS)
|
|
26
|
+
receiver = Core::Remote::Dispatcher::Receiver.new(matcher) do |repository, changes|
|
|
27
|
+
engine = OpenFeature.engine
|
|
28
|
+
next unless engine
|
|
29
|
+
|
|
30
|
+
changes.each do |change|
|
|
31
|
+
content = repository[change.path]
|
|
32
|
+
|
|
33
|
+
unless content || change.type == :delete
|
|
34
|
+
next telemetry.error("OpenFeature: Remote Configuration change is not present on #{change.type}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# NOTE: In the current RC implementation we immediately apply the configuration,
|
|
38
|
+
# but that might change if we need to apply patches instead.
|
|
39
|
+
case change.type
|
|
40
|
+
when :insert, :update
|
|
41
|
+
begin
|
|
42
|
+
# @type var content: Core::Remote::Configuration::Content
|
|
43
|
+
engine.reconfigure!(read_content(content))
|
|
44
|
+
content.applied
|
|
45
|
+
rescue ReadError => e
|
|
46
|
+
content.errored("Error reading Remote Configuration content: #{e.message}")
|
|
47
|
+
rescue EvaluationEngine::ReconfigurationError => e
|
|
48
|
+
content.errored("Error applying OpenFeature configuration: #{e.message}")
|
|
49
|
+
end
|
|
50
|
+
when :delete
|
|
51
|
+
# NOTE: For now, we treat deletion as clearing the configuration
|
|
52
|
+
# In a multi-config scenario, we might track configs per path
|
|
53
|
+
engine.reconfigure!(nil)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
[receiver]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def read_content(content)
|
|
64
|
+
data = content.data.read
|
|
65
|
+
content.data.rewind
|
|
66
|
+
|
|
67
|
+
raise ReadError, 'EOF reached' if data.nil?
|
|
68
|
+
|
|
69
|
+
data
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'ext'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenFeature
|
|
7
|
+
# This class is based on the `OpenFeature::SDK::Provider::ResolutionDetails` class
|
|
8
|
+
#
|
|
9
|
+
# See: https://github.com/open-feature/ruby-sdk/blob/v0.4.1/lib/open_feature/sdk/provider/resolution_details.rb
|
|
10
|
+
class ResolutionDetails < Struct.new(
|
|
11
|
+
:value,
|
|
12
|
+
:reason,
|
|
13
|
+
:variant,
|
|
14
|
+
:error_code,
|
|
15
|
+
:error_message,
|
|
16
|
+
:flag_metadata,
|
|
17
|
+
:allocation_key,
|
|
18
|
+
:extra_logging,
|
|
19
|
+
:log?,
|
|
20
|
+
:error?,
|
|
21
|
+
keyword_init: true
|
|
22
|
+
)
|
|
23
|
+
def self.build_error(value:, error_code:, error_message:, reason: Ext::ERROR)
|
|
24
|
+
new(
|
|
25
|
+
value: value,
|
|
26
|
+
error_code: error_code,
|
|
27
|
+
error_message: error_message,
|
|
28
|
+
reason: reason,
|
|
29
|
+
error?: true,
|
|
30
|
+
log?: false
|
|
31
|
+
).freeze
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../core/transport/http'
|
|
4
|
+
require_relative '../core/transport/http/env'
|
|
5
|
+
require_relative '../core/transport/parcel'
|
|
6
|
+
require_relative '../core/transport/request'
|
|
7
|
+
|
|
8
|
+
module Datadog
|
|
9
|
+
module OpenFeature
|
|
10
|
+
module Transport
|
|
11
|
+
class EncodedParcel
|
|
12
|
+
include Core::Transport::Parcel
|
|
13
|
+
|
|
14
|
+
def encode_with(encoder)
|
|
15
|
+
encoder.encode(data)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class HTTP
|
|
20
|
+
class Spec < Core::Transport::HTTP::API::Spec
|
|
21
|
+
def initialize
|
|
22
|
+
@endpoint = Core::Transport::HTTP::API::Endpoint.new(
|
|
23
|
+
:post, '/evp_proxy/v2/api/v2/exposures'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call(env, &block)
|
|
30
|
+
@endpoint.call(env) do |request_env|
|
|
31
|
+
request_env.headers['Content-Type'] = Core::Encoding::JSONEncoder.content_type
|
|
32
|
+
request_env.headers['X-Datadog-EVP-Subdomain'] = 'event-platform-intake'
|
|
33
|
+
request_env.body = env.request.parcel.encode_with(Core::Encoding::JSONEncoder)
|
|
34
|
+
|
|
35
|
+
block.call(request_env)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Instance < Core::Transport::HTTP::API::Instance
|
|
41
|
+
def send_exposures(env)
|
|
42
|
+
@spec.call(env) { |request_env| call(request_env) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.build(agent_settings:, logger:)
|
|
47
|
+
Core::Transport::HTTP.build(
|
|
48
|
+
api_instance_class: HTTP::Instance,
|
|
49
|
+
agent_settings: agent_settings,
|
|
50
|
+
logger: logger
|
|
51
|
+
) { |t| t.api('exposures', HTTP::Spec.new) }.to_transport(self)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(apis, default_api, logger:)
|
|
55
|
+
@api = apis[default_api]
|
|
56
|
+
@logger = logger
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def send_exposures(payload)
|
|
60
|
+
request = Core::Transport::Request.new(EncodedParcel.new(payload))
|
|
61
|
+
@api.send_exposures(Core::Transport::HTTP::Env.new(request))
|
|
62
|
+
rescue => e
|
|
63
|
+
message = "Internal error during request. Cause: #{e.class.name} #{e.message} " \
|
|
64
|
+
"Location: #{Array(e.backtrace).first}"
|
|
65
|
+
@logger.debug(message)
|
|
66
|
+
|
|
67
|
+
Core::Transport::InternalErrorResponse.new(e)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'core/configuration'
|
|
4
|
+
require_relative 'open_feature/configuration'
|
|
5
|
+
|
|
6
|
+
module Datadog
|
|
7
|
+
# A namespace for the OpenFeature component.
|
|
8
|
+
module OpenFeature
|
|
9
|
+
Core::Configuration::Settings.extend(Configuration::Settings)
|
|
10
|
+
|
|
11
|
+
def self.enabled?
|
|
12
|
+
Datadog.configuration.open_feature.enabled
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.engine
|
|
16
|
+
Datadog.send(:components).open_feature&.engine
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../core/configuration/ext'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenTelemetry
|
|
7
|
+
module Configuration
|
|
8
|
+
module Settings
|
|
9
|
+
def self.extended(base)
|
|
10
|
+
base = base.singleton_class unless base.is_a?(Class)
|
|
11
|
+
add_settings!(base)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.normalize_temporality_preference(env_var_name)
|
|
15
|
+
proc do |value|
|
|
16
|
+
if value && value.to_s.downcase != 'delta' && value.to_s.downcase != 'cumulative'
|
|
17
|
+
Datadog.logger.warn("#{env_var_name}=#{value} is not supported. Using delta instead.")
|
|
18
|
+
'delta'
|
|
19
|
+
else
|
|
20
|
+
value
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.normalize_protocol(env_var_name)
|
|
26
|
+
proc do |value|
|
|
27
|
+
if value && value.to_s.downcase != 'http/protobuf'
|
|
28
|
+
Datadog.logger.warn("#{env_var_name}=#{value} is not supported. Using http/protobuf instead.")
|
|
29
|
+
end
|
|
30
|
+
'http/protobuf'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.headers_parser(env_var_name)
|
|
35
|
+
lambda do |value|
|
|
36
|
+
return {} if value.nil? || value.empty?
|
|
37
|
+
|
|
38
|
+
headers = {}
|
|
39
|
+
header_items = value.split(',')
|
|
40
|
+
header_items.each do |key_value|
|
|
41
|
+
key, header_value = key_value.split('=', 2)
|
|
42
|
+
# If header is malformed, return an empty hash
|
|
43
|
+
if key.nil? || header_value.nil?
|
|
44
|
+
Datadog.logger.warn("#{env_var_name} has malformed header: #{key_value.inspect}")
|
|
45
|
+
return {}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
key.strip!
|
|
49
|
+
header_value.strip!
|
|
50
|
+
if key.empty? || header_value.empty?
|
|
51
|
+
Datadog.logger.warn("#{env_var_name} has empty key or value in: #{key_value.inspect}")
|
|
52
|
+
return {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
headers[key] = header_value
|
|
56
|
+
end
|
|
57
|
+
headers
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.add_settings!(base)
|
|
62
|
+
base.class_eval do
|
|
63
|
+
settings :opentelemetry do
|
|
64
|
+
settings :exporter do
|
|
65
|
+
option :protocol do |o|
|
|
66
|
+
o.type :string
|
|
67
|
+
o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_PROTOCOL'))
|
|
68
|
+
o.env 'OTEL_EXPORTER_OTLP_PROTOCOL'
|
|
69
|
+
o.default 'http/protobuf'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
option :timeout_millis do |o|
|
|
73
|
+
o.type :int
|
|
74
|
+
o.env 'OTEL_EXPORTER_OTLP_TIMEOUT'
|
|
75
|
+
o.default 10_000
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
option :headers do |o|
|
|
79
|
+
o.type :hash
|
|
80
|
+
o.env 'OTEL_EXPORTER_OTLP_HEADERS'
|
|
81
|
+
o.default { {} }
|
|
82
|
+
o.env_parser(&Settings.headers_parser('OTEL_EXPORTER_OTLP_HEADERS'))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
option :endpoint do |o|
|
|
86
|
+
o.type :string, nilable: true
|
|
87
|
+
o.env 'OTEL_EXPORTER_OTLP_ENDPOINT'
|
|
88
|
+
o.default nil
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
settings :metrics do
|
|
93
|
+
# Metrics-specific options default to nil to detect unset state.
|
|
94
|
+
# If a metrics-specific env var (e.g., OTEL_EXPORTER_OTLP_METRICS_TIMEOUT) is not set,
|
|
95
|
+
# we fall back to the general OTLP env var (e.g., OTEL_EXPORTER_OTLP_TIMEOUT) per OpenTelemetry spec.
|
|
96
|
+
option :enabled do |o|
|
|
97
|
+
o.type :bool
|
|
98
|
+
o.env 'DD_METRICS_OTEL_ENABLED'
|
|
99
|
+
o.default false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
option :exporter do |o|
|
|
103
|
+
o.type :string
|
|
104
|
+
o.env 'OTEL_METRICS_EXPORTER'
|
|
105
|
+
o.default 'otlp'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
option :export_interval_millis do |o|
|
|
109
|
+
o.type :int
|
|
110
|
+
o.env 'OTEL_METRIC_EXPORT_INTERVAL'
|
|
111
|
+
o.default 10_000
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
option :export_timeout_millis do |o|
|
|
115
|
+
o.type :int
|
|
116
|
+
o.env 'OTEL_METRIC_EXPORT_TIMEOUT'
|
|
117
|
+
o.default 7_500
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
option :temporality_preference do |o|
|
|
121
|
+
o.type :string
|
|
122
|
+
o.env 'OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'
|
|
123
|
+
o.default 'delta'
|
|
124
|
+
o.setter(&Settings.normalize_temporality_preference('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
option :endpoint do |o|
|
|
128
|
+
o.type :string, nilable: true
|
|
129
|
+
o.env 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'
|
|
130
|
+
o.default nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
option :headers do |o|
|
|
134
|
+
o.type :hash, nilable: true
|
|
135
|
+
o.env 'OTEL_EXPORTER_OTLP_METRICS_HEADERS'
|
|
136
|
+
o.default nil
|
|
137
|
+
o.env_parser(&Settings.headers_parser('OTEL_EXPORTER_OTLP_METRICS_HEADERS'))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
option :timeout_millis do |o|
|
|
141
|
+
o.type :int, nilable: true
|
|
142
|
+
o.env 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT'
|
|
143
|
+
o.default nil
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
option :protocol do |o|
|
|
147
|
+
o.type :string, nilable: true
|
|
148
|
+
o.env 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'
|
|
149
|
+
o.default nil
|
|
150
|
+
o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'))
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../core/configuration/ext'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenTelemetry
|
|
7
|
+
class Metrics
|
|
8
|
+
EXPORTER_NONE = 'none'
|
|
9
|
+
|
|
10
|
+
def self.initialize!(components)
|
|
11
|
+
new(components).configure_metrics_sdk
|
|
12
|
+
true
|
|
13
|
+
rescue => exc
|
|
14
|
+
components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc}: #{exc.backtrace.join("\n")}")
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(components)
|
|
19
|
+
@logger = components.logger
|
|
20
|
+
@settings = components.settings
|
|
21
|
+
@agent_host = components.agent_settings.hostname
|
|
22
|
+
@agent_ssl = components.agent_settings.ssl
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def configure_metrics_sdk
|
|
26
|
+
provider = ::OpenTelemetry.meter_provider
|
|
27
|
+
provider.shutdown if provider.is_a?(::OpenTelemetry::SDK::Metrics::MeterProvider)
|
|
28
|
+
|
|
29
|
+
# The OpenTelemetry SDK defaults to cumulative temporality, but Datadog prefers delta temporality.
|
|
30
|
+
# Here is an example of how this config is applied: https://github.com/open-telemetry/opentelemetry-ruby/blob/1933d4c18e5f5e45c53fa9e902e58aa91e85cc38/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb#L14
|
|
31
|
+
if DATADOG_ENV['OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'].nil?
|
|
32
|
+
ENV['OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'] = 'delta' # rubocop:disable CustomCops/EnvUsageCop
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
resource = create_resource
|
|
36
|
+
provider = ::OpenTelemetry::SDK::Metrics::MeterProvider.new(resource: resource)
|
|
37
|
+
configure_metric_reader(provider)
|
|
38
|
+
::OpenTelemetry.meter_provider = provider
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def create_resource
|
|
44
|
+
resource_attributes = {}
|
|
45
|
+
resource_attributes['host.name'] = Datadog::Core::Environment::Socket.hostname if @settings.tracing.report_hostname
|
|
46
|
+
|
|
47
|
+
@settings.tags&.each do |key, value|
|
|
48
|
+
otel_key = case key
|
|
49
|
+
when 'service' then 'service.name'
|
|
50
|
+
when 'env' then 'deployment.environment'
|
|
51
|
+
when 'version' then 'service.version'
|
|
52
|
+
else key
|
|
53
|
+
end
|
|
54
|
+
resource_attributes[otel_key] = value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
resource_attributes['service.name'] = @settings.service_without_fallback || resource_attributes['service.name'] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
|
|
58
|
+
resource_attributes['deployment.environment'] = @settings.env if @settings.env
|
|
59
|
+
resource_attributes['service.version'] = @settings.version if @settings.version
|
|
60
|
+
|
|
61
|
+
::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def configure_metric_reader(provider)
|
|
65
|
+
exporter_name = @settings.opentelemetry.metrics.exporter
|
|
66
|
+
return if exporter_name == EXPORTER_NONE
|
|
67
|
+
|
|
68
|
+
configure_otlp_exporter(provider)
|
|
69
|
+
rescue => e
|
|
70
|
+
@logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def resolve_metrics_endpoint
|
|
74
|
+
metrics_config = @settings.opentelemetry.metrics
|
|
75
|
+
exporter_config = @settings.opentelemetry.exporter
|
|
76
|
+
|
|
77
|
+
return metrics_config.endpoint if metrics_config.endpoint
|
|
78
|
+
return exporter_config.endpoint if exporter_config.endpoint
|
|
79
|
+
"#{@agent_ssl ? "https" : "http"}://#{@agent_host}:4318/v1/metrics"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def configure_otlp_exporter(provider)
|
|
83
|
+
require 'opentelemetry/exporter/otlp_metrics'
|
|
84
|
+
require_relative 'sdk/metrics_exporter'
|
|
85
|
+
|
|
86
|
+
metrics_config = @settings.opentelemetry.metrics
|
|
87
|
+
exporter_config = @settings.opentelemetry.exporter
|
|
88
|
+
timeout = metrics_config.timeout_millis || exporter_config.timeout_millis
|
|
89
|
+
headers = metrics_config.headers || exporter_config.headers || {}
|
|
90
|
+
|
|
91
|
+
protocol = metrics_config.protocol || exporter_config.protocol
|
|
92
|
+
exporter = Datadog::OpenTelemetry::SDK::MetricsExporter.new(
|
|
93
|
+
endpoint: resolve_metrics_endpoint,
|
|
94
|
+
timeout: timeout / 1000.0,
|
|
95
|
+
headers: headers,
|
|
96
|
+
protocol: protocol
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
reader = ::OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(
|
|
100
|
+
exporter: exporter,
|
|
101
|
+
export_interval_millis: metrics_config.export_interval_millis,
|
|
102
|
+
export_timeout_millis: metrics_config.export_timeout_millis
|
|
103
|
+
)
|
|
104
|
+
provider.add_metric_reader(reader)
|
|
105
|
+
rescue LoadError => e
|
|
106
|
+
@logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -30,7 +30,31 @@ module Datadog
|
|
|
30
30
|
[SpanProcessor.new]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
def metrics_configuration_hook
|
|
34
|
+
components = Datadog.send(:components)
|
|
35
|
+
return super unless components.settings.opentelemetry.metrics.enabled
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
require 'opentelemetry-metrics-sdk'
|
|
39
|
+
rescue LoadError => exc
|
|
40
|
+
components.logger.warn("Failed to load OpenTelemetry metrics gems: #{exc.class}: #{exc}")
|
|
41
|
+
return super
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
success = Datadog::OpenTelemetry::Metrics.initialize!(components)
|
|
45
|
+
super unless success
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Prepend to ConfiguratorPatch (not Configurator) so our hook runs first.
|
|
49
|
+
begin
|
|
50
|
+
require 'opentelemetry-metrics-sdk' if defined?(OpenTelemetry::SDK) && !defined?(OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
|
|
51
|
+
rescue LoadError
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if defined?(::OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
|
|
55
|
+
::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.prepend(self) unless ::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.ancestors.include?(self)
|
|
56
|
+
end
|
|
57
|
+
::OpenTelemetry::SDK::Configurator.prepend(self) unless ::OpenTelemetry::SDK::Configurator.ancestors.include?(self)
|
|
34
58
|
end
|
|
35
59
|
end
|
|
36
60
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'opentelemetry/exporter/otlp_metrics'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenTelemetry
|
|
7
|
+
module SDK
|
|
8
|
+
class MetricsExporter < ::OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter
|
|
9
|
+
METRIC_EXPORT_ATTEMPTS = 'otel.metrics_export_attempts'
|
|
10
|
+
METRIC_EXPORT_SUCCESSES = 'otel.metrics_export_successes'
|
|
11
|
+
METRIC_EXPORT_FAILURES = 'otel.metrics_export_failures'
|
|
12
|
+
|
|
13
|
+
def initialize(endpoint:, timeout:, headers:, protocol:)
|
|
14
|
+
super(endpoint: endpoint, timeout: timeout, headers: headers)
|
|
15
|
+
@telemetry_tags = {'protocol' => protocol, 'encoding' => 'protobuf'}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def export(metrics, timeout: nil)
|
|
19
|
+
telemetry&.inc('tracers', METRIC_EXPORT_ATTEMPTS, 1, tags: @telemetry_tags)
|
|
20
|
+
result = super
|
|
21
|
+
metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
|
|
22
|
+
telemetry&.inc('tracers', metric_name, 1, tags: @telemetry_tags)
|
|
23
|
+
result
|
|
24
|
+
rescue => e
|
|
25
|
+
Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e}")
|
|
26
|
+
telemetry&.inc('tracers', METRIC_EXPORT_FAILURES, 1, tags: @telemetry_tags)
|
|
27
|
+
raise
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def telemetry
|
|
33
|
+
Datadog.send(:components).telemetry
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|