ddtrace 0.46.0 → 0.47.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 (70) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +52 -12
  3. data/.circleci/images/primary/{Dockerfile-jruby-9.2 → Dockerfile-jruby-9.2-latest} +2 -1
  4. data/.circleci/images/primary/Dockerfile-jruby-9.2.0.0 +73 -0
  5. data/.circleci/images/primary/Dockerfile-truffleruby-21.0.0 +73 -0
  6. data/.github/workflows/create-next-milestone.yml +2 -2
  7. data/.rubocop_todo.yml +3 -2
  8. data/.simplecov +6 -0
  9. data/Appraisals +1 -1
  10. data/CHANGELOG.md +83 -1
  11. data/Gemfile +19 -4
  12. data/LICENSE-3rdparty.csv +2 -0
  13. data/docker-compose.yml +75 -7
  14. data/docs/GettingStarted.md +61 -8
  15. data/lib/ddtrace/configuration.rb +92 -23
  16. data/lib/ddtrace/configuration/options.rb +2 -4
  17. data/lib/ddtrace/context_provider.rb +0 -2
  18. data/lib/ddtrace/contrib/active_record/configuration/resolver.rb +97 -26
  19. data/lib/ddtrace/contrib/aws/services.rb +1 -0
  20. data/lib/ddtrace/contrib/configurable.rb +63 -39
  21. data/lib/ddtrace/contrib/configuration/resolver.rb +70 -5
  22. data/lib/ddtrace/contrib/configuration/resolvers/pattern_resolver.rb +19 -17
  23. data/lib/ddtrace/contrib/configuration/settings.rb +7 -6
  24. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +1 -0
  25. data/lib/ddtrace/contrib/extensions.rb +26 -3
  26. data/lib/ddtrace/contrib/httpclient/instrumentation.rb +12 -16
  27. data/lib/ddtrace/contrib/httpclient/patcher.rb +5 -2
  28. data/lib/ddtrace/contrib/httprb/instrumentation.rb +12 -17
  29. data/lib/ddtrace/contrib/httprb/patcher.rb +5 -2
  30. data/lib/ddtrace/contrib/patcher.rb +8 -5
  31. data/lib/ddtrace/contrib/presto/patcher.rb +5 -2
  32. data/lib/ddtrace/contrib/rails/patcher.rb +6 -2
  33. data/lib/ddtrace/contrib/redis/configuration/resolver.rb +11 -4
  34. data/lib/ddtrace/contrib/resque/integration.rb +1 -1
  35. data/lib/ddtrace/ext/runtime.rb +2 -0
  36. data/lib/ddtrace/patcher.rb +23 -1
  37. data/lib/ddtrace/pin.rb +5 -9
  38. data/lib/ddtrace/runtime/cgroup.rb +1 -1
  39. data/lib/ddtrace/runtime/container.rb +25 -27
  40. data/lib/ddtrace/runtime/identity.rb +8 -0
  41. data/lib/ddtrace/sync_writer.rb +5 -2
  42. data/lib/ddtrace/tracer.rb +6 -4
  43. data/lib/ddtrace/transport/http.rb +14 -5
  44. data/lib/ddtrace/transport/http/adapters/net.rb +18 -4
  45. data/lib/ddtrace/transport/http/builder.rb +5 -1
  46. data/lib/ddtrace/transport/http/env.rb +8 -0
  47. data/lib/ddtrace/transport/io/response.rb +1 -3
  48. data/lib/ddtrace/transport/io/traces.rb +6 -0
  49. data/lib/ddtrace/transport/traces.rb +15 -1
  50. data/lib/ddtrace/utils/compression.rb +27 -0
  51. data/lib/ddtrace/utils/object_set.rb +41 -0
  52. data/lib/ddtrace/utils/only_once.rb +40 -0
  53. data/lib/ddtrace/utils/sequence.rb +17 -0
  54. data/lib/ddtrace/utils/string_table.rb +45 -0
  55. data/lib/ddtrace/utils/time.rb +7 -0
  56. data/lib/ddtrace/vendor/multipart-post/LICENSE +11 -0
  57. data/lib/ddtrace/vendor/multipart-post/multipart.rb +12 -0
  58. data/lib/ddtrace/vendor/multipart-post/multipart/post.rb +8 -0
  59. data/lib/ddtrace/vendor/multipart-post/multipart/post/composite_read_io.rb +116 -0
  60. data/lib/ddtrace/vendor/multipart-post/multipart/post/multipartable.rb +57 -0
  61. data/lib/ddtrace/vendor/multipart-post/multipart/post/parts.rb +135 -0
  62. data/lib/ddtrace/vendor/multipart-post/multipart/post/version.rb +9 -0
  63. data/lib/ddtrace/vendor/multipart-post/net/http/post/multipart.rb +32 -0
  64. data/lib/ddtrace/version.rb +1 -1
  65. data/lib/ddtrace/workers/async.rb +3 -3
  66. data/lib/ddtrace/workers/loop.rb +14 -3
  67. data/lib/ddtrace/workers/queue.rb +1 -0
  68. data/lib/ddtrace/workers/trace_writer.rb +1 -0
  69. data/lib/ddtrace/writer.rb +4 -1
  70. metadata +21 -4
