datadog 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
  6. data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
  9. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
  11. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  12. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  13. data/lib/datadog/appsec/extensions.rb +1 -0
  14. data/lib/datadog/core/configuration/components.rb +6 -3
  15. data/lib/datadog/core/configuration/settings.rb +39 -0
  16. data/lib/datadog/core/configuration.rb +3 -17
  17. data/lib/datadog/core/deprecations.rb +58 -0
  18. data/lib/datadog/core/environment/yjit.rb +5 -0
  19. data/lib/datadog/core/runtime/ext.rb +1 -0
  20. data/lib/datadog/core/runtime/metrics.rb +6 -0
  21. data/lib/datadog/core/telemetry/component.rb +107 -0
  22. data/lib/datadog/core/telemetry/event.rb +100 -25
  23. data/lib/datadog/core/telemetry/ext.rb +2 -0
  24. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  25. data/lib/datadog/core/telemetry/metric.rb +167 -0
  26. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  27. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  28. data/lib/datadog/core/telemetry/request.rb +1 -1
  29. data/lib/datadog/core/telemetry/worker.rb +173 -0
  30. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  31. data/lib/datadog/core.rb +2 -19
  32. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  33. data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
  34. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
  35. data/lib/datadog/profiling/component.rb +18 -1
  36. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  37. data/lib/datadog/profiling.rb +1 -0
  38. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  39. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  40. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  41. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  42. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  43. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  44. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  45. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  46. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  47. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  48. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  49. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  50. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  51. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  52. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  53. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  54. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  55. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  56. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  57. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  58. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  59. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  60. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  61. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  62. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  63. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  64. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  65. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  66. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  67. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  68. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  69. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  70. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
  71. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  72. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  73. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  74. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  75. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  76. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  77. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  78. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  79. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  80. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  81. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  82. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  83. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  84. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  85. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  86. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  87. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  88. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  89. data/lib/datadog/tracing/span_operation.rb +3 -2
  90. data/lib/datadog/tracing/trace_operation.rb +7 -3
  91. data/lib/datadog/tracing/trace_segment.rb +4 -1
  92. data/lib/datadog/tracing/tracer.rb +9 -2
  93. data/lib/datadog/tracing.rb +5 -1
  94. data/lib/datadog/version.rb +2 -2
  95. metadata +22 -9
  96. data/lib/datadog/core/telemetry/client.rb +0 -95
  97. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../notifications/event'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module ActiveSupport
