datadog 2.11.0 → 2.12.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.
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client'
4
+
5
+ module Datadog
6
+ module DI
7
+ module Transport
8
+ module HTTP
9
+ module Diagnostics
10
+ module Client
11
+ def send_diagnostics_payload(request)
12
+ send_request(request) do |api, env|
13
+ api.send_diagnostics(env)
14
+ end
15
+ end
16
+ end
17
+
18
+ module API
19
+ module Instance
20
+ def send_diagnostics(env)
21
+ raise DiagnosticsNotSupportedError, spec unless spec.is_a?(Diagnostics::API::Spec)
22
+
23
+ spec.send_diagnostics(env) do |request_env|
24
+ call(request_env)
25
+ end
26
+ end
27
+
28
+ class DiagnosticsNotSupportedError < StandardError
29
+ attr_reader :spec
30
+
31
+ def initialize(spec)
32
+ super
33
+
34
+ @spec = spec
35
+ end
36
+
37
+ def message
38
+ 'Diagnostics not supported for this API!'
39
+ end
40
+ end
41
+ end
42
+
43
+ module Spec
44
+ attr_accessor :diagnostics
45
+
46
+ def send_diagnostics(env, &block)
47
+ raise NoDiagnosticsEndpointDefinedError, self if diagnostics.nil?
48
+
49
+ diagnostics.call(env, &block)
50
+ end
51
+
52
+ class NoDiagnosticsEndpointDefinedError < StandardError
53
+ attr_reader :spec
54
+
55
+ def initialize(spec)
56
+ super
57
+
58
+ @spec = spec
59
+ end
60
+
61
+ def message
62
+ 'No diagnostics endpoint is defined for API specification!'
63
+ end
64
+ end
65
+ end
66
+
67
+ # Endpoint for negotiation
68
+ class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
69
+ attr_reader :encoder
70
+
71
+ def initialize(path, encoder)
72
+ super(:post, path)
73
+ @encoder = encoder
74
+ end
75
+
76
+ def call(env, &block)
77
+ event_payload = Core::Vendor::Multipart::Post::UploadIO.new(
78
+ StringIO.new(env.request.parcel.data), 'application/json', 'event.json'
79
+ )
80
+ env.form = {'event' => event_payload}
81
+
82
+ super(env, &block)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ HTTP::Client.include(Diagnostics::Client)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client'
4
+
5
+ module Datadog
6
+ module DI
7
+ module Transport
8
+ module HTTP
9
+ module Input
10
+ module Client
11
+ def send_input_payload(request)
12
+ send_request(request) do |api, env|
13
+ api.send_input(env)
14
+ end
15
+ end
16
+ end
17
+
18
+ module API
19
+ module Instance
20
+ def send_input(env)
21
+ raise InputNotSupportedError, spec unless spec.is_a?(Input::API::Spec)
22
+
23
+ spec.send_input(env) do |request_env|
24
+ call(request_env)
25
+ end
26
+ end
27
+
28
+ class InputNotSupportedError < StandardError
29
+ attr_reader :spec
30
+
31
+ def initialize(spec)
32
+ super
33
+
34
+ @spec = spec
35
+ end
36
+
37
+ def message
38
+ 'Input not supported for this API!'
39
+ end
40
+ end
41
+ end
42
+
43
+ module Spec
44
+ attr_accessor :input
45
+
46
+ def send_input(env, &block)
47
+ raise NoInputEndpointDefinedError, self if input.nil?
48
+
49
+ input.call(env, &block)
50
+ end
51
+
52
+ class NoInputEndpointDefinedError < StandardError
53
+ attr_reader :spec
54
+
55
+ def initialize(spec)
56
+ super
57
+
58
+ @spec = spec
59
+ end
60
+
61
+ def message
62
+ 'No input endpoint is defined for API specification!'
63
+ end
64
+ end
65
+ end
66
+
67
+ # Endpoint for negotiation
68
+ class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
69
+ HEADER_CONTENT_TYPE = 'Content-Type'
70
+
71
+ attr_reader \
72
+ :encoder
73
+
74
+ def initialize(path, encoder)
75
+ super(:post, path)
76
+ @encoder = encoder
77
+ end
78
+
79
+ def call(env, &block)
80
+ # Encode body & type
81
+ env.headers[HEADER_CONTENT_TYPE] = encoder.content_type
82
+ env.body = env.request.parcel.data
83
+
84
+ super(env, &block)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ HTTP::Client.include(Input::Client)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ require_relative '../../core/environment/container'
6
+ require_relative '../../core/environment/ext'
7
+ require_relative '../../core/transport/ext'
8
+ require_relative '../../core/transport/http/adapters/net'
9
+ require_relative '../../core/transport/http/adapters/test'
10
+ require_relative '../../core/transport/http/adapters/unix_socket'
11
+ require_relative 'diagnostics'
12
+ require_relative 'input'
13
+ require_relative 'http/api'
14
+ require_relative '../../core/transport/http/builder'
15
+ require_relative '../../../datadog/version'
16
+
17
+ module Datadog
18
+ module DI
19
+ module Transport
20
+ # Namespace for HTTP transport components
21
+ module HTTP
22
+ module_function
23
+
24
+ # Builds a new Transport::HTTP::Client
25
+ def new(klass, &block)
26
+ Core::Transport::HTTP::Builder.new(
27
+ api_instance_class: API::Instance, &block
28
+ ).to_transport(klass)
29
+ end
30
+
31
+ # Builds a new Transport::HTTP::Client with default settings
32
+ # Pass a block to override any settings.
33
+ def diagnostics(
34
+ agent_settings:,
35
+ **options
36
+ )
37
+ new(DI::Transport::Diagnostics::Transport) do |transport|
38
+ transport.adapter(agent_settings)
39
+ transport.headers default_headers
40
+
41
+ apis = API.defaults
42
+
43
+ transport.api API::DIAGNOSTICS, apis[API::DIAGNOSTICS]
44
+
45
+ # Apply any settings given by options
46
+ unless options.empty?
47
+ transport.default_api = options[:api_version] if options.key?(:api_version)
48
+ transport.headers options[:headers] if options.key?(:headers)
49
+ end
50
+
51
+ # Call block to apply any customization, if provided
52
+ yield(transport) if block_given?
53
+ end
54
+ end
55
+
56
+ # Builds a new Transport::HTTP::Client with default settings
57
+ # Pass a block to override any settings.
58
+ def input(
59
+ agent_settings:,
60
+ **options
61
+ )
62
+ new(DI::Transport::Input::Transport) do |transport|
63
+ transport.adapter(agent_settings)
64
+ transport.headers default_headers
65
+
66
+ apis = API.defaults
67
+
68
+ transport.api API::INPUT, apis[API::INPUT]
69
+
70
+ # Apply any settings given by options
71
+ unless options.empty?
72
+ transport.default_api = options[:api_version] if options.key?(:api_version)
73
+ transport.headers options[:headers] if options.key?(:headers)
74
+ end
75
+
76
+ # Call block to apply any customization, if provided
77
+ yield(transport) if block_given?
78
+ end
79
+ end
80
+
81
+ def default_headers
82
+ {
83
+ Datadog::Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_TOP_LEVEL => '1',
84
+ Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG => Datadog::Core::Environment::Ext::LANG,
85
+ Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_VERSION => Datadog::Core::Environment::Ext::LANG_VERSION,
86
+ Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER =>
87
+ Datadog::Core::Environment::Ext::LANG_INTERPRETER,
88
+ Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER_VENDOR => Core::Environment::Ext::LANG_ENGINE,
89
+ Datadog::Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION =>
90
+ Datadog::Core::Environment::Ext::GEM_DATADOG_VERSION
91
+ }.tap do |headers|
92
+ # Add container ID, if present.
93
+ container_id = Datadog::Core::Environment::Container.container_id
94
+ headers[Datadog::Core::Transport::Ext::HTTP::HEADER_CONTAINER_ID] = container_id unless container_id.nil?
95
+ # Pretend that stats computation are already done by the client
96
+ if Datadog.configuration.appsec.standalone.enabled
97
+ headers[Datadog::Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_STATS] = 'yes'
98
+ end
99
+ end
100
+ end
101
+
102
+ def default_adapter
103
+ Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
104
+ end
105
+
106
+ # Add adapters to registry
107
+ Datadog::Core::Transport::HTTP::Builder::REGISTRY.set(
108
+ Datadog::Core::Transport::HTTP::Adapters::Net,
109
+ Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
110
+ )
111
+ Datadog::Core::Transport::HTTP::Builder::REGISTRY.set(Datadog::Core::Transport::HTTP::Adapters::Test, Datadog::Core::Transport::Ext::Test::ADAPTER)
112
+ Datadog::Core::Transport::HTTP::Builder::REGISTRY.set(
113
+ Datadog::Core::Transport::HTTP::Adapters::UnixSocket,
114
+ Datadog::Core::Transport::Ext::UnixSocket::ADAPTER
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/transport/parcel'
4
+ require_relative 'http/client'
5
+
6
+ module Datadog
7
+ module DI
8
+ module Transport
9
+ module Input
10
+ class EncodedParcel
11
+ include Datadog::Core::Transport::Parcel
12
+ end
13
+
14
+ class Request < Datadog::Core::Transport::Request
15
+ end
16
+
17
+ class Transport
18
+ attr_reader :client, :apis, :default_api, :current_api_id
19
+
20
+ def initialize(apis, default_api)
21
+ @apis = apis
22
+
23
+ @client = HTTP::Client.new(current_api)
24
+ end
25
+
26
+ def current_api
27
+ @apis[HTTP::API::INPUT]
28
+ end
29
+
30
+ def send_input(payload)
31
+ json = JSON.dump(payload)
32
+ parcel = EncodedParcel.new(json)
33
+ request = Request.new(parcel)
34
+
35
+ response = @client.send_input_payload(request)
36
+ unless response.ok?
37
+ # TODO Datadog::Core::Transport::InternalErrorResponse
38
+ # does not have +code+ method, what is the actual API of
39
+ # these response objects?
40
+ raise Error::AgentCommunicationError, "send_input failed: #{begin
41
+ response.code
42
+ rescue
43
+ "???"
44
+ end}: #{response.payload}"
45
+ end
46
+ rescue Error::AgentCommunicationError
47
+ raise
48
+ # Datadog::Core::Transport does not perform any exception mapping,
49
+ # therefore we could have any exception here from failure to parse
50
+ # agent URI for example.
51
+ # If we ever implement retries for network errors, we should distinguish
52
+ # actual network errors from non-network errors that are raised by
53
+ # transport code.
54
+ rescue => exc
55
+ raise Error::AgentCommunicationError, "send_input failed: #{exc.class}: #{exc}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
data/lib/datadog/di.rb CHANGED
@@ -16,7 +16,8 @@ require_relative 'di/probe_notifier_worker'
16
16
  require_relative 'di/redactor'