@@ -21,7 +21,7 @@ module Datadog
21
21
  filepath = "/proc/#{process}/cgroup"
22
22
 
23
23
  if File.exist?(filepath)
24
- File.open("/proc/#{process}/cgroup").each do |line|
24
+ File.foreach("/proc/#{process}/cgroup") do |line|
25
25
  line = line.strip
26
26
  descriptors << parse(line) unless line.empty?
27
27
  end
@@ -32,39 +32,37 @@ module Datadog
32
32
  end
33
33
 
34
34
  def descriptor
35
- @descriptor ||= begin
36
- Descriptor.new.tap do |descriptor|
37
- begin
38
- Cgroup.descriptors.each do |cgroup_descriptor|
39
- # Parse container data from cgroup descriptor
40
- path = cgroup_descriptor.path
41
- next if path.nil?
35
+ @descriptor ||= Descriptor.new.tap do |descriptor|
36
+ begin
37
+ Cgroup.descriptors.each do |cgroup_descriptor|
38
+ # Parse container data from cgroup descriptor
39
+ path = cgroup_descriptor.path
40
+ next if path.nil?
42
41
 
43
- # Split path into parts
44
- parts = path.split('/')
45
- parts.shift # Remove leading empty part
46
- next if parts.length < 2
42
+ # Split path into parts
43
+ parts = path.split('/')
44
+ parts.shift # Remove leading empty part
45
+ next if parts.length < 2
47
46
 
48
- # Read info from path
49
- platform = parts[0]
50
- container_id = parts[-1][CONTAINER_REGEX]
51
- task_uid = parts[-2][POD_REGEX]
47
+ # Read info from path
48
+ platform = parts[0]
49
+ container_id = parts[-1][CONTAINER_REGEX]
50
+ task_uid = parts[-2][POD_REGEX]
52
51
 
53
- # If container ID wasn't found, ignore.
54
- # Path might describe a non-container environment.
55
- next if container_id.nil?
52
+ # If container ID wasn't found, ignore.
53
+ # Path might describe a non-container environment.
54
+ next if container_id.nil?
56
55
 
57
- descriptor.platform = platform
58
- descriptor.container_id = container_id
59
- descriptor.task_uid = task_uid
56
+ descriptor.platform = platform
57
+ descriptor.container_id = container_id
58
+ descriptor.task_uid = task_uid
60
59
 
61
- break
62
- end
63
- rescue StandardError => e
64
- Datadog.logger.error(
65
- "Error while parsing container info. Cause: #{e.message} Location: #{e.backtrace.first}"
66
- )
60
+ break
67
61
  end
62
+ rescue StandardError => e
63
+ Datadog.logger.error(
64
+ "Error while parsing container info. Cause: #{e.message} Location: #{e.backtrace.first}"
65
+ )
68
66
  end
69
67
  end
70
68
  end
@@ -24,10 +24,18 @@ module Datadog
24
24
  Ext::Runtime::LANG
25
25
  end
26
26
 
27
+ def lang_engine
28
+ Ext::Runtime::LANG_ENGINE
29
+ end
30
+
27
31
  def lang_interpreter
28
32
  Ext::Runtime::LANG_INTERPRETER
29
33
  end
30
34
 
35
+ def lang_platform
36
+ Ext::Runtime::LANG_PLATFORM
37
+ end
38
+
31
39
  def lang_version
32
40
  Ext::Runtime::LANG_VERSION
33
41
  end
@@ -1,6 +1,7 @@
1
1
  require 'ddtrace/ext/net'
2
2
  require 'ddtrace/runtime/socket'
