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
@@ -14,10 +14,8 @@ module Datadog
14
14
  # Class behavior for a configuration object with options
15
15
  module ClassMethods
16
16
  def options
17
- @options ||= begin
18
- # Allows for class inheritance of option definitions
19
- superclass <= Options ? superclass.options.dup : OptionDefinitionSet.new
20
- end
17
+ # Allows for class inheritance of option definitions
18
+ @options ||= superclass <= Options ? superclass.options.dup : OptionDefinitionSet.new
21
19
  end
22
20
 
23
21
  protected
@@ -55,8 +55,6 @@ module Datadog
55
55
 
56
56
  # Return the thread-local context.
57
57
  def local(thread = Thread.current)
58
- raise ArgumentError, '\'thread\' must be a Thread.' unless thread.is_a?(Thread)
59
-
60
58
  thread[@key] ||= Datadog::Context.new
61
59
  end
62
60
  end
@@ -6,48 +6,119 @@ module Datadog
6
6
  module ActiveRecord
7
7
  module Configuration
8
8
  # Converts Symbols, Strings, and Hashes to a normalized connection settings Hash.
9
+ #
10
+ # When matching using a Hash, these are the valid fields:
11
+ # ```
12
+ # {
13
+ # adapter: ...,
14
+ # host: ...,
15
+ # port: ...,
16
+ # database: ...,
17
+ # username: ...,
18
+ # role: ...,
19
+ # }
20
+ # ```
21
+ #
22
+ # Partial matching is supported: not including certain fields or setting them to `nil`
23
+ # will cause them to matching all values for that field. For example: `database: nil`
24
+ # will match any database, given the remaining fields match.
25
+ #
26
+ # Any fields not listed above are discarded.
27
+ #
28
+ # When more than one configuration could be matched, the last one to match is selected,
29
+ # based on addition order (`#add`).
9
30
  class Resolver < Contrib::Configuration::Resolver
10
- def initialize(configurations = nil)
11
- @configurations = configurations
31
+ def initialize(active_record_configuration = nil)
32
+ super()
33
+
34
+ @active_record_configuration = active_record_configuration
12
35
  end
13
36
 
14
- def resolve(key)
15
- normalize(resolve_connection_key(key).symbolize_keys)
37
+ def active_record_configuration
38
+ @active_record_configuration || ::ActiveRecord::Base.configurations
16
39
  end
17
40
 
18
- def resolve_connection_key(key)
19
- result = connection_resolver.resolve(key)
41
+ def add(matcher, value)
42
+ parsed = parse_matcher(matcher)
20
43
 
21
- if result.respond_to?(:configuration_hash) # Rails >= 6.1
22
- result.configuration_hash
23
- else # Rails < 6.1
24
- result
44
+ # In case of error parsing, don't store `nil` key
45
+ # as it wouldn't be useful for matching configuration
46
+ # hashes in `#resolve`.
47
+ super(parsed, value) if parsed
48
+ end
49
+
50
+ def resolve(db_config)
51
+ active_record_config = resolve_connection_key(db_config).symbolize_keys
52
+
53
+ hash = normalize(active_record_config)
54
+
55
+ # Hashes in Ruby maintain insertion order
56
+ _, config = @configurations.reverse_each.find do |matcher, _|
57
+ matcher.none? do |key, value|
58
+ value != hash[key]
59
+ end
25
60
  end
61
+
62
+ config
63
+ rescue => e
64
+ Datadog.logger.error(
65
+ "Failed to resolve ActiveRecord configuration key #{db_config.inspect}. " \
66
+ "Cause: #{e.message} Source: #{e.backtrace.first}"
67
+ )
68
+
69
+ nil
26
70
  end
27
71
 
28
- def configurations
29
- @configurations || ::ActiveRecord::Base.configurations
72
+ protected
73
+
74
+ def parse_matcher(matcher)
75
+ resolved_pattern = resolve_connection_key(matcher).symbolize_keys
76
+ normalized = normalize(resolved_pattern)
77
+
78
+ # Remove empty fields to allow for partial matching
79
+ normalized.reject! { |_, v| v.nil? }
80
+
81
+ normalized
82
+ rescue => e
83
+ Datadog.logger.error(
84
+ "Failed to resolve ActiveRecord configuration key #{matcher.inspect}. " \
85
+ "Cause: #{e.message} Source: #{e.backtrace.first}"
86
+ )
30
87
  end