17
17
  require_relative 'di/remote'
18
18
  require_relative 'di/serializer'
19
- require_relative 'di/transport'
19
+ #require_relative 'di/transport'
20
+ require_relative 'di/transport/http'
20
21
  require_relative 'di/utils'
21
22
 
22
23
  module Datadog
@@ -49,6 +49,7 @@ module Datadog
49
49
  Tracing::Tracer.new(
50
50
  default_service: settings.service,
51
51
  enabled: settings.tracing.enabled,
52
+ logger: logger,
52
53
  trace_flush: trace_flush,
53
54
  sampler: sampler_delegator,
54
55
  span_sampler: build_span_sampler(settings),
@@ -17,6 +17,7 @@ module Datadog
17
17
  # @public_api
18
18
  class SyncWriter
19
19
  attr_reader \
20
+ :logger,
20
21
  :events,
21
22
  :transport
22
23
 
@@ -25,7 +26,9 @@ module Datadog
25
26
  # @param [Hash<Symbol,Object>] transport_options options for the default transport instance.
26
27
  # @param [Datadog::Tracing::Configuration::AgentSettingsResolver::AgentSettings] agent_settings agent options for
27
28
  # the default transport instance.
28
- def initialize(transport: nil, transport_options: {}, agent_settings: nil)
29
+ def initialize(transport: nil, transport_options: {}, agent_settings: nil, logger: Datadog.logger)
30
+ @logger = logger
31
+
29
32
  @transport = transport || begin
30
33
  transport_options[:agent_settings] = agent_settings if agent_settings
31
34
  Transport::HTTP.default(**transport_options)
@@ -40,7 +43,7 @@ module Datadog
40
43
  def write(trace)
41
44
  flush_trace(trace)
42
45
  rescue => e
43
- Datadog.logger.debug(e)
46
+ logger.debug(e)
44
47
  end
45
48
 
46
49
  # Does nothing.
@@ -28,7 +28,8 @@ module Datadog
28
28
  :provider,
29
29
  :sampler,
30
30
  :span_sampler,
31
- :tags
31
+ :tags,
32
+ :logger
32
33
 
33
34
  attr_accessor \
34
35
  :default_service,
@@ -52,17 +53,19 @@ module Datadog
52
53
  context_provider: DefaultContextProvider.new,
53
54
  default_service: Core::Environment::Ext::FALLBACK_SERVICE_NAME,
54
55
  enabled: true,
56
+ logger: Datadog.logger,
55
57
  sampler: Sampling::PrioritySampler.new(
56
58
  base_sampler: Sampling::AllSampler.new,
57
59
  post_sampler: Sampling::RuleSampler.new
58
60
  ),
59
61
  span_sampler: Sampling::Span::Sampler.new,
60
62
  tags: {},
61
- writer: Writer.new
63
+ writer: Writer.new(logger: logger)
62
64
  )
