datadog 2.14.0 → 2.15.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -2
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +7 -6
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +3 -0
  5. data/ext/datadog_profiling_native_extension/encoded_profile.c +69 -0
  6. data/ext/datadog_profiling_native_extension/encoded_profile.h +7 -0
  7. data/ext/datadog_profiling_native_extension/http_transport.c +25 -32
  8. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  9. data/ext/datadog_profiling_native_extension/stack_recorder.c +22 -21
  10. data/ext/libdatadog_api/datadog_ruby_common.h +3 -0
  11. data/lib/datadog/appsec/assets/waf_rules/README.md +50 -5
  12. data/lib/datadog/appsec/assets/waf_rules/processors.json +239 -10
  13. data/lib/datadog/appsec/assets/waf_rules/recommended.json +0 -1344
  14. data/lib/datadog/appsec/assets/waf_rules/scanners.json +926 -17
  15. data/lib/datadog/appsec/assets/waf_rules/strict.json +0 -1344
  16. data/lib/datadog/appsec/component.rb +19 -17
  17. data/lib/datadog/appsec/compressed_json.rb +40 -0
  18. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  19. data/lib/datadog/appsec/event.rb +21 -50
  20. data/lib/datadog/appsec/remote.rb +4 -0
  21. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  22. data/lib/datadog/core/telemetry/metric.rb +5 -5
  23. data/lib/datadog/core/telemetry/request.rb +1 -1
  24. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  25. data/lib/datadog/di/transport/http/diagnostics.rb +0 -1
  26. data/lib/datadog/di/transport/http/input.rb +0 -1
  27. data/lib/datadog/di/transport/http.rb +0 -6
  28. data/lib/datadog/profiling/collectors/info.rb +3 -0
  29. data/lib/datadog/profiling/encoded_profile.rb +11 -0
  30. data/lib/datadog/profiling/exporter.rb +2 -3
  31. data/lib/datadog/profiling/ext.rb +0 -1
  32. data/lib/datadog/profiling/flush.rb +4 -7
  33. data/lib/datadog/profiling/http_transport.rb +10 -59
  34. data/lib/datadog/profiling/stack_recorder.rb +4 -4
  35. data/lib/datadog/profiling.rb +1 -0
  36. data/lib/datadog/tracing/contrib/active_record/integration.rb +1 -1
  37. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +17 -0
  38. data/lib/datadog/tracing/contrib/opensearch/ext.rb +9 -0
  39. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +5 -1
  40. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  41. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  42. data/lib/datadog/tracing/span_event.rb +1 -1
  43. data/lib/datadog/version.rb +1 -1
  44. metadata +10 -6
@@ -12,7 +12,25 @@ module Datadog
12
12
  class << self
13
13
  def build_appsec_component(settings, telemetry:)
14
14
  return if !settings.respond_to?(:appsec) || !settings.appsec.enabled
15
- return if incompatible_ffi_version?
15
+
16
+ ffi_version = Gem.loaded_specs['ffi'] && Gem.loaded_specs['ffi'].version
17
+ unless ffi_version
18
+ Datadog.logger.warn('FFI gem is not loaded, AppSec will be disabled.')
19
+ telemetry.error('AppSec: Component not loaded, due to missing FFI gem')
20
+
21
+ return
22
+ end
23
+
24
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3') && ffi_version < Gem::Version.new('1.16.0')
25
+ Datadog.logger.warn(
26
+ 'AppSec is not supported in Ruby versions above 3.3.0 when using `ffi` versions older than 1.16.0, ' \
27
+ 'and will be forcibly disabled due to a memory leak in `ffi`. ' \
28
+ 'Please upgrade your `ffi` version to 1.16.0 or higher.'
29
+ )
30
+ telemetry.error('AppSec: Component not loaded, ffi version is leaky with ruby > 3.3.0')
31
+
32
+ return
33
+ end
16
34
 
17
35
  processor = create_processor(settings, telemetry)
18
36
 
@@ -29,22 +47,6 @@ module Datadog
29
47
 
30
48
  private
31
49
 
