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
@@ -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