datadog 2.17.0 → 2.19.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +63 -56
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +263 -76
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +20 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +62 -12
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +7 -0
  9. data/ext/datadog_profiling_native_extension/heap_recorder.c +239 -363
  10. data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -6
  11. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
  12. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +38 -26
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -4
  15. data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
  16. data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -11
  17. data/ext/datadog_profiling_native_extension/stack_recorder.c +154 -57
  18. data/ext/libdatadog_api/extconf.rb +2 -2
  19. data/ext/libdatadog_api/library_config.c +54 -12
  20. data/ext/libdatadog_api/library_config.h +6 -0
  21. data/ext/libdatadog_api/process_discovery.c +2 -7
  22. data/ext/libdatadog_extconf_helpers.rb +1 -1
  23. data/lib/datadog/appsec/api_security/lru_cache.rb +9 -2
  24. data/lib/datadog/appsec/api_security/route_extractor.rb +71 -0
  25. data/lib/datadog/appsec/api_security/sampler.rb +59 -0
  26. data/lib/datadog/appsec/api_security.rb +14 -0
  27. data/lib/datadog/appsec/assets/waf_rules/recommended.json +257 -85
  28. data/lib/datadog/appsec/assets/waf_rules/strict.json +10 -78
  29. data/lib/datadog/appsec/component.rb +30 -54
  30. data/lib/datadog/appsec/configuration/settings.rb +60 -2
  31. data/lib/datadog/appsec/context.rb +6 -6
  32. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
  33. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +27 -16
  34. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +1 -1
  35. data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
  36. data/lib/datadog/appsec/remote.rb +15 -55
  37. data/lib/datadog/appsec/security_engine/engine.rb +194 -0
  38. data/lib/datadog/appsec/security_engine/runner.rb +10 -11
  39. data/lib/datadog/appsec.rb +4 -7
  40. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  41. data/lib/datadog/core/configuration/agent_settings_resolver.rb +1 -43
  42. data/lib/datadog/core/configuration/components.rb +2 -4
  43. data/lib/datadog/core/configuration/option.rb +9 -9
  44. data/lib/datadog/core/configuration/settings.rb +42 -10
  45. data/lib/datadog/core/configuration/stable_config.rb +1 -2
  46. data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
  47. data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
  48. data/lib/datadog/core/process_discovery.rb +5 -1
  49. data/lib/datadog/core/remote/configuration/repository.rb +12 -0
  50. data/lib/datadog/core/tag_builder.rb +56 -0
  51. data/lib/datadog/core/telemetry/component.rb +8 -4
  52. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +1 -0
  53. data/lib/datadog/core/telemetry/event/app_started.rb +148 -40
  54. data/lib/datadog/core/telemetry/logger.rb +5 -4
  55. data/lib/datadog/core/telemetry/logging.rb +11 -5
  56. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  57. data/lib/datadog/core/transport/http/builder.rb +2 -2
  58. data/lib/datadog/core/transport/http/env.rb +8 -0
  59. data/lib/datadog/core/utils.rb +7 -0
  60. data/lib/datadog/di/instrumenter.rb +48 -5
  61. data/lib/datadog/di/probe_notification_builder.rb +37 -42
  62. data/lib/datadog/di/probe_notifier_worker.rb +9 -1
  63. data/lib/datadog/di/serializer.rb +10 -2
  64. data/lib/datadog/di/transport/http/input.rb +10 -0
  65. data/lib/datadog/di/transport/input.rb +10 -2
  66. data/lib/datadog/di.rb +0 -6
  67. data/lib/datadog/kit/appsec/events/v2.rb +195 -0
  68. data/lib/datadog/profiling/collectors/code_provenance.rb +17 -8
  69. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -0
  70. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  71. data/lib/datadog/profiling/collectors/info.rb +41 -0
  72. data/lib/datadog/profiling/collectors/thread_context.rb +16 -1
  73. data/lib/datadog/profiling/component.rb +8 -9
  74. data/lib/datadog/profiling/exporter.rb +9 -3
  75. data/lib/datadog/profiling/ext.rb +0 -12
  76. data/lib/datadog/profiling/http_transport.rb +2 -2
  77. data/lib/datadog/profiling/profiler.rb +2 -0
  78. data/lib/datadog/profiling/scheduler.rb +2 -1
  79. data/lib/datadog/profiling/sequence_tracker.rb +44 -0
  80. data/lib/datadog/profiling/stack_recorder.rb +5 -5
  81. data/lib/datadog/profiling/tag_builder.rb +7 -37
  82. data/lib/datadog/profiling/tasks/setup.rb +2 -0
  83. data/lib/datadog/profiling.rb +1 -0
  84. data/lib/datadog/single_step_instrument.rb +9 -0
  85. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
  86. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
  87. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  88. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +7 -1
  89. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +13 -0
  90. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  91. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -6
  92. data/lib/datadog/tracing/contrib/rails/patcher.rb +4 -1
  93. data/lib/datadog/tracing/contrib/rails/runner.rb +61 -40
  94. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  95. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
  96. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  97. data/lib/datadog/tracing/span_event.rb +1 -1
  98. data/lib/datadog/tracing/span_operation.rb +22 -0
  99. data/lib/datadog/tracing/sync_writer.rb +1 -1
  100. data/lib/datadog/tracing/trace_operation.rb +12 -4
  101. data/lib/datadog/tracing/tracer.rb +6 -2
  102. data/lib/datadog/version.rb +1 -1
  103. data/lib/datadog.rb +7 -0
  104. metadata +14 -10
  105. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
  106. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
  107. data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
  108. data/lib/datadog/appsec/processor.rb +0 -107
@@ -67,6 +67,7 @@ module Datadog
67
67
  allocation_profiling_enabled: allocation_profiling_enabled,
68
68
  allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
69
69
  gvl_profiling_enabled: enable_gvl_profiling?(settings, logger),
70
+ sighandler_sampling_enabled: settings.profiling.advanced.sighandler_sampling_enabled,
70
71
  )
71
72
 
72
73
  internal_metadata = {
@@ -96,6 +97,7 @@ module Datadog
96
97
  timeline_enabled: timeline_enabled,
97
98
  waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
98
99
  otel_context_enabled: settings.profiling.advanced.preview_otel_context_enabled,
100
+ native_filenames_enabled: settings.profiling.advanced.native_filenames_enabled,
99
101
  )
100
102
  end
101
103
 
@@ -221,13 +223,14 @@ module Datadog
221
223
  end
222
224
 
223
225
  unless allocation_profiling_enabled
224
- raise ArgumentError, "Heap profiling requires allocation profiling to be enabled"
226
+ logger.warn(
227
+ "Heap profiling was requested but allocation profiling is not enabled. " \
228
+ "Heap profiling has been disabled."
229
+ )
230
+ return false
225
231
  end
226
232
 
227
- logger.warn(
228
- "Enabled experimental heap profiling: heap_sample_rate=#{heap_sample_rate}. This is experimental, not " \
229
- "recommended, and will increase overhead!"
230
- )
233
+ logger.debug("Enabled heap profiling: heap_sample_rate=#{heap_sample_rate}")
231
234
 
232
235
  true
233
236
  end
@@ -237,10 +240,6 @@ module Datadog
237
240
 
238
241
  return false unless heap_profiling_enabled && heap_size_profiling_enabled
239
242
 
240
- logger.warn(
241
- "Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!"
242
- )
243
-
244
243
  true
245
244
  end
246
245
 
@@ -26,7 +26,8 @@ module Datadog
26
26
  :last_flush_finish_at,
27
27
  :created_at,
28
28
  :internal_metadata,
29
- :info_json
29
+ :info_json,
30
+ :sequence_tracker
30
31
 
31
32
  public
32
33
 
@@ -37,7 +38,8 @@ module Datadog
37
38
  code_provenance_collector:,
38
39
  internal_metadata:,
39
40
  minimum_duration_seconds: PROFILE_DURATION_THRESHOLD_SECONDS,
40
- time_provider: Time
41
+ time_provider: Time,
42
+ sequence_tracker: Datadog::Profiling::SequenceTracker
41
43
  )
42
44
  @pprof_recorder = pprof_recorder
43
45
  @worker = worker
@@ -50,6 +52,7 @@ module Datadog
50
52
  # NOTE: At the time of this comment collected info does not change over time so we'll hardcode
51
53
  # it on startup to prevent serializing the same info on every flush.
52
54
  @info_json = JSON.generate(info_collector.info).freeze
55
+ @sequence_tracker = sequence_tracker
53
56
  end
54
57
 
55
58
  def flush