3
3
  require 'ddtrace/runtime/metrics'
4
+ require 'ddtrace/utils/only_once'
4
5
 
5
6
  module Datadog
6
7
  # SyncWriter flushes both services and traces synchronously
@@ -10,6 +11,8 @@ module Datadog
10
11
  # the separate `datadog-lambda` uses it as of February 2021:
11
12
  # <https://github.com/DataDog/datadog-lambda-rb/blob/c15f0f0916c90123416dc44e7d6800ef4a7cfdbf/lib/datadog/lambda.rb#L38>
12
13
  class SyncWriter
14
+ DEPRECATION_WARN_ONLY_ONCE = Datadog::Utils::OnlyOnce.new
15
+
13
16
  attr_reader \
14
17
  :priority_sampler,
15
18
  :transport
@@ -25,10 +28,10 @@ module Datadog
25
28
 
26
29
  def write(trace, services = nil)
27
30
  unless services.nil?
28
- Datadog::Patcher.do_once('SyncWriter#write') do
31
+ DEPRECATION_WARN_ONLY_ONCE.run do
29
32
  Datadog.logger.warn(%(
30
33
  write: Writing services has been deprecated and no longer need to be provided.
31
- write(traces, services) can be updted to write(traces)
34
+ write(traces, services) can be updated to write(traces)
32
35
  ))
33
36
  end
34
37
  end
@@ -10,6 +10,7 @@ require 'ddtrace/runtime/identity'
10
10
  require 'ddtrace/sampler'
11
11
  require 'ddtrace/sampling'
12
12
  require 'ddtrace/correlation'
13
+ require 'ddtrace/utils/only_once'
13
14
 
14
15
  # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
15
16
  module Datadog
@@ -19,6 +20,9 @@ module Datadog
19
20
  # of these function calls and sub-requests would be encapsulated within a single trace.
20
21
  # rubocop:disable Metrics/ClassLength
21
22
  class Tracer
23
+ SERVICES_DEPRECATION_WARN_ONLY_ONCE = Datadog::Utils::OnlyOnce.new
24
+ SET_SERVICE_INFO_DEPRECATION_WARN_ONLY_ONCE = Datadog::Utils::OnlyOnce.new
25
+
22
26
  attr_reader :sampler, :tags, :provider, :context_flush
23
27
  attr_accessor :enabled, :writer
24
28
  attr_writer :default_service
@@ -27,8 +31,7 @@ module Datadog
27
31
  DEFAULT_ON_ERROR = proc { |span, error| span.set_error(error) unless span.nil? }
28
32
 
29
33
  def services
30
- # Only log each deprecation warning once (safeguard against log spam)
31
- Datadog::Patcher.do_once('Tracer#set_service_info') do
34
+ SERVICES_DEPRECATION_WARN_ONLY_ONCE.run do
32
35
  Datadog.logger.warn('services: Usage of Tracer.services has been deprecated')
33
36
  end
34
37
 
@@ -129,8 +132,7 @@ module Datadog
129
132
  #
130
133
  # set_service_info is deprecated, no service information needs to be tracked
131
134
  def set_service_info(service, app, app_type)