9
+ module Cache
10
+ # Defines basic behaviors for an ActiveSupport event.
11
+ module Event
12
+ def self.included(base)
13
+ base.include(ActiveSupport::Notifications::Event)
14
+ base.extend(ClassMethods)
15
+ end
16
+
17
+ # Class methods for ActiveRecord events.
18
+ module ClassMethods
19
+ def span_options
20
+ {}
21
+ end
22
+
23
+ def configuration
24
+ Datadog.configuration.tracing[:active_support]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ext'
4
+ require_relative '../event'
5
+
6
+ module Datadog
7
+ module Tracing
8
+ module Contrib
9
+ module ActiveSupport
10
+ module Cache
11
+ module Events
12
+ # Defines instrumentation for instantiation.active_record event
13
+ module Cache
14
+ include ActiveSupport::Cache::Event
15
+
16
+ module_function
17
+
18
+ # Acts as this module's initializer.
19
+ def subscribe!
20
+ @cache_backend = {}
21
+ super
22
+ end
23
+
24
+ def event_name
25
+ /\Acache_(?:delete|read|read_multi|write|write_multi)\.active_support\z/
26
+ end
27
+
28
+ def span_name
29
+ Ext::SPAN_CACHE
30
+ end
31
+
32
+ def span_options
33
+ {
34
+ type: Ext::SPAN_TYPE_CACHE
35
+ }
36
+ end
37
+
38
+ # DEV: Look for other uses of `ActiveSupport::Cache::Store#instrument`, to find other useful event keys.
39
+ MAPPING = {
40
+ 'cache_delete.active_support' => { resource: Ext::RESOURCE_CACHE_DELETE },
41
+ 'cache_read.active_support' => { resource: Ext::RESOURCE_CACHE_GET },
42
+ 'cache_read_multi.active_support' => { resource: Ext::RESOURCE_CACHE_MGET, multi_key: true },
43
+ 'cache_write.active_support' => { resource: Ext::RESOURCE_CACHE_SET },
44
+ 'cache_write_multi.active_support' => { resource: Ext::RESOURCE_CACHE_MSET, multi_key: true }
45
+ }.freeze
46
+
47
+ def trace?(event, _payload)
48
+ return false if !Tracing.enabled? || !configuration.enabled
49
+
50
+ # DEV-3.0: Backwards compatibility code for the 2.x gem series.
51
+ # DEV-3.0: See documentation at {Datadog::Tracing::Contrib::ActiveSupport::Cache::Instrumentation}
52
+ # DEV-3.0: for the complete information about this backwards compatibility code.
53
+ case event
54
+ when 'cache_read.active_support'
55
+ !ActiveSupport::Cache::Instrumentation.nested_read?
56
+ when 'cache_read_multi.active_support'
57
+ !ActiveSupport::Cache::Instrumentation.nested_multiread?
58
+ else
59
+ true
60
+ end
61
+ end
62
+
63
+ def on_start(span, event, _id, payload)
64
+ key = payload[:key]
65
+ store = payload[:store]
66
+
67
+ mapping = MAPPING[event]
68
+
69
+ span.service = configuration[:cache_service]
70
+ span.resource = mapping[:resource]
71
+
72
+ span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
73
+ span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CACHE)
74
+
75
+ if span.service != Datadog.configuration.service
76
+ span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
77
+ end
78
+
79
+ span.set_tag(Ext::TAG_CACHE_BACKEND, cache_backend(store))
80
+
81
+ span.set_tag('EVENT', event)
82
+
83
+ set_cache_key(span, key, mapping[:multi_key])
84
+ end
85
+
86
+ def set_cache_key(span, key, multi_key)
87
+ if multi_key
88
+ keys = key.is_a?(Hash) ? key.keys : key # `write`s use Hashes, while `read`s use Arrays
89
+ resolved_key = keys.map { |k| ::ActiveSupport::Cache.expand_cache_key(k) }
90
+ cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
91
+ span.set_tag(Ext::TAG_CACHE_KEY_MULTI, cache_key)
92
+ else
93
+ resolved_key = ::ActiveSupport::Cache.expand_cache_key(key)
94
+ cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
95
+ span.set_tag(Ext::TAG_CACHE_KEY, cache_key)
96
+ end
97
+ end
98
+
99
+ # The name of the `store` is never saved by Rails.
100
+ # ActiveSupport looks up stores by converting a symbol into a 'require' path,
101
+ # then "camelizing" it for a `const_get` call:
102
+ # ```
103
+ # require "active_support/cache/#{store}"
104
+ # ActiveSupport::Cache.const_get(store.to_s.camelize)
105
+ # ```
106
+ # @see https://github.com/rails/rails/blob/261975dbef77731d2c76f907f1076c5132ebc0e4/activesupport/lib/active_support/cache.rb#L139-L149
107
+ #
108
+ # We can reverse engineer
109
+ # the original symbol by converting the class name to snake case:
110
+ # e.g. ActiveSupport::Cache::RedisStore -> active_support/cache/redis_store
111
+ # In this case, `redis_store` is the store name.
112
+ #
113
+ # Because there's no API retrieve only the class name
114
+ # (only `RedisStore`, and not `ActiveSupport::Cache::RedisStore`)
115
+ # the easiest way to retrieve the store symbol is to convert the fully qualified
116
+ # name using the Rails-provided method `#underscore`, which is the reverse of `#camelize`,
117
+ # then extracting the last part of it.
118
+ #
119
+ # Also, this method caches the store name, given this value will be retrieve
120
+ # multiple times and involves string manipulation.
121
+ def cache_backend(store)
122
+ # Cache the backend name to avoid the expensive string manipulation required to calculate it.
123
+ # DEV: We can't store it directly in the `store` object because it is a frozen String.
124
+ if (name = @cache_backend[store])
125
+ return name
126
+ end
127
+
128
+ # DEV: #underscore is available through ActiveSupport, and is
129
+ # DEV: the exact reverse operation to `#camelize`.
130
+ # DEV: #demodulize is available through ActiveSupport, and is
131
+ # DEV: used to remove the module ('*::') part of a constant name.
132
+ name = ::ActiveSupport::Inflector.demodulize(store)
133
+ name = ::ActiveSupport::Inflector.underscore(name)
134
+
135
+ # Despite a given application only ever having 1-3 store types,
136
+ # we limit the size of the `@cache_backend` just in case, because
137
+ # the user can create custom Cache store classes themselves.
138
+ @cache_backend[store] = name if @cache_backend.size < 50
139
+
140
+ name
141
+ end
142
+
143
+ # DEV: There are two possibly interesting fields in the `on_finish` payload:
144
+ # | `:hit` | If this read is a hit |
145
+ # | `:super_operation` | `:fetch` if a read is done with [`fetch`][ActiveSupport::Cache::Store#fetch] |
146
+ # @see https://github.com/rails/rails/blob/b9d6759401c3d50a51e0a7650cb2331f4218d11f/guides/source/active_support_instrumentation.md?plain=1#L528-L529
147
+ # def on_finish(span, event, id, payload)
148
+ # super
149
+ # end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'events/cache'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module ActiveSupport
9
+ module Cache
10
+ # Defines collection of instrumented ActiveSupport events
11
+ module Events
12
+ ALL = [
13
+ Events::Cache,
14
+ ].freeze
15
+
16
+ module_function
17
+
18
+ def all
19
+ self::ALL
20
+ end
21
+
22
+ def subscriptions
23
+ all.collect(&:subscriptions).collect(&:to_a).flatten
24
+ end
25
+
26
+ def subscribe!
27
+ all.each(&:subscribe!)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -9,7 +9,31 @@ module Datadog
9
9
  module Contrib