32
- def incompatible_ffi_version?
33
- ffi_version = Gem.loaded_specs['ffi'] && Gem.loaded_specs['ffi'].version
34
- return true unless ffi_version
35
-
36
- return false unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3') &&
37
- ffi_version < Gem::Version.new('1.16.0')
38
-
39
- Datadog.logger.warn(
40
- 'AppSec is not supported in Ruby versions above 3.3.0 when using `ffi` versions older than 1.16.0, ' \
41
- 'and will be forcibly disabled due to a memory leak in `ffi`. ' \
42
- 'Please upgrade your `ffi` version to 1.16.0 or higher.'
43
- )
44
-
45
- true
46
- end
47
-
48
50
  def create_processor(settings, telemetry)
49
51
  rules = AppSec::Processor::RuleLoader.load_rules(
50
52
  telemetry: telemetry,
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'zlib'
5
+ require 'stringio'
6
+
7
+ require_relative '../core/utils/base64'
8
+
9
+ module Datadog
10
+ module AppSec
11
+ # Converts derivative schema payloads into JSON and compresses them into a
12
+ # base64 encoded string if the payload is worth compressing.
13
+ #
14
+ # See: https://github.com/DataDog/dd-trace-rb/pull/3177#issuecomment-1747221082
15
+ module CompressedJson
16
+ MIN_SIZE_FOR_COMPRESSION = 260
17
+
18
+ def self.dump(payload)
19
+ value = JSON.dump(payload)
20
+ return value if value.bytesize < MIN_SIZE_FOR_COMPRESSION
21
+
22
+ compress_and_encode(value)
23
+ rescue ArgumentError, Encoding::UndefinedConversionError, JSON::JSONError => e
24
+ AppSec.telemetry.report(e, description: 'AppSec: Failed to convert value into JSON')
25
+
26
+ nil
27
+ end
28
+
29
+ private_class_method def self.compress_and_encode(payload)
30
+ Core::Utils::Base64.strict_encode64(
31
+ Zlib.gzip(payload, level: Zlib::BEST_SPEED, strategy: Zlib::DEFAULT_STRATEGY)
32
+ )
33
+ rescue Zlib::Error, TypeError => e
34
+ AppSec.telemetry.report(e, description: 'AppSec: Failed to compress and encode value')
35
+
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -13,7 +13,7 @@ module Datadog
13
13
 
14
14
  MINIMUM_VERSION = Gem::Version.new('4')
15
15
 
16
- register_as :active_record, auto_patch: false
16
+ register_as :active_record, auto_patch: true
17
17
 
18
18
  def self.version
19
19
  Gem.loaded_specs['activerecord'] && Gem.loaded_specs['activerecord'].version
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'zlib'
5
-
6
4
  require_relative 'rate_limiter'
7
- require_relative '../core/utils/base64'
5
+ require_relative 'compressed_json'
8
6
 
9
7
  module Datadog
10
8
  module AppSec
11
9
  # AppSec event
12
10
  module Event
11
+ DERIVATIVE_SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
12
+ DERIVATIVE_SCHEMA_MAX_COMPRESSED_SIZE = 25000
13
13
  ALLOWED_REQUEST_HEADERS = %w[
14
14
  X-Forwarded-For
15
15
  X-Client-IP
@@ -38,11 +38,6 @@ module Datadog
38
38
  Content-Language
39
39
  ].map!(&:downcase).freeze
40
40
 
41
- MAX_ENCODED_SCHEMA_SIZE = 25000
42
- # For more information about this number
43
- # please check https://github.com/DataDog/dd-trace-rb/pull/3177#issuecomment-1747221082
44
- MIN_SCHEMA_SIZE_FOR_COMPRESSION = 260
45
-
46
41
  # Record events for a trace
47
42
  #
48
43
  # This is expected to be called only once per trace for the rate limiter
@@ -80,7 +75,6 @@ module Datadog
80
75
  end
81
76
  end
82
77
 
83
- # rubocop:disable Metrics/MethodLength
84
78
  def build_service_entry_tags(event_group)
85
79
  waf_events = []
86
80
  entry_tags = event_group.each_with_object({ '_dd.origin' => 'appsec' }) do |event, tags|
@@ -106,26 +100,17 @@ module Datadog
106
100
  waf_events += waf_result.events
107
101
 
108
102
  waf_result.derivatives.each do |key, value|
109
- parsed_value = json_parse(value)
110
- next unless parsed_value
111
-
112
- parsed_value_size = parsed_value.size
113
-
114
- schema_value = if parsed_value_size >= MIN_SCHEMA_SIZE_FOR_COMPRESSION
115
- compressed_and_base64_encoded(parsed_value)
116
- else
117
- parsed_value
118
- end
119
- next unless schema_value
120
-
121
- if schema_value.size >= MAX_ENCODED_SCHEMA_SIZE
122
- Datadog.logger.debug do
123
- "Schema key: #{key} exceeds the max size value. It will not be included as part of the span tags"
124
- end
103
+ next tags[key] = value unless key.start_with?(DERIVATIVE_SCHEMA_KEY_PREFIX)
104
+
105
+ value = CompressedJson.dump(value)
106
+ next if value.nil?
107
+
108
+ if value.size >= DERIVATIVE_SCHEMA_MAX_COMPRESSED_SIZE
109
+ Datadog.logger.debug { "AppSec: Schema key '#{key}' will not be included into span tags due to it's size" }
125
110
  next
126
111
  end
127
112
 
128
- tags[key] = schema_value
113
+ tags[key] = value
129
114
  end
130
115
 
131
116
  tags
@@ -135,14 +120,16 @@ module Datadog
135
120
  entry_tags['_dd.appsec.json'] = appsec_events if appsec_events
136
121
  entry_tags
137
122
  end
138
- # rubocop:enable Metrics/MethodLength
139
123
 
140
124
  def tag_and_keep!(context, waf_result)
141
125
  # We want to keep the trace in case of security event
142
126
  context.trace.keep! if context.trace
143
127
 
144
128
  if context.span
145
- context.span.set_tag('appsec.blocked', 'true') if waf_result.actions.key?('block_request')
129
+ if waf_result.actions.key?('block_request') || waf_result.actions.key?('redirect_request')
130
+ context.span.set_tag('appsec.blocked', 'true')
131
+ end
132
+
146
133
  context.span.set_tag('appsec.event', 'true')
147
134
  end
148
135
 
@@ -151,31 +138,15 @@ module Datadog
151
138
 
152
139
  private
153
140
 
154
- def compressed_and_base64_encoded(value)
155
- Datadog::Core::Utils::Base64.strict_encode64(gzip(value))
156
- rescue TypeError => e
157
- Datadog.logger.debug do
158
- "Failed to compress and encode value when populating AppSec::Event. Error: #{e.message}"
159
- end
160
- nil
161
- end
162
-
141
+ # NOTE: Handling of Encoding::UndefinedConversionError is added as a quick fix to
142
+ # the issue between Ruby encoded strings and libddwaf produced events and now
143
+ # is under investigation.
163
144
  def json_parse(value)
164
145
  JSON.dump(value)
165
- rescue ArgumentError => e
166
- Datadog.logger.debug do
167
- "Failed to parse value to JSON when populating AppSec::Event. Error: #{e.message}"
168
- end
169
- nil
170
- end
146
+ rescue ArgumentError, Encoding::UndefinedConversionError, JSON::JSONError => e
147
+ AppSec.telemetry.report(e, description: 'AppSec: Failed to convert value into JSON')
171
148
 
172
- def gzip(value)
173
- sio = StringIO.new
174
- # For an in depth comparison of Zlib options check https://github.com/DataDog/dd-trace-rb/pull/3177#issuecomment-1747215473
175
- gz = Zlib::GzipWriter.new(sio, Zlib::BEST_SPEED, Zlib::DEFAULT_STRATEGY)
176
- gz.write(value)
177
- gz.close
178
- sio.string
149
+ nil
179
150
  end
180
151
 
181
152
  # Propagate to downstream services the information that the current distributed trace is
@@ -23,6 +23,8 @@ module Datadog
23
23
  CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
24
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
25
  CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
26
+ CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
27
+ CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
26
28
 
27
29
  # TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
28
30
  ASM_CAPABILITIES = [
@@ -35,6 +37,8 @@ module Datadog
35
37
  CAP_ASM_CUSTOM_RULES,
36
38
  CAP_ASM_CUSTOM_BLOCKING_RESPONSE,
37
39
  CAP_ASM_TRUSTED_IPS,
40
+ CAP_ASM_RASP_SSRF,
41
+ CAP_ASM_RASP_SQLI,
38
42
  ].freeze
39
43
 
40
44
  ASM_PRODUCTS = [
@@ -79,7 +79,7 @@ module Datadog
79
79
 
80
80
  # @return [String] current time in ISO8601 format
81
81
  def date
82
- Time.now.utc.iso8601
82
+ Core::Utils::Time.now.utc.iso8601
83
83
  end
84
84
 
85
85
  # Best portable guess of OS information.
@@ -108,9 +108,9 @@ module Datadog
108
108
  value = value.to_i
109
109
 
110
110
  if values.empty?
111
- values << [Time.now.to_i, value]
111
+ values << [Core::Utils::Time.now.to_i, value]
112
112
  else
113
- values[0][0] = Time.now.to_i
113
+ values[0][0] = Core::Utils::Time.now.to_i
114
114
  values[0][1] += value
115
115
  end
116
116
  nil
@@ -129,9 +129,9 @@ module Datadog
129
129
 
130
130
  def track(value)
131
131
  if values.empty?
132
- values << [Time.now.to_i, value]
132
+ values << [Core::Utils::Time.now.to_i, value]
133
133
  else
134
- values[0][0] = Time.now.to_i
134
+ values[0][0] = Core::Utils::Time.now.to_i
135
135
  values[0][1] = value
136
136
  end
137
137
  nil
@@ -155,7 +155,7 @@ module Datadog
155
155
 
156
156
  def track(value = 1.0)
157
157
  @value += value
158
- @values = [[Time.now.to_i, @value / interval]]
158
+ @values = [[Core::Utils::Time.now.to_i, @value / interval]]
159
159
  nil
160
160
  end
161
161
  end
@@ -21,7 +21,7 @@ module Datadog
21
21
  request_type: event.type,
22
22
  runtime_id: Core::Environment::Identity.id,
23
23
  seq_id: seq_id,
24
- tracer_time: Time.now.to_i,
24
+ tracer_time: Core::Utils::Time.now.to_i,
25
25
  }
26
26
  hash.compact!
27
27
  hash
@@ -191,7 +191,7 @@ module Datadog
191
191
  end
192
192
 
193
193
  def timestamp_now
194
- (Time.now.to_f * 1000).to_i
194
+ (Core::Utils::Time.now.to_f * 1000).to_i
195
195
  end
196
196
 
197
197
  def get_local_variables(trace_point)
@@ -38,7 +38,6 @@ module Datadog
38
38
  end
39
39
  end
40
40
 
41
- # Endpoint for negotiation
42
41
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
43
42
  attr_reader :encoder
44
43
 
@@ -38,7 +38,6 @@ module Datadog
38
38
  end
39
39
  end
40
40
 
41
- # Endpoint for negotiation
42
41
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
43
42
  HEADER_CONTENT_TYPE = 'Content-Type'
44
43
 
@@ -1,15 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'uri'
4
-
5
- require_relative '../../core/environment/container'
6
- require_relative '../../core/environment/ext'
7
- require_relative '../../core/transport/ext'
8
3
  require_relative 'diagnostics'
9
4
  require_relative 'input'
10
5
  require_relative 'http/api'
11
6
  require_relative '../../core/transport/http'
12
- require_relative '../../../datadog/version'
13
7
 
14
8
  module Datadog
15
9
  module DI
@@ -31,6 +31,9 @@ module Datadog
31
31
  # Instead of trying to figure out real process start time by checking
32
32
  # /proc or some other complex/non-portable way, approximate start time
33
33
  # by time of requirement of this file.
34
+ #
35
+ # Note: this does not use Core::Utils::Time.now because this constant
36
+ # gets initialized before a user has a chance to configure the library.
34
37
  START_TIME = Time.now.utc.freeze
35
38
 
36
39
  def collect_platform_info
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Profiling
5
+ # This class exists to wrap a ddog_prof_EncodedProfile into a Ruby object
6
+ #
7
+ # This class is not empty; all of this class is implemented as native code.
8
+ class EncodedProfile # rubocop:disable Lint/EmptyClass
9
+ end
10
+ end
11
+ end
@@ -57,7 +57,7 @@ module Datadog
57
57
  serialization_result = pprof_recorder.serialize
58
58
  return if serialization_result.nil?
59
59
 
60
- start, finish, compressed_pprof, profile_stats = serialization_result
60
+ start, finish, encoded_profile, profile_stats = serialization_result
61
61
  @last_flush_finish_at = finish
62
62
 
63
63
  if duration_below_threshold?(start, finish)
@@ -70,8 +70,7 @@ module Datadog
70
70
  Flush.new(
71
71
  start: start,
72
72
  finish: finish,
73
- pprof_file_name: Datadog::Profiling::Ext::Transport::HTTP::PPROF_DEFAULT_FILENAME,
74
- pprof_data: compressed_pprof.to_s,
73
+ encoded_profile: encoded_profile,
75
74
  code_provenance_file_name: Datadog::Profiling::Ext::Transport::HTTP::CODE_PROVENANCE_FILENAME,
76
75
  code_provenance_data: uncompressed_code_provenance,
77
76
  tags_as_array: Datadog::Profiling::TagBuilder.call(settings: Datadog.configuration).to_a,
@@ -26,7 +26,6 @@ module Datadog
26
26
  TAG_GIT_REPOSITORY_URL = "git.repository_url"
27
27
  TAG_GIT_COMMIT_SHA = "git.commit.sha"
28
28
 
29
- PPROF_DEFAULT_FILENAME = "rubyprofile.pprof"
30
29
  CODE_PROVENANCE_FILENAME = "code-provenance.json"
31
30
  end
32
31
  end
@@ -9,10 +9,9 @@ module Datadog
9
9
  attr_reader \
10
10
  :start,
11
11
  :finish,
12
- :pprof_file_name,
13
- :pprof_data, # gzipped pprof bytes
12
+ :encoded_profile,
14
13
  :code_provenance_file_name,
15
- :code_provenance_data, # gzipped json bytes
14
+ :code_provenance_data,
16
15
  :tags_as_array,
17
16
  :internal_metadata_json,
18
17
  :info_json
@@ -20,8 +19,7 @@ module Datadog
20
19
  def initialize(
21
20
  start:,
22
21
  finish:,
23
- pprof_file_name:,
24
- pprof_data:,
22
+ encoded_profile:,
25
23
  code_provenance_file_name:,
26
24
  code_provenance_data:,
27
25
  tags_as_array:,
@@ -30,8 +28,7 @@ module Datadog
30
28
  )
31
29
  @start = start
32
30
  @finish = finish
33
- @pprof_file_name = pprof_file_name
34
- @pprof_data = pprof_data
31
+ @encoded_profile = encoded_profile
35
32
  @code_provenance_file_name = code_provenance_file_name
36
33
  @code_provenance_data = code_provenance_data
37
34
  @tags_as_array = tags_as_array
@@ -20,34 +20,21 @@ module Datadog
20
20
  [:agent, agent_settings.url].freeze
21
21
  end
22
22
 
23
- status, result = validate_exporter(exporter_configuration)
23
+ status, result = self.class._native_validate_exporter(exporter_configuration)
24
24
 
25
25
  raise(ArgumentError, "Failed to initialize transport: #{result}") if status == :error
26
26
  end
27
27
 
28
28
  def export(flush)
29
- status, result = do_export(
30
- exporter_configuration: exporter_configuration,
31
- upload_timeout_milliseconds: @upload_timeout_milliseconds,
32
-
33
- # why "timespec"?
34
- # libdatadog represents time using POSIX's struct timespec, see
35
- # https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
36
- # aka it represents the seconds part separate from the nanoseconds part
37
- start_timespec_seconds: flush.start.tv_sec,
38
- start_timespec_nanoseconds: flush.start.tv_nsec,
39
- finish_timespec_seconds: flush.finish.tv_sec,
40
- finish_timespec_nanoseconds: flush.finish.tv_nsec,
41
-
42
- pprof_file_name: flush.pprof_file_name,
43
- pprof_data: flush.pprof_data,
44
- code_provenance_file_name: flush.code_provenance_file_name,
45
- code_provenance_data: flush.code_provenance_data,
46
-
47
- tags_as_array: flush.tags_as_array,
48
- internal_metadata_json: flush.internal_metadata_json,
49
-
50
- info_json: flush.info_json
29
+ status, result = self.class._native_do_export(
30
+ exporter_configuration,
31
+ @upload_timeout_milliseconds,
32
+ flush,
33
+ # TODO: This is going to be removed once we move to libdatadog 17
34
+ flush.start.tv_sec,
35
+ flush.start.tv_nsec,
36
+ flush.finish.tv_sec,
37
+ flush.finish.tv_nsec,
51
38
  )
52
39
 
53
40
  if status == :ok
@@ -77,42 +64,6 @@ module Datadog
77
64
  site && api_key && Core::Environment::VariableHelpers.env_to_bool(Profiling::Ext::ENV_AGENTLESS, false)
78
65
  end
79
66
 
80
- def validate_exporter(exporter_configuration)
81
- self.class._native_validate_exporter(exporter_configuration)
82
- end
83
-
84
- def do_export(
85
- exporter_configuration:,
86
- upload_timeout_milliseconds:,
87
- start_timespec_seconds:,
88
- start_timespec_nanoseconds:,
89
- finish_timespec_seconds:,
90
- finish_timespec_nanoseconds:,
91
- pprof_file_name:,
92
- pprof_data:,
93
- code_provenance_file_name:,
94
- code_provenance_data:,
95
- tags_as_array:,
96
- internal_metadata_json:,
97
- info_json:
98
- )
99
- self.class._native_do_export(
100
- exporter_configuration,
101
- upload_timeout_milliseconds,
102
- start_timespec_seconds,
103
- start_timespec_nanoseconds,
104
- finish_timespec_seconds,
105
- finish_timespec_nanoseconds,
106
- pprof_file_name,
107
- pprof_data,
108
- code_provenance_file_name,
109
- code_provenance_data,
110
- tags_as_array,
111
- internal_metadata_json,
112
- info_json,
113
- )
114
- end
115
-
116
67
  def config_without_api_key
117
68
  "#{exporter_configuration[0]}: #{exporter_configuration[1]}"
118
69
  end
@@ -63,11 +63,11 @@ module Datadog
63
63
  status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
64
64
 
65
65
  if status == :ok
66
- start, finish, encoded_pprof, profile_stats = result
66
+ start, finish, encoded_profile, profile_stats = result
67
67
 
68
68
  Datadog.logger.debug { "Encoded profile covering #{start.iso8601} to #{finish.iso8601}" }
69
69
 
70
- [start, finish, encoded_pprof, profile_stats]
70
+ [start, finish, encoded_profile, profile_stats]
71
71
  else
72
72
  error_message = result
73
73
 
@@ -82,9 +82,9 @@ module Datadog
82
82
  status, result = @no_concurrent_synchronize_mutex.synchronize { self.class._native_serialize(self) }
83
83
 
84
84
  if status == :ok
85
- _start, _finish, encoded_pprof = result
85
+ _start, _finish, encoded_profile = result
86
86
 
87
- encoded_pprof
87
+ encoded_profile
88
88
  else
89
89
  error_message = result
90
90
 
@@ -149,6 +149,7 @@ module Datadog
149
149
  require_relative 'profiling/collectors/thread_context'
150
150
  require_relative 'profiling/stack_recorder'
151
151
  require_relative 'profiling/exporter'
152
+ require_relative 'profiling/encoded_profile'
152
153
  require_relative 'profiling/flush'
153
154
  require_relative 'profiling/scheduler'
154
155
  require_relative 'profiling/tasks/setup'
@@ -57,7 +57,7 @@ module Datadog
57
57
  end
58
58
 
59
59
  def reset_resolver_cache
60
- @resolver&.reset_cache
60
+ @resolver&.reset_cache if defined?(@resolver)
61
61
  end
62
62
 
63
63
  Contrib::Component.register('activerecord') do |_config|
@@ -46,6 +46,23 @@ module Datadog
46
46
  o.type :string, nilable: true
47
47
  o.env Ext::ENV_PEER_SERVICE
48
48
  end
49
+
50
+ option :resource_pattern do |o|
51
+ o.type :string
52
+ o.env Ext::ENV_RESOURCE_PATTERN
53
+ o.default Ext::DEFAULT_RESOURCE_PATTERN
54
+ o.setter do |value|
55
+ next value if Ext::VALID_RESOURCE_PATTERNS.include?(value)
56
+
57
+ Datadog.logger.warn(
58
+ "Invalid resource pattern: #{value}. " \
59
+ "Supported values are: #{Ext::VALID_RESOURCE_PATTERNS.join(' | ')}. " \
60
+ "Using default value: #{Ext::DEFAULT_RESOURCE_PATTERN}."
61
+ )
62
+
63
+ Ext::DEFAULT_RESOURCE_PATTERN
64
+ end
65
+ end
49
66
  end
50
67
  end
51
68
  end
@@ -13,6 +13,15 @@ module Datadog
13
13
  # @!visibility private
14
14
  ENV_ANALYTICS_ENABLED = 'DD_TRACE_OPENSEARCH_ANALYTICS_ENABLED'
15
15
  ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_OPENSEARCH_ANALYTICS_SAMPLE_RATE'
16
+ ENV_RESOURCE_PATTERN = 'DD_TRACE_OPENSEARCH_RESOURCE_PATTERN'
17
+ ABSOLUTE_RESOURCE_PATTERN = 'absolute'
18
+ RELATIVE_RESOURCE_PATTERN = 'relative'
19
+ VALID_RESOURCE_PATTERNS = [
20
+ ABSOLUTE_RESOURCE_PATTERN,
21
+ RELATIVE_RESOURCE_PATTERN
22
+ ].freeze
23
+ # Default should be changed to RELATIVE in 3.0 to match the Elasticsearch integration
24
+ DEFAULT_RESOURCE_PATTERN = ABSOLUTE_RESOURCE_PATTERN
16
25
  DEFAULT_PEER_SERVICE_NAME = 'opensearch'
17
26
  SPAN_QUERY = 'opensearch.query'
18
27
  SPAN_TYPE_QUERY = 'opensearch'
@@ -77,7 +77,11 @@ module Datadog
77
77
  span.set_tag(Tracing::Metadata::Ext::TAG_PEER_HOSTNAME, host) if host
78
78
 
79
79
  # Define span resource
80
- quantized_url = OpenSearch::Quantize.format_url(url)
80
+ quantized_url = if datadog_configuration[:resource_pattern] == Ext::RELATIVE_RESOURCE_PATTERN
81
+ OpenSearch::Quantize.format_url(url.path)
82
+ else # Default to Ext::ABSOLUTE_RESOURCE_PATTERN
83
+ OpenSearch::Quantize.format_url(url)
84
+ end
81
85
  span.resource = "#{method} #{quantized_url}"
82
86
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
83
87
  rescue StandardError => e
@@ -17,7 +17,7 @@ module Datadog
17
17
 
18
18
  module_function
19
19
 
20
- def get_request_start(env, now = Time.now.utc)
20
+ def get_request_start(env, now = Core::Utils::Time.now.utc)
21
21
  header = env[REQUEST_START] || env[QUEUE_START]
22
22
  return unless header
23
23
 
@@ -61,7 +61,7 @@ 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 * (Time.now.utc.to_f - job['enqueued_at'].to_f))
64
+ span.set_tag(Ext::TAG_JOB_DELAY, 1000.0 * (Core::Utils::Time.now.utc.to_f - job['enqueued_at'].to_f))
65
65
 
66
66
  args = job['args']
67
67
  if args && !args.empty?
@@ -33,7 +33,7 @@ module Datadog
33
33
 
34
34
  # OpenTelemetry SDK stores span event timestamps in nanoseconds (not seconds).
35
35
  # We will do the same here to avoid unnecessary conversions and inconsistencies.
36
- @time_unix_nano = time_unix_nano || (Time.now.to_r * 1_000_000_000).to_i
36
+ @time_unix_nano = time_unix_nano || (Core::Utils::Time.now.to_r * 1_000_000_000).to_i
37
37
  end
38
38
 
39
39
  # Converts the span event into a hash to be used by with the span tag serialization
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 14
6
+ MINOR = 15
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil