ddtrace 0.46.0 → 0.47.0

Sign up to get free protection for your applications and to get access to all the features.
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