10
10
  module ActiveSupport
11
11
  module Cache
12
- # Defines instrumentation for ActiveSupport caching
12
+ # DEV-3.0: Backwards compatibility code for the 2.x gem series.
13
+ # DEV-3.0:
14
+ # DEV-3.0: `ActiveSupport::Cache` is now instrumented by subscribing to ActiveSupport::Notifications events.
15
+ # DEV-3.0: The implementation is located at {Datadog::Tracing::Contrib::ActiveSupport::Cache::Events::Cache}.
16
+ # DEV-3.0: The events emitted provide richer introspection points (e.g. events for cache misses on `#fetch`) while
17
+ # DEV-3.0: also ensuring we are using Rails' public API for improved compatibility.
18
+ # DEV-3.0:
19
+ # DEV-3.0: But a few operations holds us back:
20
+ # DEV-3.0: 1. `ActiveSupport::Cache::Store#fetch`:
21
+ # DEV-3.0: This method does not have an event that can produce an equivalent span to today's 2.x implementation.
22
+ # DEV-3.0: In 2.x, `#fetch` produces two separate, *nested* spans: one for the `#read` operation and
23
+ # DEV-3.0: another for the `#write` operation that is called internally by `#fetch` when the cache key needs
24
+ # DEV-3.0: to be populated on a cache miss.
25
+ # DEV-3.0: But the ActiveSupport events emitted by `#fetch` provide two *sibling* events for the`#read` and
26
+ # DEV-3.0: `#write` operations.
27
+ # DEV-3.0: Moving from nested spans to sibling spans would be a breaking change. One notable difference is
28
+ # DEV-3.0: that if the nested `#write` operation fails 2.x, the `#read` span is marked as an error. This would
29
+ # DEV-3.0: not be the case with sibling spans, and would be a very visible change.
30
+ # DEV-3.0: 2. `ActiveSupport::Cache::Store#read_multi` & `ActiveSupport::Cache::Store#fetch_multi`:
31
+ # DEV-3.0: ActiveSupport events were introduced in ActiveSupport 5.2.0 for these methods.
32
+ # DEV-3.0:
33
+ # DEV-3.0: At the end of the day, moving to ActiveSupport events is the better approach, but we have to retain
34
+ # DEV-3.0: this last few monkey patches (and all the supporting code) to avoid a breaking change for now.
35
+ #
36
+ # Defines the deprecate monkey-patch instrumentation for `ActiveSupport::Cache::Store#fetch`
13
37
  module Instrumentation
14
38
  module_function
15
39
 
@@ -43,6 +67,10 @@ module Datadog
43
67
  # In most of the cases, `#fetch()` and `#read()` calls are nested.
44
68
  # Instrument both does not add any value.
45
69
  # This method checks if these two operations are nested.
70
+ #
71
+ # DEV-3.0: We should not have these checks in the 3.x series because ActiveSupport events provide more
72
+ # DEV-3.0: legible nested spans. While using ActiveSupport events, the nested spans actually provide meaningful
73
+ # DEV-3.0: information.
46
74
  def nested_read?
47
75
  current_span = Tracing.active_span
48
76
  current_span && current_span.name == Ext::SPAN_CACHE && current_span.resource == Ext::RESOURCE_CACHE_GET
@@ -107,29 +135,20 @@ module Datadog
107
135
  end
108
136
  end
109
137
 
110
- # Defines instrumentation for ActiveSupport cache reading
111
- module Read
112
- include InstanceMethods
113
-
114
- def read(*args, &block)
115
- return super if Instrumentation.nested_read?
116
-
117
- Instrumentation.trace(Ext::RESOURCE_CACHE_GET, dd_store_name, key: args[0]) { super }
118
- end
119
- end
120
-
121
- # Defines instrumentation for ActiveSupport cache reading of multiple keys
138
+ # Defines the the legacy monkey-patching instrumentation for ActiveSupport cache read_multi
139
+ # DEV-3.0: ActiveSupport::Notifications events were introduced in ActiveSupport 5.2.0 for this method.
140
+ # DEV-3.0: As long as we support ActiveSupport < 5.2.0, we have to keep this method.
122
141
  module ReadMulti
123
142
  include InstanceMethods
124
143
 
125
- def read_multi(*keys, &block)
144
+ def read_multi(*keys, **options, &block)
126
145
  return super if Instrumentation.nested_multiread?
127
146
 
128
147
  Instrumentation.trace(Ext::RESOURCE_CACHE_MGET, dd_store_name, multi_key: keys) { super }
129
148
  end
130
149
  end
131
150
 
132
- # Defines instrumentation for ActiveSupport cache fetching
151
+ # Defines the the legacy monkey-patching instrumentation for ActiveSupport cache fetch
133
152
  module Fetch
134
153
  include InstanceMethods
135
154
 
@@ -140,11 +159,13 @@ module Datadog
140
159
  end
141
160
  end
142
161
 
143
- # Defines instrumentation for ActiveSupport cache fetching of multiple keys
162
+ # Defines the the legacy monkey-patching instrumentation for ActiveSupport cache fetch_multi
163
+ # DEV-3.0: ActiveSupport::Notifications events were introduced in ActiveSupport 5.2.0 for this method.
164
+ # DEV-3.0: As long as we support ActiveSupport < 5.2.0, we have to keep this method.
144
165
  module FetchMulti
145
166
  include InstanceMethods
146
167
 
147
- def fetch_multi(*args, &block)
168
+ def fetch_multi(*args, **options, &block)
148
169
  return super if Instrumentation.nested_multiread?
149
170
 
150
171
  keys = args[-1].instance_of?(Hash) ? args[0..-2] : args
@@ -152,30 +173,13 @@ module Datadog
152
173
  end
153
174
  end
154
175
 
155
- # Defines instrumentation for ActiveSupport cache writing
156
- module Write
157
- include InstanceMethods
158
-
159
- def write(*args, &block)
160
- Instrumentation.trace(Ext::RESOURCE_CACHE_SET, dd_store_name, key: args[0]) { super }
161
- end
162
- end
163
-
164
- # Defines instrumentation for ActiveSupport cache writing of multiple keys
165
- module WriteMulti
166
- include InstanceMethods
167
-
168
- def write_multi(hash, options = nil)
169
- Instrumentation.trace(Ext::RESOURCE_CACHE_MSET, dd_store_name, multi_key: hash.keys) { super }
170
- end
171
- end
172
-
173
- # Defines instrumentation for ActiveSupport cache deleting
174
- module Delete
175
- include InstanceMethods
176
-
177
- def delete(*args, &block)
178
- Instrumentation.trace(Ext::RESOURCE_CACHE_DELETE, dd_store_name, key: args[0]) { super }
176
+ # Backports the payload[:store] key present since Rails 6.1:
177
+ # https://github.com/rails/rails/commit/6fa747f2946ee244b2aab0cd8c3c064f05d950a5
178
+ module Store
179
+ def instrument(operation, key, options = nil)
180
+ polyfill_options = options&.dup || {}
181
+ polyfill_options[:store] = self.class.name
182
+ super(operation, key, polyfill_options)
179
183
  end
180
184
  end
181
185
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../patcher'
4
4
  require_relative 'instrumentation'
5
+ require_relative 'events'
5
6
 
6
7
  module Datadog
7
8
  module Tracing
@@ -19,54 +20,30 @@ module Datadog
19
20
  end
20
21
 
21
22
  def patch
22
- patch_cache_store_read
23
- patch_cache_store_read_multi
24
- patch_cache_store_fetch
25
- patch_cache_store_fetch_multi
26
- patch_cache_store_write
27
- patch_cache_store_write_multi
28
- patch_cache_store_delete
23
+ Events.subscribe!
24
+
25
+ # Backfill the `:store` key in the ActiveSupport event payload for older Rails.
26
+ if Integration.version < Gem::Version.new('6.1.0')
27
+ ::ActiveSupport::Cache::Store.prepend(Cache::Instrumentation::Store)
28
+ end
29
+
30
+ # DEV-3.0: Backwards compatibility code for the 2.x gem series.
31
+ # DEV-3.0: See documentation at {Datadog::Tracing::Contrib::ActiveSupport::Cache::Instrumentation}
32
+ # DEV-3.0: for the complete information about this backwards compatibility code.
33
+ patch_legacy_cache_store
29
34
  end
30
35
 
31
36
  # This method is overwritten by
32
37
  # `datadog/tracing/contrib/active_support/cache/redis.rb`
33
38
  # with more complex behavior.
34
39
  def cache_store_class(meth)
35
- ::ActiveSupport::Cache::Store
36
- end
37
-
38
- def patch_cache_store_read
39
- cache_store_class(:read).prepend(Cache::Instrumentation::Read)
40
- end
41
-
42
- def patch_cache_store_read_multi
43
- cache_store_class(:read_multi).prepend(Cache::Instrumentation::ReadMulti)
44
- end
45
-
46
- def patch_cache_store_fetch
47
- cache_store_class(:fetch).prepend(Cache::Instrumentation::Fetch)
48
- end
49
-
50
- def patch_cache_store_fetch_multi
51
- klass = cache_store_class(:fetch_multi)
52
- return unless klass.public_method_defined?(:fetch_multi)
53
-
54
- klass.prepend(Cache::Instrumentation::FetchMulti)
55
- end
56
-
57
- def patch_cache_store_write
58
- cache_store_class(:write).prepend(Cache::Instrumentation::Write)
59
- end
60
-
61
- def patch_cache_store_write_multi
62
- klass = cache_store_class(:write_multi)
63
- return unless klass.public_method_defined?(:write_multi)
64
-
65
- klass.prepend(Cache::Instrumentation::WriteMulti)
40
+ [::ActiveSupport::Cache::Store]
66
41
  end
67
42
 
68
- def patch_cache_store_delete
69
- cache_store_class(:delete).prepend(Cache::Instrumentation::Delete)
43
+ def patch_legacy_cache_store
44
+ cache_store_class(:read_multi).each { |clazz| clazz.prepend(Cache::Instrumentation::ReadMulti) }
45
+ cache_store_class(:fetch).each { |clazz| clazz.prepend(Cache::Instrumentation::Fetch) }
46
+ cache_store_class(:fetch_multi).each { |clazz| clazz.prepend(Cache::Instrumentation::FetchMulti) }
70
47
  end
71
48
  end
72
49
  end
@@ -30,7 +30,10 @@ module Datadog
30
30
 
31
31
  def cache_store_class(meth)
32
32
  if patch_redis?(meth)