@@ -73,7 +76,10 @@ module Datadog
73
76
  encoded_profile: encoded_profile,
74
77
  code_provenance_file_name: Datadog::Profiling::Ext::Transport::HTTP::CODE_PROVENANCE_FILENAME,
75
78
  code_provenance_data: uncompressed_code_provenance,
76
- tags_as_array: Datadog::Profiling::TagBuilder.call(settings: Datadog.configuration).to_a,
79
+ tags_as_array: Datadog::Profiling::TagBuilder.call(
80
+ settings: Datadog.configuration,
81
+ profile_seq: sequence_tracker.get_next,
82
+ ).to_a,
77
83
  internal_metadata: internal_metadata.merge(
78
84
  {
79
85
  worker_stats: worker_stats,
@@ -11,19 +11,7 @@ module Datadog
11
11
 
12
12
  module Transport
13
13
  module HTTP
14
- FORM_FIELD_TAG_ENV = "env"
15
- FORM_FIELD_TAG_HOST = "host"
16
- FORM_FIELD_TAG_LANGUAGE = "language"
17
- FORM_FIELD_TAG_PID = "process_id"
18
14
  FORM_FIELD_TAG_PROFILER_VERSION = "profiler_version"
19
- FORM_FIELD_TAG_RUNTIME = "runtime"
20
- FORM_FIELD_TAG_RUNTIME_ENGINE = "runtime_engine"
21
- FORM_FIELD_TAG_RUNTIME_ID = "runtime-id"
22
- FORM_FIELD_TAG_RUNTIME_VERSION = "runtime_version"
23
- FORM_FIELD_TAG_SERVICE = "service"
24
- FORM_FIELD_TAG_VERSION = "version"
25
- TAG_GIT_REPOSITORY_URL = "git.repository_url"
26
- TAG_GIT_COMMIT_SHA = "git.commit.sha"
27
15
 
28
16
  CODE_PROVENANCE_FILENAME = "code-provenance.json"
29
17
  end
@@ -37,7 +37,7 @@ module Datadog
37
37
  Datadog.logger.debug("Successfully reported profiling data")
38
38
  true
39
39
  else
40
- Datadog.logger.error(
40
+ Datadog.logger.warn(
41
41
  "Failed to report profiling data (#{config_without_api_key}): " \
42
42
  "server returned unexpected HTTP #{result} status code"
43
43
  )
@@ -47,7 +47,7 @@ module Datadog
47
47
  false
48
48
  end
49
49
  else
50
- Datadog.logger.error("Failed to report profiling data (#{config_without_api_key}): #{result}")
50
+ Datadog.logger.warn("Failed to report profiling data (#{config_without_api_key}): #{result}")
51
51
  Datadog::Core::Telemetry::Logger.error("Failed to report profiling data")
52
52
  false
53
53
  end
@@ -50,6 +50,8 @@ module Datadog
50
50
  "Detected issue with profiler (#{failed_component} component), stopping profiling. " \
51
51
  "See previous log messages for details."
52
52
  )
53
+ Datadog::Core::Telemetry::Logger
54
+ .error("Detected issue with profiler (#{failed_component} component), stopping profiling")
53
55
 
54
56
  # We explicitly not stop the crash tracker in this situation, under the assumption that, if a component failed,
55
57
  # we're operating in a degraded state and crash tracking may still be helpful.
@@ -68,6 +68,7 @@ module Datadog
68
68
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
69
69
  )
70
70
  on_failure_proc&.call
71
+ Datadog::Core::Telemetry::Logger.report(e, description: "Profiling::Scheduler thread error")
71
72
  raise
72
73
  ensure
73
74
  Datadog.logger.debug("#flush was interrupted or failed before it could complete") if interrupted
@@ -133,7 +134,7 @@ module Datadog
133
134
  begin
134
135
  transport.export(flush)
135
136
  rescue => e
136
- Datadog.logger.error(
137
+ Datadog.logger.warn(
137
138
  "Unable to report profile. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
138
139
  )
139
140
  Datadog::Core::Telemetry::Logger.report(e, description: "Unable to report profile")
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/utils/forking'
4
+
5
+ module Datadog
6
+ module Profiling
7
+ # Used to generate the `profile_seq` tag, which effectively counts how many profiles we've attempted to report
8
+ # from a given runtime-id.
9
+ #
10
+ # Note that the above implies a few things:
11
+ # 1. The sequence number only gets incremented when we decide to report a profile and create a `Flush` for it
12
+ # 2. The `SequenceTracker` must live across profiler reconfigurations and resets, since no matter how many
13
+ # profiler instances get created due to reconfiguration, the runtime-id is still the same, so the sequence number
14
+ # should be kept and not restarted from 0
15
+ # 3. The `SequenceTracker` must be reset after a fork, since the runtime-id will change, and we want to start
16
+ # counting from 0 again
17
+ #
18
+ # This is why this module is implemented as a singleton that we reuse, not as an instance that we recreate.
19
+ #
20
+ # Note that this module is not thread-safe, so it's up to the callers to make sure
21
+ # it's only used by a single thread at a time (which is what the `Profiling::Exporter`)
22
+ # is doing.
23
+ module SequenceTracker
24
+ class << self
25
+ include Core::Utils::Forking
26
+
27
+ def get_next
28
+ reset! unless defined?(@sequence_number)
29
+ after_fork! { reset! }
30
+
31
+ next_seq = @sequence_number
32
+ @sequence_number += 1
33
+ next_seq
34
+ end
35
+
36
+ private
37
+
38
+ def reset!
39
+ @sequence_number = 0
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -23,7 +23,7 @@ module Datadog
23
23
  # This isn't something we expect to happen normally, but because it would break the assumptions of the
24
24
  # C-level mutexes (that there is a single serializer thread), we add it here as an extra safeguard against it
25
25
  # accidentally happening.
26
- @no_concurrent_synchronize_mutex = Mutex.new
26
+ @no_concurrent_serialize_mutex = Mutex.new
27
27
 
28
28
  self.class._native_initialize(
29
29
  self_instance: self,
@@ -60,7 +60,7 @@ module Datadog
60
60
  end
61
61
 
62
62
  def serialize
63
- status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
63
+ status, result = @no_concurrent_serialize_mutex.synchronize { self.class._native_serialize(self) }
64
64
 
65
65
  if status == :ok
66
66
  start, finish, encoded_profile, profile_stats = result
@@ -71,15 +71,15 @@ module Datadog
71
71
  else
72
72
  error_message = result
73
73
 
74
- Datadog.logger.error("Failed to serialize profiling data: #{error_message}")
75
- Datadog::Core::Telemetry::Logger.error("Failed to serialize profiling data")
74
+ Datadog.logger.warn("Failed to serialize profiling data: #{error_message}")
75
+ Datadog::Core::Telemetry::Logger.error("Failed to serialize profiling data (#{error_message})")
76
76
 
77
77
  nil
78
78
  end
79
79
  end
80
80
 
81
81
  def serialize!
82
- status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
82
+ status, result = @no_concurrent_serialize_mutex.synchronize { self.class._native_serialize(self) }
83
83
 
84
84
  if status == :ok
85
85
  _start, _finish, encoded_profile = result
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../core/tag_builder"
3
4
  require_relative "../core/utils"
4
- require_relative "../core/environment/git"
5
5
 
6
6
  module Datadog
7
7
  module Profiling
@@ -11,45 +11,15 @@ module Datadog
11
11
 
12
12
  def self.call(
13
13
  settings:,
14
- # Unified service tagging
15
- env: settings.env,
16
- service: settings.service,
17
- version: settings.version,
18
14
  # Other metadata
19
- host: Core::Environment::Socket.hostname,
20
- language: Core::Environment::Identity.lang,
21
- pid: Process.pid.to_s,
22
- profiler_version: Core::Environment::Identity.gem_datadog_version,
23
- runtime_engine: Core::Environment::Identity.lang_engine,
24
- runtime_id: Core::Environment::Identity.id,
25
- runtime_version: Core::Environment::Identity.lang_version,
26
- git_repository_url: Core::Environment::Git.git_repository_url,
27
- git_commit_sha: Core::Environment::Git.git_commit_sha,
28
- # User-provided tags
29
- user_tags: settings.tags
15
+ profile_seq:,
16
+ profiler_version: Core::Environment::Identity.gem_datadog_version
30
17
  )
31
- tags = {
32
- # When changing or adding these, make sure they are kept in sync with
33
- # https://docs.google.com/spreadsheets/d/1LOGMf4c4Avbtn36uZ2SWvhIGKRPLM1BoWkUP4JYj7hA/ (Datadog internal link)
34
- FORM_FIELD_TAG_HOST => host,
35
- FORM_FIELD_TAG_LANGUAGE => language,
36
- FORM_FIELD_TAG_PID => pid,
18
+ hash = Core::TagBuilder.tags(settings).merge(
37
19
  FORM_FIELD_TAG_PROFILER_VERSION => profiler_version,
38
- FORM_FIELD_TAG_RUNTIME => language, # This is known to be repeated from language, above
39
- FORM_FIELD_TAG_RUNTIME_ENGINE => runtime_engine,
40
- FORM_FIELD_TAG_RUNTIME_ID => runtime_id,
41
- FORM_FIELD_TAG_RUNTIME_VERSION => runtime_version,
42
- }
43
- tags[FORM_FIELD_TAG_ENV] = env if env
44
- tags[FORM_FIELD_TAG_SERVICE] = service if service
45
- tags[FORM_FIELD_TAG_VERSION] = version if version
46
- tags[TAG_GIT_REPOSITORY_URL] = git_repository_url if git_repository_url
47
- tags[TAG_GIT_COMMIT_SHA] = git_commit_sha if git_commit_sha
48
-
49
- # Make sure everything is an utf-8 string, to avoid encoding issues in native code/further downstream
50
- user_tags.merge(tags).map do |key, value|
51
- [Datadog::Core::Utils.utf8_encode(key), Datadog::Core::Utils.utf8_encode(value)]
52
- end.to_h
20
+ 'profile_seq' => profile_seq.to_s,
21
+ )
22
+ Core::Utils.encode_tags(hash)
53
23
  end
54
24
  end
55
25
  end
@@ -19,6 +19,7 @@ module Datadog
19
19
  "Profiler extensions unavailable. Cause: #{e.class.name} #{e.message} " \
20
20
  "Location: #{Array(e.backtrace).first}"
21
21
  end
22
+ Datadog::Core::Telemetry::Logger.report(e, description: "Profiler extensions unavailable")
22
23
  end
23
24
  end
24
25
 
@@ -33,6 +34,7 @@ module Datadog
33
34
  "Error during post-fork hooks. Cause: #{e.class.name} #{e.message} " \
34
35
  "Location: #{Array(e.backtrace).first}"
35
36
  end
37
+ Datadog::Core::Telemetry::Logger.report(e, description: "Error during post-fork hooks")
36
38
  end
37
39
  end
38
40
  end
@@ -157,6 +157,7 @@ module Datadog
157
157
  require_relative 'profiling/native_extension'
158
158
  require_relative 'profiling/tag_builder'
159
159
  require_relative 'profiling/http_transport'
160
+ require_relative 'profiling/sequence_tracker'
160
161
 
161
162
  replace_noop_allocation_count
162
163
 
@@ -5,8 +5,17 @@
5
5
  #
6
6
  # This file's path is private. Do not reference this file.
7
7
  #
8
+
9
+ module Datadog
10
+ # This module handles conditional loading of single step auto-instrumentation,
11
+ # which enables Datadog tracing and profiling features when available.
12
+ module SingleStepInstrument
13
+ end
14
+ end
15
+
8
16
  begin
9
17
  require_relative 'auto_instrument'
18
+ Datadog::SingleStepInstrument::LOADED = true
10
19
  rescue StandardError, LoadError => e
11
20
  warn "Single step instrumentation failed: #{e.class}:#{e.message}\n\tSource:\n\t#{Array(e.backtrace).join("\n\t")}"
12
21
  end
@@ -68,6 +68,12 @@ module Datadog
68
68
 
69
69
  span.set_tag(Ext::TAG_ROUTE_ACTION, payload.fetch(:action))
70
70
  span.set_tag(Ext::TAG_ROUTE_CONTROLLER, payload.fetch(:controller))
71
+ if (runtime = payload[:view_runtime])
72
+ span.set_tag(Ext::TAG_VIEW_RUNTIME, runtime)
73
+ end
74
+ if (runtime = payload[:db_runtime])
75
+ span.set_tag(Ext::TAG_DB_RUNTIME, runtime)
76
+ end
71
77
 
72
78
  exception = payload[:exception_object]
73
79
  if exception.nil?
@@ -118,6 +124,15 @@ module Datadog
118
124
  payload[:exception] = [e.class.name, e.message]
119
125
  payload[:exception_object] = e
120
126
  raise e
127
+ ensure
128
+ # Database and view runtime are available for controllers
129
+ # deriving from ActionController::Base.
130
+ # They are not defined on controllers deriving from
131
+ # ActionController::Metal, unless
132
+ # ActionController::Instrumentation is explicitly included
133
+ # into the controller class.
134
+ payload[:db_runtime] = db_runtime if respond_to?(:db_runtime)
135
+ payload[:view_runtime] = view_runtime if respond_to?(:view_runtime)
121
136
  end
122
137
  # rubocop:enable Lint/RescueException
123
138
  ensure
@@ -9,9 +9,12 @@ module Datadog
9
9
  module ActionDispatch
10
10
  # Instrumentation for ActionDispatch components
11
11
  module Instrumentation
12
+ SCRIPT_NAME_KEY = 'SCRIPT_NAME'
13
+ FORMAT_SUFFIX = '(.:format)'
14
+
12
15
  module_function
13
16
 
14
- def set_http_route_tags(route_spec, script_name)
17
+ def set_http_route_tags(route_spec, route_path)
15
18
  return unless Tracing.enabled?
16
19
 
17
20
  return unless route_spec
@@ -19,10 +22,10 @@ module Datadog
19
22
  request_trace = Tracing.active_trace
20
23
  return unless request_trace
21
24
 
22
- request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, route_spec.to_s.gsub(/\(.:format\)\z/, ''))
25
+ request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, route_spec)
23
26
 
24
- if script_name && !script_name.empty?
25
- request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, script_name)
27
+ if route_path && !route_path.empty?
28
+ request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, route_path)
26
29
  end
27
30
  end
28
31
 
@@ -40,16 +43,17 @@ module Datadog
40
43
  # Instrumentation for ActionDispatch::Journey::Router for Rails versions older than 7.1
41
44
  module Router
42
45
  def find_routes(req)
46
+ # result is an array of [match, parameters, route] tuples
43
47
  result = super
48
+ result.each do |_, _, route|
49
+ next unless Instrumentation.dispatcher_route?(route)
44
50
 
45
- # result is an array of [match, parameters, route] tuples
46
- routes = result.map(&:last)
51
+ http_route = route.path.spec.to_s
52
+ http_route.delete_suffix!(FORMAT_SUFFIX)
47
53
 
48
- routes.each do |route|
49
- if Instrumentation.dispatcher_route?(route)
50
- Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
51
- break
52
- end
54
+ Instrumentation.set_http_route_tags(http_route, req.env[SCRIPT_NAME_KEY])
55
+
56
+ break
53
57
  end
54
58
 
55
59
  result
@@ -62,7 +66,10 @@ module Datadog
62
66
  def find_routes(req)
63
67
  super do |match, parameters, route|
64
68
  if Instrumentation.dispatcher_route?(route)
65
- Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
69
+ http_route = route.path.spec.to_s
70
+ http_route.delete_suffix!(FORMAT_SUFFIX)
71
+
72
+ Instrumentation.set_http_route_tags(http_route, req.env[SCRIPT_NAME_KEY])
66
73
  end