31
88
 
32
89
  def connection_resolver
33
- @resolver ||= begin
34
- if defined?(::ActiveRecord::Base.configurations.resolve)
35
- ::ActiveRecord::DatabaseConfigurations.new(configurations)
36
- elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver)
37
- ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configurations)
38
- else
39
- ::Datadog::Vendor::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configurations)
40
- end
90
+ @resolver ||= if defined?(::ActiveRecord::Base.configurations.resolve)
91
+ ::ActiveRecord::DatabaseConfigurations.new(active_record_configuration)
92
+ elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver)
93
+ ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(
94
+ active_record_configuration
95
+ )
96
+ else
97
+ ::Datadog::Vendor::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(
98
+ active_record_configuration
99
+ )
100
+ end
101
+ end
102
+
103
+ def resolve_connection_key(key)
104
+ result = connection_resolver.resolve(key)
105
+
106
+ if result.respond_to?(:configuration_hash) # Rails >= 6.1
107
+ result.configuration_hash
108
+ else # Rails < 6.1
109
+ result
41
110
  end
42
111
  end
43
112
 
44
- def normalize(hash)
113
+ # Extract only fields we'd like to match
114
+ # from the ActiveRecord configuration.
115
+ def normalize(active_record_config)
45
116
  {
46
- adapter: hash[:adapter],
47
- host: hash[:host],
48
- port: hash[:port],
49
- database: hash[:database],
50
- username: hash[:username]
117
+ adapter: active_record_config[:adapter],
118
+ host: active_record_config[:host],
119
+ port: active_record_config[:port],
120
+ database: active_record_config[:database],
121
+ username: active_record_config[:username]
51
122
  }
52
123
  end
53
124
  end
@@ -99,6 +99,7 @@ module Datadog
99
99
  STS
100
100
  SWF
101
101
  ServiceCatalog
102
+ Schemas
102
103
  Shield
103
104
  SimpleDB
104
105
  Snowball
@@ -3,7 +3,11 @@ require 'ddtrace/contrib/configuration/settings'
3
3
 
4
4
  module Datadog
5
5
  module Contrib
6
- # Defines configurable behavior for integrations
6
+ # Defines configurable behavior for integrations.
7
+ #
8
+ # This module is responsible for coordination between
9
+ # the configuration resolver and default configuration
10
+ # fallback.
7
11
  module Configurable
8
12
  def self.included(base)
9
13
  base.send(:include, InstanceMethods)
@@ -11,66 +15,86 @@ module Datadog
11
15
 
12
16
  # Configurable instance behavior for integrations
13
17
  module InstanceMethods
18
+ # Provides a new configuration instance for this integration.
19
+ #
20
+ # This method normally needs to be overridden for each integration
21
+ # as their settings, defaults and environment variables are
22
+ # specific for each integration.
23
+ #
24
+ # DEV(1.0): Rename to `new_configuration`, make it protected.
25
+ # DEV(1.0):
26
+ # DEV(1.0): This method always provides a new instance of the configuration object for
27
+ # DEV(1.0): the current integration, not the currently effective default configuration.
28
+ # DEV(1.0): This is a misnomer of its function.
29
+ # DEV(1.0):
30
+ # DEV(1.0): Unfortunately, change this would be a breaking change for all custom integrations,
31
+ # DEV(1.0): thus we have to be very intentional with the right time to make this change.
32
+ # DEV(1.0): Currently marking this for a 1.0 milestone.
14
33
  def default_configuration
15
34
  Configuration::Settings.new
16
35
  end
17
36
 
18
- def reset_configuration!
19
- @configurations = nil
20
- @resolver = nil
21
- end
37
+ # Get matching configuration by matcher.
38
+ # If no match, returns the default configuration instance.
39
+ def configuration(matcher = :default)
40
+ return default_configuration_instance if matcher == :default
22
41
 
23
- # Get matching configuration for key.
24
- # If no match, returns default configuration.
25
- def configuration(key = :default)
26
- configurations[configuration_key(key)]
42
+ resolver.get(matcher) || default_configuration_instance
27
43
  end
28
44
 
29
- # If the key has matching configuration explicitly defined for it,
30
- # then return true. Otherwise return false.
31
- # Note: a resolver's resolve method should not return a fallback value
32
- # See: https://github.com/DataDog/dd-trace-rb/issues/1204
33
- def configuration_for?(key)
34
- key = resolver.resolve(key) unless key == :default
35
- configurations.key?(key)
45
+ # Resolves the matching configuration for integration-specific value.
46
+ # If no match, returns the default configuration instance.
47
+ def resolve(value)
48
+ return default_configuration_instance if value == :default
49
+
50
+ resolver.resolve(value) || default_configuration_instance
36
51
  end
37
52
 
53
+ # Returns all registered matchers and their respective configurations.
38
54
  def configurations
39
- @configurations ||= {
40
- default: default_configuration
41
- }
55
+ resolver.configurations.merge(default: default_configuration_instance)
42
56
  end
43
57
 
44
- # Create or update configuration with provided settings.
45
- def configure(key, options = {}, &block)
46
- key ||= :default
47
-
48
- # Get or add the configuration
49
- config = configuration_for?(key) ? configuration(key) : add_configuration(key)
58
+ # Create or update configuration associated with `matcher` with
59
+ # the provided `options` and `&block`.
60
+ def configure(matcher = :default, options = {}, &block)
61
+ config = if matcher == :default
62
+ default_configuration_instance
63
+ else
64
+ # Get or add the configuration
65
+ resolver.get(matcher) || resolver.add(matcher, default_configuration)
66
+ end
50
67
 
51
68
  # Apply the settings
52
69
  config.configure(options, &block)
53
70
  config
54
71
  end
55
72
 
56
- protected
57
-
58
- def resolver
59
- @resolver ||= Configuration::Resolver.new
73
+ # Resets all configuration options
74
+ def reset_configuration!
75
+ @resolver = nil
76
+ @default_configuration = nil
60
77
  end
61
78
 
62
- def add_configuration(key)
63
- resolver.add(key)
64
- config_key = resolver.resolve(key)
65
- configurations[config_key] = default_configuration
66
- end
79
+ protected
67
80
 
68
- def configuration_key(key)
69
- return :default if key.nil? || key == :default
81
+ # DEV(1.0): Rename to `default_configuration`, make it public.
82
+ # DEV(1.0): See comment on `default_configuration` for more information.
83
+ def default_configuration_instance
84
+ @default_configuration ||= default_configuration # rubocop:disable Naming/MemoizedInstanceVariableName
85
+ end
70
86
 
71
- key = resolver.resolve(key)
72
- key = :default unless configurations.key?(key)
73
- key
87
+ # Overridable configuration resolver.
88
+ #
89
+ # This resolver is responsible for performing the matching
90
+ # of `#configure(matcher)` `matcher`s with `value`s provided
91
+ # in subsequent calls to `#resolve(value)`.
92
+ #
93
+ # By default, the `value` in `#resolve(value)` must be equal
94
+ # to the `matcher` object provided in `#configure(matcher)`
95
+ # to retrieve the associated configuration.
96
+ def resolver
97
+ @resolver ||= Configuration::Resolver.new
74
98
  end
75
99
  end
76
100
  end
@@ -1,14 +1,79 @@
1
1
  module Datadog
2
2
  module Contrib
3
3
  module Configuration
4
- # Resolves a configuration key to a Datadog::Contrib::Configuration:Settings object
4
+ # Resolves an integration-specific matcher to an associated
5
+ # object.
6
+ #
7
+ # Integrations that perform any configuration matching
8
+ # based on patterns might want to override this class
9
+ # to provide richer matching. For example, match configuration
10
+ # based on: HTTP request parameters, request headers,
11
+ # async queue name.
12
+ #
13
+ # When overriding this class, for simple use cases, only
14
+ # overriding `#parse_matcher` might suffice. See
15
+ # `#parse_matcher`'s documentation for more information.
5
16
  class Resolver
6
- def resolve(key)
7
- key
17
+ attr_reader :configurations
18
+
19
+ def initialize
20
+ @configurations = {}
21
+ end
22
+
23
+ # Adds a new `matcher`, associating with it a `value`.
24
+ #
25
+ # This `value` is returned when `#resolve` is called
26
+ # with a matching value for this matcher. When multiple
27
+ # matchers would match, `#resolve` returns the latest
28
+ # added one.
29
+ #
30
+ # The `matcher` can be transformed internally by the
31
+ # `#parse_matcher` method before being stored.
32
+ #
33
+ # The `value` can also be retrieved by calling `#get`
34
+ # with the same `matcher` added by this method.
35
+ #
36
+ # @param [Object] matcher integration-specific matcher
37
+ # @param [Object] value arbitrary value to be associated with `matcher`
38
+ def add(matcher, value)
39
+ @configurations[parse_matcher(matcher)] = value
40
+ end
41
+
42
+ # Retrieves the stored value for a `matcher`
43
+ # previously stored by `#add`.
44
+ #
45
+ # @param [Object] matcher integration-specific matcher
46
+ # @return [Object] previously stored `value` from `#add`, or `nil` if not found
47
+ def get(matcher)
48
+ @configurations[parse_matcher(matcher)]
49
+ end
50
+
51
+ # Matches an arbitrary value against the configured
52
+ # matchers previously set with `#add`.
53
+ #
54
+ # If multiple matchers would match, returns the latest one.
55
+ #
56
+ # @param [Object] value integration-specific value
57
+ # @return [Object] matching `value` configured at `#add`, or `nil` if none match
58
+ def resolve(value)
59
+ @configurations[value]
8
60
  end
9
61
 
10
- def add(key)
11
- key
62
+ protected
63
+
64
+ # Converts `matcher` into an appropriate key
65
+ # for the internal Hash storage.
66
+ #
67
+ # It's recommended to override this method,
68
+ # instead of the public methods, if the
69
+ # integration can simply convert both
70
+ # `matcher` (provided to `#add`) and `value`
71
+ # (provided to `#resolve`) to the same value.
72
+ #
73
+ # @param [Object] matcher integration-specific matcher
74
+ # @return [Object] processed matcher
75
+ def parse_matcher(matcher)
76
+ matcher
12
77
  end
13
78
  end
14
79
  end
@@ -5,30 +5,32 @@ module Datadog
5
5
  module Configuration
6
6
  # Resolves a value to a configuration key
7
7
  module Resolvers
8
- # Matches strings against Regexps.
9
- class PatternResolver < Datadog::Contrib::Configuration::Resolver
10
- def resolve(name)
11
- return if patterns.empty?
8
+ # Matches Strings and Regexps against `object.to_s` objects
9
+ # and Procs against plain objects.
10
+ class PatternResolver < Contrib::Configuration::Resolver
11
+ def resolve(value)
12
+ return if configurations.empty?
12
13
 
13
14
  # Try to find a matching pattern
14
- patterns.find do |pattern|
15
- if pattern.is_a?(Proc)
16
- (pattern === name)
17
- else
18
- (pattern === name.to_s) ||
19
- (pattern == name) # Only required during configuration time.
20
- end
15
+ _, config = configurations.reverse_each.find do |matcher, _|
16
+ matcher === if matcher.is_a?(Proc)
17
+ value
18
+ else
19
+ value.to_s
20
+ end
21
21
  end
22
- end
23
22
 
24
- def add(pattern)
25
- patterns << (pattern.is_a?(Regexp) || pattern.is_a?(Proc) ? pattern : pattern.to_s)
23
+ config
26
24
  end
27
25
 
28
- private
26
+ protected
29
27
 
30
- def patterns
31
- @patterns ||= Set.new
28
+ def parse_matcher(matcher)
29
+ if matcher.is_a?(Regexp) || matcher.is_a?(Proc)
30
+ matcher
31
+ else
32
+ matcher.to_s
33
+ end
32
34
  end
33
35
  end
34
36
  end