33
- ::ActiveSupport::Cache::RedisStore
33
+ [::ActiveSupport::Cache::RedisStore, ::ActiveSupport::Cache::Store]
34
+ elsif Gem.loaded_specs['redis'] && defined?(::ActiveSupport::Cache::RedisCacheStore) \
35
+ && ::ActiveSupport::Cache::RedisCacheStore.instance_methods(false).include?(meth)
36
+ [::ActiveSupport::Cache::RedisCacheStore, ::ActiveSupport::Cache::Store]
34
37
  else
35
38
  super
36
39
  end
@@ -26,21 +26,25 @@ module Datadog
26
26
  super
27
27
  end
28
28
 
29
- def subscription(span_name = nil, options = nil)
29
+ def subscription(span_name = nil, span_options = nil, on_start: nil, on_finish: nil, trace: nil)
30
30
  super(
31
31
  span_name || self.span_name,
32
- options || span_options,
33
- &method(:process)
32
+ span_options || self.span_options,
33
+ on_start: on_start,
34
+ on_finish: on_finish,
35
+ trace: trace
34
36
  )
35
37
  end
36
38
 
37
- def subscribe(pattern = nil, span_name = nil, options = nil)
39
+ def subscribe(pattern = nil, span_name = nil, span_options = nil)
38
40
  if supported?
39
41
  super(
40
42
  pattern || event_name,
41
43
  span_name || self.span_name,
42
- options || span_options,
43
- &method(:process)
44
+ span_options || self.span_options,
45
+ on_start: method(:on_start),
46
+ on_finish: method(:on_finish),
47
+ trace: method(:trace?)
44
48
  )
45
49
  end
46
50
  end
@@ -62,6 +66,25 @@ module Datadog
62
66
  payload[:exception_object] ||
63
67
  payload[:exception] # Fallback for ActiveSupport < 5.0
64
68
  end
69
+
70
+ def on_start(_span, _event, _id, _payload); end
71
+
72
+ def on_finish(span, _event, _id, payload)
73
+ record_exception(span, payload)
74
+ end
75
+
76
+ def trace?(_event, _payload)
77
+ true
78
+ end
79
+
80
+ def record_exception(span, payload)
81
+ if payload[:exception_object]
82
+ span.set_error(payload[:exception_object])
83
+ elsif payload[:exception]
84
+ # Fallback for ActiveSupport < 5.0
85
+ span.set_error(payload[:exception])
86
+ end
87
+ end
65
88
  end
66
89
  end
67
90
  end
@@ -45,16 +45,28 @@ module Datadog
45
45
  end
46
46
 
47
47
  # Creates a subscription and immediately activates it.
48
- def subscribe(pattern, span_name, options = {}, &block)
49
- subscription(span_name, options, &block).tap do |subscription|
48
+ def subscribe(pattern, span_name, span_options = {}, on_start: nil, on_finish: nil, trace: nil)
49
+ subscription(
50
+ span_name,
51
+ span_options,
52
+ on_start: on_start,
53
+ on_finish: on_finish,
54
+ trace: trace
55
+ ).tap do |subscription|
50
56
  subscription.subscribe(pattern)
51
57
  end
52
58
  end
53
59
 
54
60
  # Creates a subscription without activating it.
55
61
  # Subscription is added to the inheriting class' list of subscriptions.
56
- def subscription(span_name, options = {}, &block)
57
- Subscription.new(span_name, options, &block).tap do |subscription|
62
+ def subscription(span_name, span_options = {}, on_start: nil, on_finish: nil, trace: nil)
63
+ Subscription.new(
64
+ span_name,
65
+ span_options,
66
+ on_start: on_start,
67
+ on_finish: on_finish,
68
+ trace: trace
69
+ ).tap do |subscription|
58
70
  subscriptions << subscription
59
71
  end
60
72
  end
@@ -9,31 +9,34 @@ module Datadog
9
9
  class Subscription
10
10
  attr_accessor \
11
11
  :span_name,
12
- :options
13
-
14
- def initialize(span_name, options, &block)
15
- raise ArgumentError, 'Must be given a block!' unless block
12
+ :span_options
13
+
14
+ # @param span_name [String] the operation name for the span
15
+ # @param span_options [Hash] span_options to pass during span creation
16
+ # @param on_start [Proc] a block to run when the event is fired,
17
+ # might not include all required information in the `payload` argument.
18
+ # @param on_finish [Proc] a block to run when the event has finished processing,
19
+ # possibly including more information in the `payload` argument.
20
+ # @param trace [Proc] whether to trace the event. Defaults to returning `true`.
21
+ def initialize(span_name, span_options, on_start: nil, on_finish: nil, trace: nil)
22
+ raise ArgumentError, 'Must be given either on_start or on_finish' unless on_start || on_finish
16
23
 
17
24
  @span_name = span_name
18
- @options = options
19
- @handler = Handler.new(&block)
25
+ @span_options = span_options
26
+ @on_start = Handler.new(on_start)
27
+ @on_finish = Handler.new(on_finish)
28
+ @trace = trace
20
29
  @callbacks = Callbacks.new
21
30
  end
22
31
 
23
- # ActiveSupport 3.x calls this
24
- def call(name, start, finish, id, payload)
25
- start_span(name, id, payload, start)
26
- finish_span(name, id, payload, finish)
27
- end
28
-
29
- # ActiveSupport 4+ calls this on start
32
+ # Called by ActiveSupport on event start
30
33
  def start(name, id, payload)
31
- start_span(name, id, payload)
34
+ start_span(name, id, payload) if @trace&.call(name, payload)
32
35
  end
33
36
 
34
- # ActiveSupport 4+ calls this on finish
37
+ # Called by ActiveSupport on event finish
35
38
  def finish(name, id, payload)
36
- finish_span(name, id, payload)
39
+ finish_span(name, id, payload) if payload[:datadog_span]
37
40
  end
38
41
 
39
42
  def before_trace(&block)
@@ -69,7 +72,8 @@ module Datadog
69
72
  protected
70
73
 
71
74
  attr_reader \
72
- :handler,
75
+ :on_start,
76
+ :on_finish,
73
77
  :callbacks
74
78
 
75
79
  def start_span(name, id, payload, start = nil)
@@ -77,11 +81,15 @@ module Datadog
77
81
  callbacks.run(name, :before_trace, id, payload, start)
78
82
 
79
83
  # Start a trace
80
- Tracing.trace(@span_name, **@options).tap do |span|
81
- # Start span if time is provided
82
- span.start(start) unless start.nil?
83
- payload[:datadog_span] = span
84
- end
84
+ span = Tracing.trace(@span_name, **@span_options)
85
+
86
+ # Start span if time is provided
87
+ span.start(start) unless start.nil?
88
+ payload[:datadog_span] = span
89
+
90
+ on_start.run(span, name, id, payload)
91
+
92
+ span
85
93
  end
86
94
 
87
95
  def finish_span(name, id, payload, finish = nil)
@@ -90,7 +98,7 @@ module Datadog
90
98
  return nil if span.nil?
91
99
 
92
100
  # Run handler for event
93
- handler.run(span, name, id, payload)
101
+ on_finish.run(span, name, id, payload)
94
102
 
95
103
  # Finish the span
96
104
  span.finish(finish)
@@ -109,21 +117,17 @@ module Datadog
109
117
  class Handler
110
118
  attr_reader :block
111
119
 
112
- def initialize(&block)
120
+ def initialize(block)
113
121
  @block = block
114
122
  end
115
123
 
116
124
  def run(span, name, id, payload)
117
- run!(span, name, id, payload)
125
+ @block.call(span, name, id, payload) if @block
118
126
  rescue StandardError => e
119
127
  Datadog.logger.debug(
120
128
  "ActiveSupport::Notifications handler for '#{name}' failed: #{e.class.name} #{e.message}"
121
129
  )
122
130
  end
123
-
124
- def run!(*args)
125
- @block.call(*args)
126
- end
127
131
  end
128
132
 
129
133
  # Wrapper for subscription callbacks
@@ -9,6 +9,11 @@ module Datadog
9
9
  module Analytics
10
10
  module_function
11
11
 
12
+ # Applies Analytics sampling rate, if applicable for this Contrib::Configuration.
13
+ def set_rate!(span, configuration)
14
+ set_sample_rate(span, configuration[:analytics_sample_rate]) if enabled?(configuration[:analytics_enabled])
15
+ end
16
+
12
17
  # Checks whether analytics should be enabled.
13
18
  # `flag` is a truthy/falsey value that represents a setting on the integration.
14
19
  def enabled?(flag = nil)