datadog 2.1.0 → 2.3.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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -1
  3. data/ext/datadog_profiling_loader/extconf.rb +15 -15
  4. data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
  6. data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
  7. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +132 -44
  8. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
  9. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
  10. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
  11. data/ext/datadog_profiling_native_extension/collectors_stack.c +90 -37
  12. data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
  14. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
  17. data/ext/datadog_profiling_native_extension/extconf.rb +69 -62
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
  20. data/ext/datadog_profiling_native_extension/helpers.h +6 -17
  21. data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
  22. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
  23. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
  24. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -126
  25. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
  26. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
  27. data/ext/datadog_profiling_native_extension/profiling.c +0 -2
  28. data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
  29. data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
  30. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  31. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
  32. data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
  33. data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -0
  34. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  35. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  36. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +20 -7
  37. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  38. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  39. data/ext/libdatadog_api/extconf.rb +108 -0
  40. data/ext/libdatadog_api/macos_development.md +26 -0
  41. data/ext/libdatadog_extconf_helpers.rb +130 -0
  42. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  44. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  45. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  46. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  47. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  48. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  49. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  50. data/lib/datadog/appsec/extensions.rb +1 -0
  51. data/lib/datadog/appsec/processor/actions.rb +1 -1
  52. data/lib/datadog/appsec/response.rb +15 -1
  53. data/lib/datadog/appsec.rb +1 -0
  54. data/lib/datadog/core/configuration/components.rb +17 -12
  55. data/lib/datadog/core/configuration/settings.rb +93 -7
  56. data/lib/datadog/core/configuration.rb +3 -17
  57. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  58. data/lib/datadog/core/crashtracking/component.rb +111 -0
  59. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  60. data/lib/datadog/core/deprecations.rb +58 -0
  61. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  62. data/lib/datadog/core/environment/yjit.rb +5 -0
  63. data/lib/datadog/core/runtime/ext.rb +1 -0
  64. data/lib/datadog/core/runtime/metrics.rb +6 -0
  65. data/lib/datadog/core/telemetry/component.rb +154 -0
  66. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  67. data/lib/datadog/core/telemetry/event.rb +132 -26
  68. data/lib/datadog/core/telemetry/ext.rb +3 -0
  69. data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
  70. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  71. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  72. data/lib/datadog/core/telemetry/logging.rb +35 -0
  73. data/lib/datadog/core/telemetry/metric.rb +167 -0
  74. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  75. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  76. data/lib/datadog/core/telemetry/request.rb +1 -1
  77. data/lib/datadog/core/telemetry/worker.rb +173 -0
  78. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  79. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  80. data/lib/datadog/core.rb +2 -19
  81. data/lib/datadog/kit/appsec/events.rb +2 -4
  82. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  83. data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
  84. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  85. data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
  86. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  87. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  88. data/lib/datadog/profiling/collectors/info.rb +3 -3
  89. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  90. data/lib/datadog/profiling/component.rb +85 -90
  91. data/lib/datadog/profiling/exporter.rb +3 -3
  92. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  93. data/lib/datadog/profiling/ext.rb +21 -21
  94. data/lib/datadog/profiling/flush.rb +1 -1
  95. data/lib/datadog/profiling/http_transport.rb +8 -6
  96. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  97. data/lib/datadog/profiling/preload.rb +1 -1
  98. data/lib/datadog/profiling/profiler.rb +5 -8
  99. data/lib/datadog/profiling/scheduler.rb +31 -25
  100. data/lib/datadog/profiling/tag_builder.rb +2 -2
  101. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  102. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  103. data/lib/datadog/profiling.rb +5 -5
  104. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  105. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  106. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  107. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  108. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  109. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  110. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  111. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  112. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  114. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  119. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  120. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  122. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
  123. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  124. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  125. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  126. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  127. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  128. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  129. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  130. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  131. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  132. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  133. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  134. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  135. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  136. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  137. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
  138. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  139. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  140. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  141. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  142. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  143. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  144. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  145. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  146. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  147. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  148. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  149. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  150. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  151. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  152. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  153. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  154. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  155. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  156. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  157. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  158. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  159. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  160. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  161. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  162. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  163. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  164. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  165. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  166. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  167. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  168. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  169. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  170. data/lib/datadog/tracing/span.rb +9 -2
  171. data/lib/datadog/tracing/span_event.rb +41 -0
  172. data/lib/datadog/tracing/span_operation.rb +9 -4
  173. data/lib/datadog/tracing/trace_operation.rb +7 -3
  174. data/lib/datadog/tracing/trace_segment.rb +4 -1
  175. data/lib/datadog/tracing/tracer.rb +9 -2
  176. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  177. data/lib/datadog/tracing.rb +5 -1
  178. data/lib/datadog/version.rb +2 -2
  179. metadata +43 -12
  180. data/lib/datadog/core/telemetry/client.rb +0 -95
  181. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
  182. data/lib/datadog/profiling/crashtracker.rb +0 -91
  183. data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -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)
@@ -7,7 +7,21 @@ module Datadog
7
7
  module Ext
8
8
  # @public_api
9
9
  module DB
10
+ # Name of the database. This is *not* the database hostname.
11
+ #
12
+ # For databases which support such a concept, the default schema/database/namespace
13
+ # as configured in the connection string.
14
+ #
15
+ # If the tracer is already tracking changes to the default schema/database throughout the lifetime of
16
+ # the session (i.e. the client executes USE {NEW_SCHEMA} and now the default schema has changed from what
17
+ # was set upon connection initialization), then ideally this attribute reflects the “current” value.
18
+ # If the tracer is not already tracking changes then just leaving it to the default value set upon
19
+ # initialization is OK.
20
+ #
21
+ # This is the equivalent of OTel’s `db.namespace`
22
+ # @see https://opentelemetry.io/docs/specs/semconv/database/database-spans/#common-attributes
10
23
  TAG_INSTANCE = 'db.instance'
24
+
11
25
  TAG_USER = 'db.user'
12
26
  TAG_SYSTEM = 'db.system'
13
27
  TAG_STATEMENT = 'db.statement'
@@ -42,6 +42,11 @@ module Datadog
42
42
  o.type :bool
43
43
  o.default false
44
44
  end
45
+
46
+ option :with_unified_tracer do |o|
47
+ o.type :bool
48
+ o.default false
49
+ end
45
50
  end
46
51
  end
47
52
  end