63
65
  @trace_flush = trace_flush
64
66
  @default_service = default_service
65
67
  @enabled = enabled
68
+ @logger = logger
66
69
  @provider = context_provider
67
70
  @sampler = sampler
68
71
  @span_sampler = span_sampler
@@ -146,7 +149,7 @@ module Datadog
146
149
  active_trace
147
150
  end
148
151
  rescue StandardError => e
149
- Datadog.logger.debug { "Failed to trace: #{e}" }
152
+ logger.debug { "Failed to trace: #{e}" }
150
153
 
151
154
  # Tracing failed: fallback and run code without tracing.
152
155
  return skip_trace(name, &block)
@@ -268,7 +271,7 @@ module Datadog
268
271
  @sampler.sample!(trace_op)
269
272
  rescue StandardError => e
270
273
  SAMPLE_TRACE_LOG_ONLY_ONCE.run do
271
- Datadog.logger.warn { "Failed to sample trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
274
+ logger.warn { "Failed to sample trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
272
275
  end
273
276
  end
274
277
  end
@@ -488,7 +491,7 @@ module Datadog
488
491
  @span_sampler.sample!(trace_op, span)
489
492
  rescue StandardError => e
490
493
  SAMPLE_SPAN_LOG_ONLY_ONCE.run do
491
- Datadog.logger.warn { "Failed to sample span: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
494
+ logger.warn { "Failed to sample span: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
492
495
  end
493
496
  end
494
497
  end
@@ -504,7 +507,7 @@ module Datadog
504
507
  write(trace) if trace && !trace.empty?
505
508
  rescue StandardError => e
506
509
  FLUSH_TRACE_LOG_ONLY_ONCE.run do
507
- Datadog.logger.warn { "Failed to flush trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
510
+ logger.warn { "Failed to flush trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
508
511
  end
509
512
  end
510
513
  end
@@ -518,7 +521,7 @@ module Datadog
518
521
  return unless trace && @writer
519
522
 
520
523
  if Datadog.configuration.diagnostics.debug
521
- Datadog.logger.debug { "Writing #{trace.length} spans (enabled: #{@enabled})\n#{trace.spans.pretty_inspect}" }
524
+ logger.debug { "Writing #{trace.length} spans (enabled: #{@enabled})\n#{trace.spans.pretty_inspect}" }
522
525
  end
523
526
 
524
527
  @writer.write(trace)
@@ -17,10 +17,13 @@ module Datadog
17
17
  # Writes traces to transport synchronously
18
18
  class TraceWriter < Core::Worker
19
19
  attr_reader \
20
+ :logger,
20
21
  :transport
21
22
 
22
23
  # rubocop:disable Lint/MissingSuper
23
24
  def initialize(options = {})
25
+ @logger = options[:logger] || Datadog.logger
26
+
24
27
  transport_options = options.fetch(:transport_options, {})
25
28
 
26
29
  transport_options[:agent_settings] = options[:agent_settings] if options.key?(:agent_settings)
@@ -43,7 +46,7 @@ module Datadog
43
46
  traces = process_traces(traces)
44
47
  flush_traces(traces)
45
48
  rescue StandardError => e
46
- Datadog.logger.warn(
49
+ logger.warn(
47
50
  "Error while writing traces: dropped #{traces.length} items. Cause: #{e} Location: #{Array(e.backtrace).first}"
48
51
  )
49
52
  end
@@ -18,8 +18,7 @@ module Datadog
18
18
  BACK_OFF_MAX = 5
19
19
  DEFAULT_SHUTDOWN_TIMEOUT = 1
20
20
 
21
- attr_reader \
22
- :trace_buffer
21
+ attr_reader :trace_buffer, :logger
23
22
 
24
23
  def initialize(options = {})
25
24
  @transport = options[:transport]
@@ -42,6 +41,8 @@ module Datadog
42
41
  @mutex = Mutex.new
43
42
  @worker = nil
44
43
  @run = false
44
+
45
+ @logger = options.fetch(:logger)
45
46
  end
46
47
 
47
48
  # Callback function that process traces and executes the +send_traces()+ method.
@@ -56,7 +57,7 @@ module Datadog
56
57
  # ensures that the thread will not die because of an exception.
57
58
  # TODO[manu]: findout the reason and reschedule the send if it's not
58
59
  # a fatal exception
59
- Datadog.logger.warn(
60
+ logger.warn(
60
61
  "Error during traces flush: dropped #{traces.length} items. Cause: #{e} Location: #{Array(e.backtrace).first}"
61
62
  )
62
63
  end
@@ -68,7 +69,7 @@ module Datadog
68
69
  return if @run
69
70
 
70
71
  @run = true
71
- Datadog.logger.debug { "Starting thread for: #{self}" }
72
+ logger.debug { "Starting thread for: #{self}" }
72
73
  @worker = Thread.new { perform }
73
74
  @worker.name = self.class.name
74
75
  @worker.thread_variable_set(:fork_safe, true)
@@ -13,11 +13,14 @@ module Datadog
13
13
  # @public_api
14
14
  class Writer
15
15
  attr_reader \
16
+ :logger,
16
17
  :transport,
17
18
  :worker,
18
19
  :events
19
20
 
20
21
  def initialize(options = {})
22
+ @logger = options[:logger] || Datadog.logger
23
+
21
24
  # writer and transport parameters
22
25
  @buff_size = options.fetch(:buffer_size, Workers::AsyncTransport::DEFAULT_BUFFER_MAX_SIZE)
23
26
  @flush_interval = options.fetch(:flush_interval, Workers::AsyncTransport::DEFAULT_FLUSH_INTERVAL)
@@ -119,7 +122,7 @@ module Datadog
119
122
  if worker_local
120
123
  worker_local.enqueue_trace(trace)
121
124
  elsif !@stopped
122
- Datadog.logger.debug('Writer either failed to start or was stopped before #write could complete')
125
+ logger.debug('Writer either failed to start or was stopped before #write could complete')
123
126
  end
124
127
  end
125
128
 
@@ -160,7 +163,8 @@ module Datadog
160
163
  buffer_size: @buff_size,
161
164
  on_trace: @trace_handler,
162
165
  interval: @flush_interval,
163
- shutdown_timeout: @shutdown_timeout
166
+ shutdown_timeout: @shutdown_timeout,
167
+ logger: logger,
164
168
  )
165
169
 
166
170
  @worker.start
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 11
6
+ MINOR = 12
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.0
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-24 00:00:00.000000000 Z
11
+ date: 2025-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -210,6 +210,9 @@ files:
210
210
  - lib/datadog/appsec/contrib/rails/patcher.rb
211
211
  - lib/datadog/appsec/contrib/rails/request.rb
212
212
  - lib/datadog/appsec/contrib/rails/request_middleware.rb
213
+ - lib/datadog/appsec/contrib/rest_client/integration.rb
214
+ - lib/datadog/appsec/contrib/rest_client/patcher.rb
215
+ - lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb
213
216
  - lib/datadog/appsec/contrib/sinatra/framework.rb
214
217
  - lib/datadog/appsec/contrib/sinatra/gateway/request.rb
215
218
  - lib/datadog/appsec/contrib/sinatra/gateway/route_params.rb
@@ -403,7 +406,13 @@ files:
403
406
  - lib/datadog/di/redactor.rb
404
407
  - lib/datadog/di/remote.rb
405
408
  - lib/datadog/di/serializer.rb
406
- - lib/datadog/di/transport.rb
409
+ - lib/datadog/di/transport/diagnostics.rb
410
+ - lib/datadog/di/transport/http.rb
411
+ - lib/datadog/di/transport/http/api.rb
412
+ - lib/datadog/di/transport/http/client.rb
413
+ - lib/datadog/di/transport/http/diagnostics.rb
414
+ - lib/datadog/di/transport/http/input.rb
415
+ - lib/datadog/di/transport/input.rb
407
416
  - lib/datadog/di/utils.rb
408
417
  - lib/datadog/kit.rb
409
418
  - lib/datadog/kit/appsec/events.rb
@@ -920,8 +929,8 @@ licenses:
920
929
  - Apache-2.0
921
930
  metadata:
922
931
  allowed_push_host: https://rubygems.org
923
- changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.11.0/CHANGELOG.md
924
- source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.11.0
932
+ changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.12.0/CHANGELOG.md
933
+ source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.12.0
925
934
  post_install_message:
926
935
  rdoc_options: []
927
936
  require_paths: