datadog 2.17.0 → 2.18.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +12 -46
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +227 -49
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +19 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +63 -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 +1 -0
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -3
  15. data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
  16. data/ext/datadog_profiling_native_extension/ruby_helpers.h +2 -10
  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 +65 -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/processor/rule_loader.rb +5 -6
  35. data/lib/datadog/appsec/remote.rb +15 -55
  36. data/lib/datadog/appsec/security_engine/engine.rb +194 -0
  37. data/lib/datadog/appsec/security_engine/runner.rb +10 -11
  38. data/lib/datadog/appsec.rb +4 -7
  39. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  40. data/lib/datadog/core/configuration/agent_settings_resolver.rb +1 -43
  41. data/lib/datadog/core/configuration/components.rb +2 -4
  42. data/lib/datadog/core/configuration/option.rb +9 -9
  43. data/lib/datadog/core/configuration/settings.rb +22 -10
  44. data/lib/datadog/core/configuration/stable_config.rb +1 -2
  45. data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
  46. data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
  47. data/lib/datadog/core/process_discovery.rb +5 -1
  48. data/lib/datadog/core/remote/configuration/repository.rb +12 -0
  49. data/lib/datadog/core/tag_builder.rb +56 -0
  50. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +1 -0
  51. data/lib/datadog/core/telemetry/event/app_started.rb +129 -39
  52. data/lib/datadog/core/telemetry/logger.rb +5 -4
  53. data/lib/datadog/core/telemetry/logging.rb +11 -5
  54. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  55. data/lib/datadog/core/transport/http/builder.rb +2 -2
  56. data/lib/datadog/core/transport/http/env.rb +8 -0
  57. data/lib/datadog/core/utils.rb +7 -0
  58. data/lib/datadog/di/instrumenter.rb +52 -2
  59. data/lib/datadog/di/probe_notification_builder.rb +31 -41
  60. data/lib/datadog/di/probe_notifier_worker.rb +9 -1
  61. data/lib/datadog/di/serializer.rb +6 -2
  62. data/lib/datadog/di/transport/http/input.rb +10 -0
  63. data/lib/datadog/di/transport/input.rb +10 -2
  64. data/lib/datadog/profiling/collectors/code_provenance.rb +17 -8
  65. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -0
  66. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  67. data/lib/datadog/profiling/collectors/thread_context.rb +16 -1
  68. data/lib/datadog/profiling/component.rb +7 -9
  69. data/lib/datadog/profiling/ext.rb +0 -12
  70. data/lib/datadog/profiling/http_transport.rb +2 -2
  71. data/lib/datadog/profiling/profiler.rb +2 -0
  72. data/lib/datadog/profiling/scheduler.rb +2 -1
  73. data/lib/datadog/profiling/stack_recorder.rb +5 -5
  74. data/lib/datadog/profiling/tag_builder.rb +5 -37
  75. data/lib/datadog/profiling/tasks/setup.rb +2 -0
  76. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
  77. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
  78. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  79. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  80. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  81. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
  82. data/lib/datadog/tracing/sync_writer.rb +1 -1
  83. data/lib/datadog/tracing/trace_operation.rb +12 -4
  84. data/lib/datadog/tracing/tracer.rb +6 -2
  85. data/lib/datadog/version.rb +1 -1
  86. metadata +12 -10
  87. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
  88. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
  89. data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
  90. data/lib/datadog/appsec/processor.rb +0 -107
@@ -82,7 +82,11 @@ module Datadog
82
82
  # between positional and keyword arguments. We convert positional
83
83
  # arguments to keyword arguments ("arg1", "arg2", ...) and ensure
84
84
  # the positional arguments are listed first.
85
- def serialize_args(args, kwargs,
85
+ #
86
+ # Instance variables are technically a hash just like kwargs,
87
+ # we take them as a separate parameter to avoid a hash merge
88
+ # in upstream code.
89
+ def serialize_args(args, kwargs, instance_vars,
86
90
  depth: settings.dynamic_instrumentation.max_capture_depth,
87
91
  attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
88
92
  counter = 0
@@ -91,7 +95,7 @@ module Datadog
91
95
  # Conversion to symbol is needed here to put args ahead of
92
96
  # kwargs when they are merged below.
93
97
  c[:"arg#{counter}"] = value
94
- end.update(kwargs)
98
+ end.update(kwargs).update(instance_vars)
95
99
  serialize_vars(combined, depth: depth, attribute_count: attribute_count)
96
100
  end
97
101
 
@@ -53,6 +53,16 @@ module Datadog
53
53
  # Encode body & type
54
54
  env.headers[HEADER_CONTENT_TYPE] = encoder.content_type
55
55
  env.body = env.request.parcel.data
56
+ env.query = {
57
+ # DEV: In theory we could serialize the tags here
58
+ # rather than requiring them to be pre-serialized.
59
+ # In practice the tags should be relatively static
60
+ # (they would change when process forks, and hostname
61
+ # could change at any time but probably we should ignore
62
+ # those changes), therefore serializing the tags
63
+ # every time would be wasteful.
64
+ ddtags: env.request.serialized_tags,
65
+ }
56
66
 
57
67
  super
58
68
  end
@@ -12,6 +12,13 @@ module Datadog
12
12
  end
13
13
 
14
14
  class Request < Datadog::Core::Transport::Request
15
+ attr_reader :serialized_tags
16
+
17
+ def initialize(parcel, serialized_tags)
18
+ super(parcel)
19
+
20
+ @serialized_tags = serialized_tags
21
+ end
15
22
  end
16
23
 
17
24
  class Transport
@@ -28,10 +35,11 @@ module Datadog
28
35
  @apis[HTTP::API::INPUT]
29
36
  end
30
37
 
31
- def send_input(payload)
38
+ def send_input(payload, tags)
32
39
  json = JSON.dump(payload)
33
40
  parcel = EncodedParcel.new(json)
34
- request = Request.new(parcel)
41
+ serialized_tags = Core::TagBuilder.serialize_tags(tags)
42
+ request = Request.new(parcel, serialized_tags)
35
43
 
36
44
  response = @client.send_input_payload(request)
37
45
  unless response.ok?
@@ -14,7 +14,10 @@ module Datadog
14
14
  #
15
15
  # This class acts both as a collector (collecting data) as well as a recorder (records/serializes it)
16
16
  class CodeProvenance
17
- def initialize(standard_library_path: RbConfig::CONFIG.fetch("rubylibdir"))
17
+ def initialize(
18
+ standard_library_path: RbConfig::CONFIG.fetch("rubylibdir"),
19
+ ruby_native_filename: Datadog::Profiling::Collectors::Stack._native_ruby_native_filename
20
+ )
18
21
  @libraries_by_name = {}
19
22
  @libraries_by_path = {}
20
23
  @seen_files = Set.new
@@ -26,6 +29,7 @@ module Datadog
26
29
  name: "stdlib",
27
30
  version: RUBY_VERSION,
28
31
  path: standard_library_path,
32
+ extra_path: ruby_native_filename,
29
33
  )
30
34
  )
31
35
  end
@@ -37,10 +41,6 @@ module Datadog
37
41
  self
38
42
  end
39
43
 
40
- def generate
41
- seen_libraries
42
- end
43
-
44
44
  def generate_json
45
45
  JSON.generate(v1: seen_libraries.to_a)
46
46
  end
@@ -79,7 +79,15 @@ module Datadog
79
79
  loaded_specs.each do |spec|
80
80
  next if libraries_by_name.key?(spec.name)
81
81
 
82
- record_library(Library.new(kind: "library", name: spec.name, version: spec.version, path: spec.gem_dir))
82
+ record_library(
83
+ Library.new(
84
+ kind: "library",
85
+ name: spec.name,
86
+ version: spec.version,
87
+ path: spec.gem_dir,
88
+ extra_path: (spec.extension_dir if spec.extensions.any?),
89
+ )
90
+ )
83
91
  recorded_library = true
84
92
  end
85
93
 
@@ -110,11 +118,12 @@ module Datadog
110
118
  class Library
111
119
  attr_reader :kind, :name, :version
112
120
 
113
- def initialize(kind:, name:, version:, path:)
121
+ def initialize(kind:, name:, version:, path:, extra_path: nil)
122
+ extra_path = nil if extra_path&.empty?
114
123
  @kind = kind.freeze
115
124
  @name = name.dup.freeze
116
125
  @version = version.to_s.dup.freeze
117
- @paths = [path.dup.freeze].freeze
126
+ @paths = [path.dup.freeze, extra_path.dup.freeze].compact.freeze
118
127
  freeze
119
128
  end
120
129
 
@@ -33,6 +33,9 @@ module Datadog
33
33
  Datadog.logger.warn(
34
34
  "Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
35
35
  )
36
+ Datadog::Core::Telemetry::Logger.error(
37
+ "Profiling dynamic sampling rate disabled. This should only be used for testing, and will increase overhead!"
38
+ )
36
39
  end
37
40
 
38
41
  self.class._native_initialize(
@@ -77,6 +80,7 @@ module Datadog
77
80
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
78
81
  )
79
82
  on_failure_proc&.call
83
+ Datadog::Core::Telemetry::Logger.report(e, description: "CpuAndWallTimeWorker thread error", pii_safe: true)
80
84
  end
81
85
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
82
86
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -41,6 +41,7 @@ module Datadog
41
41
  "IdleSamplingHelper thread error. " \
42
42
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
43
43
  )
44
+ Datadog::Core::Telemetry::Logger.report(e, description: "IdleSamplingHelper thread error", pii_safe: true)
44
45
  end
45
46
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
46
47
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -21,7 +21,8 @@ module Datadog
21
21
  endpoint_collection_enabled:,
22
22
  timeline_enabled:,
23
23
  waiting_for_gvl_threshold_ns:,
24
- otel_context_enabled:
24
+ otel_context_enabled:,
25
+ native_filenames_enabled:
25
26
  )
26
27
  tracer_context_key = safely_extract_context_key_from(tracer)
27
28
  self.class._native_initialize(
@@ -33,6 +34,7 @@ module Datadog
33
34
  timeline_enabled: timeline_enabled,
34
35
  waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
35
36
  otel_context_enabled: otel_context_enabled,
37
+ native_filenames_enabled: validate_native_filenames(native_filenames_enabled),
36
38
  )
37
39
  end
38
40
 
@@ -44,6 +46,7 @@ module Datadog
44
46
  timeline_enabled: false,
45
47
  waiting_for_gvl_threshold_ns: 10_000_000,
46
48
  otel_context_enabled: false,
49
+ native_filenames_enabled: true,
47
50
  **options
48
51
  )
49
52
  new(
@@ -54,6 +57,7 @@ module Datadog
54
57
  timeline_enabled: timeline_enabled,
55
58
  waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
56
59
  otel_context_enabled: otel_context_enabled,
60
+ native_filenames_enabled: native_filenames_enabled,
57
61
  **options,
58
62
  )
59
63
  end
@@ -81,6 +85,17 @@ module Datadog
81
85
  context = provider.instance_variable_get(:@context)
82
86
  context&.instance_variable_get(:@key)
83
87
  end
88
+
89
+ def validate_native_filenames(native_filenames_enabled)
90
+ if native_filenames_enabled && !Datadog::Profiling::Collectors::Stack._native_filenames_available?
91
+ Datadog.logger.debug(
92
+ "Native filenames are enabled, but the required dladdr API was not available. Disabling native filenames."
93
+ )
94
+ false
95
+ else
96
+ native_filenames_enabled
97
+ end
98
+ end
84
99
  end
85
100
  end
86
101
  end
@@ -96,6 +96,7 @@ module Datadog
96
96
  timeline_enabled: timeline_enabled,
97
97
  waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
98
98
  otel_context_enabled: settings.profiling.advanced.preview_otel_context_enabled,
99
+ native_filenames_enabled: settings.profiling.advanced.native_filenames_enabled,
99
100
  )
100
101
  end
101
102
 
@@ -221,13 +222,14 @@ module Datadog
221
222
  end
222
223
 
223
224
  unless allocation_profiling_enabled
224
- raise ArgumentError, "Heap profiling requires allocation profiling to be enabled"
225
+ logger.warn(
226
+ "Heap profiling was requested but allocation profiling is not enabled. " \
227
+ "Heap profiling has been disabled."
228
+ )
229
+ return false
225
230
  end
226
231
 
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
- )
232
+ logger.debug("Enabled heap profiling: heap_sample_rate=#{heap_sample_rate}")
231
233
 
232
234
  true
233
235
  end
@@ -237,10 +239,6 @@ module Datadog
237
239
 
238
240
  return false unless heap_profiling_enabled && heap_size_profiling_enabled
239
241
 
240
- logger.warn(
241
- "Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!"
242
- )
243
-
244
242
  true
245
243
  end
246
244
 
@@ -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")
@@ -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,13 @@ 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
+ profiler_version: Core::Environment::Identity.gem_datadog_version
30
16
  )
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,
17
+ hash = Core::TagBuilder.tags(settings).merge(
37
18
  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
19
+ )
20
+ Core::Utils.encode_tags(hash)
53
21
  end
54
22
  end
55
23
  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
@@ -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
@@ -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. ' \
@@ -13,6 +13,7 @@ module Datadog
13
13
  ENV_ANALYTICS_ENABLED = 'DD_TRACE_SIDEKIQ_ANALYTICS_ENABLED'
14
14
  ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_SIDEKIQ_ANALYTICS_SAMPLE_RATE'
15
15
  SERVICE_NAME = 'sidekiq'
16
+ SIDEKIQ_8_SECONDS_PER_INTEGER = 0.001 # Sidekiq 8 uses integer epoch milliseconds, rather than epoch floats
16
17
  SPAN_PUSH = 'sidekiq.push'
17
18
  SPAN_JOB = 'sidekiq.job'
18
19
  SPAN_JOB_FETCH = 'sidekiq.job_fetch'
@@ -22,7 +22,7 @@ module Datadog
22
22
  @quantize = options[:quantize] || configuration[:quantize]
23
23
  end
24
24
 
25
- def call(worker, job, queue)
25
+ def call(worker, job, queue) # rubocop:disable Metrics/MethodLength
26
26
  resource = job_resource(job)
27
27
 
28
28
  if @distributed_tracing
@@ -61,7 +61,10 @@ module Datadog
61
61
  span.set_tag(Ext::TAG_JOB_RETRY_COUNT, job['retry_count'])
62
62
  span.set_tag(Ext::TAG_JOB_QUEUE, job['queue'])
63
63
  span.set_tag(Ext::TAG_JOB_WRAPPER, job['class']) if job['wrapped']
64
- span.set_tag(Ext::TAG_JOB_DELAY, 1000.0 * (Core::Utils::Time.now.utc.to_f - job['enqueued_at'].to_f))
64
+
65
+ enqueued_at = job['enqueued_at']
66
+ enqueued_at *= Ext::SIDEKIQ_8_SECONDS_PER_INTEGER if enqueued_at.is_a?(Integer)
67
+ span.set_tag(Ext::TAG_JOB_DELAY, 1000.0 * (Core::Utils::Time.now.utc.to_f - enqueued_at.to_f))
65
68
 
66
69
  args = job['args']
67
70
  if args && !args.empty?
@@ -25,7 +25,7 @@ module Datadog
25
25
  # @param [Datadog::Tracing::Transport::Traces::Transport] transport a custom transport instance.
26
26
  # If provided, overrides `transport_options` and `agent_settings`.
27
27
  # @param [Hash<Symbol,Object>] transport_options options for the default transport instance.
28
- # @param [Datadog::Tracing::Configuration::AgentSettingsResolver::AgentSettings] agent_settings agent options for
28
+ # @param [Datadog::Tracing::Configuration::AgentSettings] agent_settings agent options for
29
29
  # the default transport instance.
30
30
  def initialize(transport: nil, transport_options: {}, agent_settings: nil, logger: Datadog.logger)
31
31
  @logger = logger
@@ -79,7 +79,7 @@ module Datadog
79
79
  trace_state: nil,
80
80
  trace_state_unknown_fields: nil,
81
81
  remote_parent: false,
82
- tracer: nil,
82
+ tracer: nil, # DEV-3.0: deprecated, remove in 3.0
83
83
  baggage: nil
84
84
  )
85
85
  @logger = logger
@@ -106,7 +106,6 @@ module Datadog
106
106
  @apm_tracing_enabled = apm_tracing_enabled
107
107
  @trace_state = trace_state
108
108
  @trace_state_unknown_fields = trace_state_unknown_fields
109
- @tracer = tracer
110
109
  @baggage = baggage
111
110
 
112
111
  # Generic tags
@@ -329,7 +328,7 @@ module Datadog
329
328
  span_id = @active_span && @active_span.id
330
329
  span_id ||= @parent_span_id unless finished?
331
330
  # sample the trace_operation with the tracer
332
- @tracer&.sample_trace(self) unless sampling_priority
331
+ events.trace_propagated.publish(self)
333
332
 
334
333
  TraceDigest.new(
335
334
  span_id: span_id,
@@ -399,12 +398,14 @@ module Datadog
399
398
  attr_reader \
400
399
  :span_before_start,
401
400
  :span_finished,
402
- :trace_finished
401
+ :trace_finished,
402
+ :trace_propagated
403
403
 
404
404
  def initialize
405
405
  @span_before_start = SpanBeforeStart.new
406
406
  @span_finished = SpanFinished.new
407
407
  @trace_finished = TraceFinished.new
408
+ @trace_propagated = TracePropagated.new
408
409
  end
409
410
 
410
411
  # Triggered before a span starts.
@@ -421,6 +422,13 @@ module Datadog
421
422
  end
422
423
  end
423
424
 
425
+ # Triggered when trace is being propagated between applications or contexts
426
+ class TracePropagated < Tracing::Event
427
+ def initialize
428
+ super(:trace_propagated)
429
+ end
430
+ end
431
+
424
432
  # Triggered when the trace finishes, regardless of error.
425
433
  class TraceFinished < Tracing::Event
426
434
  def initialize