132
- # Only log each deprecation warning once (safeguard against log spam)
133
- Datadog::Patcher.do_once('Tracer#set_service_info') do
135
+ SET_SERVICE_INFO_DEPRECATION_WARN_ONLY_ONCE.run do
134
136
  Datadog.logger.warn(%(
135
137
  set_service_info: Usage of set_service_info has been deprecated,
136
138
  service information no longer needs to be reported to the trace agent.
@@ -27,7 +27,7 @@ module Datadog
27
27
  # Pass a block to override any settings.
28
28
  def default(options = {})
29
29
  new do |transport|
30
- transport.adapter :net_http, default_hostname, default_port
30
+ transport.adapter default_adapter, default_hostname, default_port, timeout: 1
31
31
  transport.headers default_headers
32
32
 
33
33
  apis = API.defaults
@@ -39,10 +39,15 @@ module Datadog
39
39
  # Apply any settings given by options
40
40
  unless options.empty?
41
41
  # Change hostname/port
42
- if options.key?(:hostname) || options.key?(:port)
43
- hostname = options.fetch(:hostname, default_hostname)
44
- port = options.fetch(:port, default_port)
45
- transport.adapter :net_http, hostname, port
42
+ if [:hostname, :port, :timeout, :ssl].any? { |key| options.key?(key) }
43
+ hostname = options[:hostname] || default_hostname
44
+ port = options[:port] || default_port
45
+
46
+ adapter_options = { timeout: 1 }
47
+ adapter_options[:timeout] = options[:timeout] if options.key?(:timeout)
48
+ adapter_options[:ssl] = options[:ssl] if options.key?(:ssl)
49
+
50
+ transport.adapter default_adapter, hostname, port, adapter_options
46
51
  end
47
52
 
48
53
  # Change default API
@@ -73,6 +78,10 @@ module Datadog
73
78
  end
74
79
  end
75
80
 
81
+ def default_adapter
82
+ :net_http
83
+ end
84
+
76
85
  def default_hostname
77
86
  return default_url.hostname if default_url
78
87
 
@@ -1,4 +1,5 @@
1
1
  require 'ddtrace/transport/response'
2
+ require 'ddtrace/vendor/multipart-post/net/http/post/multipart'
2
3
 
3
4
  module Datadog
4
5
  module Transport
@@ -9,14 +10,16 @@ module Datadog
9
10
  attr_reader \
10
11
  :hostname,
11
12
  :port,
12
- :timeout
13
+ :timeout,
14
+ :ssl
13
15
 
14
- DEFAULT_TIMEOUT = 1
16
+ DEFAULT_TIMEOUT = 30
15
17
 
16
18
  def initialize(hostname, port, options = {})
17
19
  @hostname = hostname
18
20
  @port = port
19
21
  @timeout = options[:timeout] || DEFAULT_TIMEOUT
22
+ @ssl = options.key?(:ssl) ? options[:ssl] == true : false
20
23
  end
21
24
 
22
25
  def open(&block)
@@ -25,6 +28,7 @@ module Datadog
25
28
  # https://github.com/ruby/ruby/blob/b2d96abb42abbe2e01f010ffc9ac51f0f9a50002/lib/net/http.rb#L614-L618
26
29
  req = ::Net::HTTP.new(hostname, port, nil)
27
30
 
31
+ req.use_ssl = ssl
28
32
  req.open_timeout = req.read_timeout = timeout
29
33
 
30
34
  req.start(&block)
@@ -39,8 +43,18 @@ module Datadog
39
43
  end
40
44
 
41
45
  def post(env)
42
- post = ::Net::HTTP::Post.new(env.path, env.headers)
43
- post.body = env.body
46
+ post = nil
47
+
48
+ if env.form.nil? || env.form.empty?
49
+ post = ::Net::HTTP::Post.new(env.path, env.headers)
50
+ post.body = env.body
51
+ else
52
+ post = ::Datadog::Vendor::Net::HTTP::Post::Multipart.new(
53
+ env.path,
54
+ env.form,
55
+ env.headers
56
+ )
57
+ end
44
58
 
45
59
  # Connect and send the request
46
60
  http_response = open do |http|
@@ -95,7 +95,7 @@ module Datadog
95
95
  api_options[:headers] = @default_headers.merge((api_options[:headers] || {}))
96
96
 
97
97
  # Add API::Instance with all settings
98
- instances[key] = API::Instance.new(
98
+ instances[key] = api_instance_class.new(
99
99
  spec,
100
100
  adapter,
101
101
  api_options
@@ -107,6 +107,10 @@ module Datadog
107
107
  end
108
108
  end
109
109
 
110
+ def api_instance_class
111
+ API::Instance
112
+ end
113
+
110
114
  # Raised when the API key does not match known APIs.
111
115
  class UnknownApiError < StandardError
112
116
  attr_reader :key
@@ -42,6 +42,14 @@ module Datadog
42
42
  def headers=(value)
43
43
  self[:headers] = value
44
44
  end
45
+
46
+ def form
47
+ self[:form] ||= {}
48
+ end
49
+
50
+ def form=(value)
51
+ self[:form] = value
52
+ end
45
53
  end
46
54
  end
47
55
  end
@@ -6,14 +6,12 @@ module Datadog
6
6
  # Response from HTTP transport for traces
7
7
  class Response
8
8
  include Transport::Response
9
- include Transport::Traces::Response
10
9
 
11
10
  attr_reader \
12
11
  :result
13
12
 
14
- def initialize(result, trace_count = 1)
13
+ def initialize(result)
15
14
  @result = result
16
- @trace_count = trace_count
17
15
  end
18
16
 
19
17
  def ok?
@@ -10,6 +10,12 @@ module Datadog
10
10
  module Traces
11
11
  # Response from HTTP transport for traces
12
12
  class Response < IO::Response
13
+ include Transport::Traces::Response
14
+
15
+ def initialize(result, trace_count = 1)
16
+ super(result)
17
+ @trace_count = trace_count
18
+ end
13
19
  end
14
20
 
15
21
  # Extensions for HTTP client
@@ -123,7 +123,21 @@ module Datadog
123
123
  return send_traces(traces)
124
124
  end
125
125
  end
126
- end.force
126
+ end
127
+
128
+ # Force resolution of lazy enumerator.
129
+ #
130
+ # The "correct" method to call here would be `#force`,
131
+ # as this method was created to force the eager loading
132
+ # of a lazy enumerator.
133
+ #
134
+ # Unfortunately, JRuby < 9.2.9.0 erroneously eagerly loads
135
+ # the lazy Enumerator during intermediate steps.
136
+ # This forces us to use `#to_a`, as this method works for both
137
+ # lazy and regular Enumerators.
138
+ # Using `#to_a` can mask the fact that we expect a lazy
139
+ # Enumerator.
140
+ responses = responses.to_a
127
141
 
128
142
  Datadog.health_metrics.transport_chunked(responses.size)
129
143
 
@@ -0,0 +1,27 @@
1
+ require 'zlib'
2
+
3
+ module Datadog
4
+ module Utils
5
+ # Common database-related utility functions.
6
+ module Compression
7
+ module_function
8
+
9
+ def gzip(string, level: nil, strategy: nil)
10
+ sio = StringIO.new
11
+ sio.binmode
12
+ gz = Zlib::GzipWriter.new(sio, level, strategy)
13
+ gz.write(string)
14
+ gz.close
15
+ sio.string
16
+ end
17
+
18
+ def gunzip(string, encoding = ::Encoding::ASCII_8BIT)
19
+ sio = StringIO.new(string)
20
+ gz = Zlib::GzipReader.new(sio, encoding: encoding)
21
+ gz.read
22
+ ensure
23
+ gz && gz.close
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ require 'ddtrace/utils/sequence'
2
+
3
+ module Datadog
4
+ module Utils
5
+ # Acts as a unique dictionary of objects
6
+ class ObjectSet
7
+ # You can provide a block that defines how the key
8
+ # for this message type is resolved.
9
+ def initialize(seed = 0, &block)
10
+ @sequence = Utils::Sequence.new(seed)
11
+ @items = {}
12
+ @key_block = block
13
+ end
14
+
15
+ # Submit an array of arguments that define the message.
16
+ # If they match an existing message, it will return the
17
+ # matching object. If it doesn't match, it will yield to
18
+ # the block with the next ID & args given.
19
+ def fetch(*args, &block)
20
+ key = @key_block ? @key_block.call(*args) : args.hash
21
+ # TODO: Ruby 2.0 doesn't like yielding here... switch when 2.0 is dropped.
22
+ # rubocop:disable Performance/RedundantBlockCall
23
+ @items[key] ||= block.call(@sequence.next, *args)
24
+ # rubocop:enable Performance/RedundantBlockCall
25
+ end
26
+
27
+ def length
28
+ @items.length
29
+ end
30
+
31
+ def objects
32
+ @items.values
33
+ end
34
+
35
+ def freeze
36
+ super
37
+ @items.freeze
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Utils
5
+ # Helper class to execute something only once such as not repeating warning logs, and instrumenting classes
6
+ # only once.
7
+ #
8
+ # Thread-safe when used correctly (e.g. be careful of races when lazily initializing instances of this class).
9
+ #
10
+ # Note: In its current state, this class is not Ractor-safe.
11
+ # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
12
+ # including an alternative implementation that is Ractor-safe once spent.
13
+ class OnlyOnce
14
+ def initialize
15
+ @mutex = Mutex.new
16
+ @ran_once = false
17
+ end
18
+
19
+ def run
20
+ @mutex.synchronize do
21
+ return if @ran_once
22
+
23
+ @ran_once = true
24
+
25
+ yield
26
+ end
27
+ end
28
+
29
+ def ran?
30
+ @mutex.synchronize { @ran_once }
31
+ end
32
+
33
+ private
34
+
35
+ def reset_ran_once_state_for_tests
36
+ @mutex.synchronize { @ran_once = false }
37
+ end
38
+ end
39
+ end
40
+ end