datadog 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -2
  3. data/README.md +1 -1
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
  5. data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
  7. data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
  8. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  9. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
  10. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  11. data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
  12. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  13. data/lib/datadog/appsec/configuration/settings.rb +5 -0
  14. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -1
  15. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  16. data/lib/datadog/appsec/extensions.rb +1 -0
  17. data/lib/datadog/core/configuration/components.rb +6 -3
  18. data/lib/datadog/core/configuration/ext.rb +1 -0
  19. data/lib/datadog/core/configuration/option.rb +21 -14
  20. data/lib/datadog/core/configuration/options.rb +5 -1
  21. data/lib/datadog/core/configuration/settings.rb +68 -5
  22. data/lib/datadog/core/configuration.rb +3 -17
  23. data/lib/datadog/core/deprecations.rb +58 -0
  24. data/lib/datadog/core/environment/ext.rb +2 -0
  25. data/lib/datadog/core/environment/yjit.rb +5 -0
  26. data/lib/datadog/core/runtime/ext.rb +2 -0
  27. data/lib/datadog/core/runtime/metrics.rb +6 -0
  28. data/lib/datadog/core/telemetry/component.rb +107 -0
  29. data/lib/datadog/core/telemetry/event.rb +124 -31
  30. data/lib/datadog/core/telemetry/ext.rb +2 -0
  31. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  32. data/lib/datadog/core/telemetry/metric.rb +167 -0
  33. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  34. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  35. data/lib/datadog/core/telemetry/request.rb +1 -1
  36. data/lib/datadog/core/telemetry/worker.rb +173 -0
  37. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  38. data/lib/datadog/core.rb +2 -19
  39. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  40. data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
  41. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
  42. data/lib/datadog/profiling/component.rb +18 -1
  43. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  44. data/lib/datadog/profiling.rb +1 -0
  45. data/lib/datadog/tracing/configuration/ext.rb +7 -0
  46. data/lib/datadog/tracing/configuration/settings.rb +52 -3
  47. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  48. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  49. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  50. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  51. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  52. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  53. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  54. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  55. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  56. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  57. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  58. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  59. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  60. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  61. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  62. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  63. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  64. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  65. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  66. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  67. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  68. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  69. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  70. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  71. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  72. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  73. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  74. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  75. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  76. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  77. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  78. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  79. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
  80. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  81. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  82. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  83. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  84. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  85. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  86. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  87. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  88. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  89. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  90. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  91. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  92. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  93. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  94. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  95. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  96. data/lib/datadog/tracing/distributed/propagation.rb +39 -4
  97. data/lib/datadog/tracing/distributed/trace_context.rb +5 -3
  98. data/lib/datadog/tracing/metadata/ext.rb +1 -0
  99. data/lib/datadog/tracing/span_operation.rb +3 -2
  100. data/lib/datadog/tracing/trace_operation.rb +7 -3
  101. data/lib/datadog/tracing/trace_segment.rb +4 -1
  102. data/lib/datadog/tracing/tracer.rb +9 -2
  103. data/lib/datadog/tracing.rb +5 -1
  104. data/lib/datadog/version.rb +2 -2
  105. metadata +21 -8
  106. data/lib/datadog/core/telemetry/client.rb +0 -95
  107. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -27,9 +27,11 @@ module Datadog
27
27
  Datadog.configuration.tracing[:action_mailer]
28
28
  end
29
29
 
30
- def process(span, event, _id, payload)
30
+ def on_start(span, event, _id, payload)
31
+ super
32
+
33
+ span.type = span_type
31
34
  span.service = configuration[:service_name] if configuration[:service_name]
32
- span.resource = payload[:mailer]
33
35
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
34
36
 
35
37
  # Set analytics sample rate
@@ -39,10 +41,6 @@ module Datadog
39
41
 
40
42
  # Measure service stats
41
43
  Contrib::Analytics.set_measured(span)
42
-
43
- report_if_exception(span, payload)
44
- rescue StandardError => e
45
- Datadog.logger.debug(e.message)
46
44
  end
47
45
  end
48
46
  end
@@ -30,15 +30,20 @@ module Datadog
30
30
  Tracing::Metadata::Ext::AppTypes::TYPE_WORKER
31
31
  end
32
32
 
33
- def process(span, event, _id, payload)
33
+ def on_start(span, event, _id, payload)
34
34
  super
35
35
 