67
74
 
68
75
  yield [match, parameters, route]
@@ -16,6 +16,8 @@ module Datadog
16
16
  TAG_OPERATION_CONTROLLER = 'controller'
17
17
  TAG_ROUTE_ACTION = 'rails.route.action'
18
18
  TAG_ROUTE_CONTROLLER = 'rails.route.controller'
19
+ TAG_DB_RUNTIME = 'rails.db.runtime'
20
+ TAG_VIEW_RUNTIME = 'rails.view.runtime'
19
21
  end
20
22
  end
21
23
  end
@@ -45,9 +45,15 @@ module Datadog
45
45
  'cache_write_multi.active_support' => { resource: Ext::RESOURCE_CACHE_MSET, multi_key: true }
46
46
  }.freeze
47
47
 
48
- def trace?(event, _payload)
48
+ def trace?(event, payload)
49
49
  return false if !Tracing.enabled? || !configuration.enabled
50
50
 
51
+ if (cache_store = configuration[:cache_store])
52
+ store = cache_backend(payload[:store])
53
+
54
+ return false unless cache_store.include?(store)
55
+ end
56
+
51
57
  # DEV-3.0: Backwards compatibility code for the 2.x gem series.
52
58
  # DEV-3.0: See documentation at {Datadog::Tracing::Contrib::ActiveSupport::Cache::Instrumentation}
53
59
  # DEV-3.0: for the complete information about this backwards compatibility code.
@@ -49,6 +49,19 @@ module Datadog
49
49
  o.default true
50
50
  end
51
51
  end
52
+
53
+ # Specifies which cache stores to trace.
54
+ # Accepts a list, with the same format as `config.cache_store`
55
+ # (e.g. `memory_store`, `file_store`, or symbols like `:file_store`).
56
+ # Defaults to `nil`, which traces all cache stores.
57
+ # @see https://github.com/rails/rails/blob/b7520a13adda46c0cc5f3fb4a4c1726633af2bba/guides/source/caching_with_rails.md?plain=1#L576-L582
58
+ option :cache_store do |o|
59
+ o.type :array, nilable: true
60
+ o.default nil
61
+ o.after_set do |stores|
62
+ stores&.map!(&:to_s) # Convert symbols to strings to match the Rails configuration format
63
+ end
64
+ end
52
65
  end
53
66
  end
54
67
  end
@@ -20,10 +20,12 @@ module Datadog
20
20
 
21
21
  # patch applies our patch
22
22
  def patch
23
+ # First check Lograge logger directly for when keep_original_rails_log option is used
24
+ used_logger = ::Lograge.logger || ::Lograge::LogSubscribers::ActionController.logger
25
+
23
26
  # ActiveSupport::TaggedLogging is the default Rails logger since Rails 5
24
27
  if defined?(::ActiveSupport::TaggedLogging::Formatter) &&
25
- ::Lograge::LogSubscribers::ActionController
26
- .logger&.formatter.is_a?(::ActiveSupport::TaggedLogging::Formatter)
28
+ used_logger&.formatter.is_a?(::ActiveSupport::TaggedLogging::Formatter)
27
29
  Datadog.logger.warn(
28
30
  'Lograge and ActiveSupport::TaggedLogging (the default Rails log formatter) are not compatible: ' \
29
31
  'Lograge does not account for Rails log tags, creating polluted logs and breaking log formatting. ' \
@@ -45,16 +45,13 @@ module Datadog
45
45
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
46
46
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_QUERY)
47
47
 
48
- span.set_tag(Tracing::Metadata::Ext::TAG_PEER_HOSTNAME, query_options[:host])
48
+ tag_database_instance(span, query_options[:database])
49
+
50
+ set_span_tags(span, query_options)
49
51
 
50
52
  # Set analytics sample rate
51
53
  Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?
52
54
 
53
- span.set_tag(Contrib::Ext::DB::TAG_INSTANCE, query_options[:database])
54
- span.set_tag(Ext::TAG_DB_NAME, query_options[:database])
55
- span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, query_options[:host])
56
- span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, query_options[:port])
57
-
58
55
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
59
56
 
60
57
  sql = inject_propagation(span, sql, trace_op)
@@ -91,6 +88,19 @@ module Datadog
91
88
  def analytics_sample_rate
92
89
  datadog_configuration[:analytics_sample_rate]
93
90
  end
91
+
92
+ def tag_database_instance(span, database)
93
+ return if database.nil? || database.empty?
94
+
95
+ span.set_tag(Contrib::Ext::DB::TAG_INSTANCE, database)
96
+ span.set_tag(Ext::TAG_DB_NAME, database)
97
+ end
98
+
99
+ def set_span_tags(span, query_options)
100
+ span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, query_options[:host])
101
+ span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, query_options[:port])
102
+ span.set_tag(Tracing::Metadata::Ext::TAG_PEER_HOSTNAME, query_options[:host])
103
+ end
94
104
  end
95
105
  end
96
106
  end
@@ -86,7 +86,10 @@ module Datadog
86
86
 
87
87
  # Instruments the `bin/rails runner` command.
88
88
  def patch_rails_runner
89
- ::Rails::Command.singleton_class.prepend(Command) if defined?(::Rails::Command)
89
+ # The `RunnerCommand` class is only available in Rails 5.1 and later.
90
+ if defined?(::Rails::Command::RunnerCommand) && Integration.version >= Gem::Version.new('5.1')
91
+ ::Rails::Command::RunnerCommand.prepend(Runner)
92
+ end
90
93
  end
91
94
  end
92
95
  end