36
- span.type = span_type
36
+ span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_DELIVER)
37
+ end
38
+
39
+ def on_finish(span, event, _id, payload)
40
+ super
41
+
42
+ span.resource = payload[:mailer] # Mailer is not available at `on_start`
43
+
37
44
  span.set_tag(Ext::TAG_MAILER, payload[:mailer])
38
45
  span.set_tag(Ext::TAG_MSG_ID, payload[:message_id])
39
46
 
40
- span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_DELIVER)
41
-
42
47
  # Since email data can contain PII we disable by default
43
48
  # Some of these fields can be either strings or arrays, so we try to normalize
44
49
  # https://github.com/rails/rails/blob/18707ab17fa492eb25ad2e8f9818a320dc20b823/actionmailer/lib/action_mailer/base.rb#L742-L754
@@ -30,10 +30,11 @@ module Datadog
30
30
  Tracing::Metadata::Ext::HTTP::TYPE_TEMPLATE
31
31
  end
32
32
 
33
- def process(span, event, _id, payload)
33
+ def on_start(span, event, _id, payload)
34
34
  super
35
35
 
36
- span.type = span_type
36
+ span.resource = payload[:mailer] # Mailer is not available at `on_start`
37
+
37
38
  span.set_tag(Ext::TAG_ACTION, payload[:action])
38
39
  span.set_tag(Ext::TAG_MAILER, payload[:mailer])
39
40
 
@@ -27,7 +27,7 @@ module Datadog
27
27
  Ext::SPAN_RENDER_PARTIAL
28
28
  end
29
29
 
30
- def process(span, _event, _id, payload)
30
+ def on_start(span, _event, _id, payload)
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.type = Tracing::Metadata::Ext::HTTP::TYPE_TEMPLATE
33
33
 
@@ -41,10 +41,6 @@ module Datadog
41
41
 
42
42
  # Measure service stats
43
43
  Contrib::Analytics.set_measured(span)
44
-
45
- record_exception(span, payload)
46
- rescue StandardError => e
47
- Datadog.logger.debug(e.message)
48
44
  end
49
45
  end
50
46
  end
@@ -27,7 +27,7 @@ module Datadog
27
27
  Ext::SPAN_RENDER_TEMPLATE
28
28
  end
29
29
 
30
- def process(span, _event, _id, payload)
30
+ def on_start(span, _event, _id, payload)
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.type = Tracing::Metadata::Ext::HTTP::TYPE_TEMPLATE
33
33
 
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_DISCARD
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_ENQUEUE
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_ENQUEUE
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_ENQUEUE_RETRY
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_PERFORM
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -26,7 +26,7 @@ module Datadog
26
26
  Ext::SPAN_RETRY_STOPPED
27
27
  end
28
28
 
29
- def process(span, event, _id, payload)
29
+ def on_start(span, event, _id, payload)
30
30
  span.name = span_name
31
31
  span.service = configuration[:service_name] if configuration[:service_name]
32
32
  span.resource = payload[:job].class.name
@@ -30,7 +30,7 @@ module Datadog
30
30
  Ext::SPAN_RENDER
31
31
  end
32
32
 
33
- def process(span, _event, _id, payload)
33
+ def on_start(span, _event, _id, payload)
34
34
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_RENDER)
35
35
 
36
36
  set_common_tags(span, payload)
@@ -32,7 +32,7 @@ module Datadog
32
32
  Ext::SPAN_SERIALIZE
33
33
  end
34
34
 
35
- def process(span, _event, _id, payload)
35
+ def on_start(span, _event, _id, payload)
36
36
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_SERIALIZE)
37
37
 
38
38
  set_common_tags(span, payload)
@@ -31,7 +31,7 @@ module Datadog
31
31
  Ext::SPAN_INSTANTIATION
32
32
  end
33
33
 
34
- def process(span, event, _id, payload)
34
+ def on_start(span, event, _id, payload)
35
35
  span.resource = payload.fetch(:class_name)
36
36
  span.type = Ext::SPAN_TYPE_INSTANTIATION
37
37
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -29,7 +29,7 @@ module Datadog
29
29
  Ext::SPAN_SQL
30
30
  end
31
31
 
32
- def process(span, event, _id, payload)
32
+ def on_start(span, event, _id, payload)
33
33
  config = Utils.connection_config(payload[:connection], payload[:connection_id])
34
34
  settings = Datadog.configuration.tracing[:active_record, config]
35
35
  adapter_name = Contrib::Utils::Database.normalize_vendor(config[:adapter])
@@ -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