datadog 2.2.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  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 +113 -43
  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 +49 -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 +65 -60
  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 -172
  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.h +1 -0
  31. data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
  32. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  33. data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
  34. data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
  35. data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
  36. data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
  37. data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
  38. data/ext/libdatadog_api/extconf.rb +108 -0
  39. data/ext/libdatadog_api/macos_development.md +26 -0
  40. data/ext/libdatadog_extconf_helpers.rb +130 -0
  41. data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
  42. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
  43. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
  44. data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
  45. data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
  46. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
  47. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  48. data/lib/datadog/appsec/processor/actions.rb +1 -1
  49. data/lib/datadog/appsec/response.rb +15 -1
  50. data/lib/datadog/appsec.rb +1 -0
  51. data/lib/datadog/core/configuration/components.rb +14 -12
  52. data/lib/datadog/core/configuration/settings.rb +54 -7
  53. data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
  54. data/lib/datadog/core/crashtracking/component.rb +111 -0
  55. data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
  56. data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
  57. data/lib/datadog/core/telemetry/component.rb +49 -2
  58. data/lib/datadog/core/telemetry/emitter.rb +9 -11
  59. data/lib/datadog/core/telemetry/event.rb +32 -1
  60. data/lib/datadog/core/telemetry/ext.rb +1 -0
  61. data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
  62. data/lib/datadog/core/telemetry/http/ext.rb +3 -0
  63. data/lib/datadog/core/telemetry/http/transport.rb +38 -9
  64. data/lib/datadog/core/telemetry/logging.rb +35 -0
  65. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
  66. data/lib/datadog/kit/appsec/events.rb +2 -4
  67. data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
  68. data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
  69. data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
  70. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
  71. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
  72. data/lib/datadog/profiling/collectors/info.rb +3 -3
  73. data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
  74. data/lib/datadog/profiling/component.rb +69 -91
  75. data/lib/datadog/profiling/exporter.rb +3 -3
  76. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  77. data/lib/datadog/profiling/ext.rb +21 -21
  78. data/lib/datadog/profiling/flush.rb +1 -1
  79. data/lib/datadog/profiling/http_transport.rb +8 -6
  80. data/lib/datadog/profiling/load_native_extension.rb +5 -5
  81. data/lib/datadog/profiling/preload.rb +1 -1
  82. data/lib/datadog/profiling/profiler.rb +5 -8
  83. data/lib/datadog/profiling/scheduler.rb +31 -25
  84. data/lib/datadog/profiling/tag_builder.rb +2 -2
  85. data/lib/datadog/profiling/tasks/exec.rb +5 -5
  86. data/lib/datadog/profiling/tasks/setup.rb +16 -35
  87. data/lib/datadog/profiling.rb +4 -5
  88. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  89. data/lib/datadog/tracing/contrib/ext.rb +14 -0
  90. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
  91. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
  92. data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
  93. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
  94. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
  95. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
  96. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
  97. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
  98. data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
  99. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
  100. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
  101. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
  102. data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
  103. data/lib/datadog/tracing/metadata/errors.rb +9 -1
  104. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  105. data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
  106. data/lib/datadog/tracing/span.rb +9 -2
  107. data/lib/datadog/tracing/span_event.rb +41 -0
  108. data/lib/datadog/tracing/span_operation.rb +6 -2
  109. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
  110. data/lib/datadog/version.rb +1 -1
  111. metadata +28 -10
  112. data/lib/datadog/profiling/crashtracker.rb +0 -91
  113. data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../configuration/ext'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Crashtracking
8
+ # This module provides a method to resolve the base URL of the agent
9
+ module AgentBaseUrl
10
+ def self.resolve(agent_settings)
11
+ case agent_settings.adapter
12
+ when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
13
+ "#{agent_settings.ssl ? 'https' : 'http'}://#{agent_settings.hostname}:#{agent_settings.port}/"
14
+ when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
15
+ "unix://#{agent_settings.uds_path}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'libdatadog'
4
+
5
+ require_relative 'tag_builder'
6
+ require_relative 'agent_base_url'
7
+ require_relative '../utils/only_once'
8
+ require_relative '../utils/at_fork_monkey_patch'
9
+
10
+ module Datadog
11
+ module Core
12
+ module Crashtracking
13
+ # Used to report Ruby VM crashes.
14
+ #
15
+ # NOTE: The crashtracker native state is a singleton;
16
+ # so even if you create multiple instances of `Crashtracking::Component` and start them,
17
+ # it only works as "last writer wins". Same for stop -- there's only one state, so calling stop
18
+ # on it will stop the crash tracker, regardless of which instance started it.
19
+ #
20
+ # Methods prefixed with _native_ are implemented in `crashtracker.c`
21
+ class Component
22
+ LIBDATADOG_API_FAILURE =
23
+ begin
24
+ require "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
25
+ nil
26
+ rescue LoadError => e
27
+ e.message
28
+ end
29
+
30
+ ONLY_ONCE = Core::Utils::OnlyOnce.new
31
+
32
+ def self.build(settings, agent_settings, logger:)
33
+ tags = TagBuilder.call(settings)
34
+ agent_base_url = AgentBaseUrl.resolve(agent_settings)
35
+ logger.warn('Missing agent base URL; cannot enable crash tracking') unless agent_base_url
36
+
37
+ ld_library_path = ::Libdatadog.ld_library_path
38
+ logger.warn('Missing ld_library_path; cannot enable crash tracking') unless ld_library_path
39
+
40
+ path_to_crashtracking_receiver_binary = ::Libdatadog.path_to_crashtracking_receiver_binary
41
+ unless path_to_crashtracking_receiver_binary
42
+ logger.warn('Missing path_to_crashtracking_receiver_binary; cannot enable crash tracking')
43
+ end
44
+
45
+ return unless agent_base_url
46
+ return unless ld_library_path
47
+ return unless path_to_crashtracking_receiver_binary
48
+
49
+ new(
50
+ tags: tags,
51
+ agent_base_url: agent_base_url,
52
+ ld_library_path: ld_library_path,
53
+ path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary,
54
+ logger: logger
55
+ ).tap(&:start)
56
+ end
57
+
58
+ def initialize(tags:, agent_base_url:, ld_library_path:, path_to_crashtracking_receiver_binary:, logger:)
59
+ @tags = tags
60
+ @agent_base_url = agent_base_url
61
+ @ld_library_path = ld_library_path
62
+ @path_to_crashtracking_receiver_binary = path_to_crashtracking_receiver_binary
63
+ @logger = logger
64
+ end
65
+
66
+ def start
67
+ Utils::AtForkMonkeyPatch.apply!
68
+
69
+ start_or_update_on_fork(action: :start)
70
+ ONLY_ONCE.run do
71
+ Utils::AtForkMonkeyPatch.at_fork(:child) do
72
+ # Must NOT reference `self` here, as only the first instance will
73
+ # be captured by the ONLY_ONCE and we want to pick the latest active one
74
+ # (which may have different tags or agent config)
75
+ Datadog.send(:components).crashtracker&.update_on_fork
76
+ end
77
+ end
78
+ end
79
+
80
+ def update_on_fork
81
+ start_or_update_on_fork(action: :update_on_fork)
82
+ end
83
+
84
+ def stop
85
+ self.class._native_stop
86
+ logger.debug('Crash tracking stopped successfully')
87
+ rescue => e
88
+ logger.error("Failed to stop crash tracking: #{e.message}")
89
+ end
90
+
91
+ private
92
+
93
+ attr_reader :tags, :agent_base_url, :ld_library_path, :path_to_crashtracking_receiver_binary, :logger
94
+
95
+ def start_or_update_on_fork(action:)
96
+ self.class._native_start_or_update_on_fork(
97
+ action: action,
98
+ exporter_configuration: [:agent, agent_base_url],
99
+ path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary,
100
+ ld_library_path: ld_library_path,
101
+ tags_as_array: tags.to_a,
102
+ upload_timeout_seconds: 1
103
+ )
104
+ logger.debug("Crash tracking #{action} successfully")
105
+ rescue => e
106
+ logger.error("Failed to #{action} crash tracking: #{e.message}")
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../utils'
4
+ require_relative '../environment/socket'
5
+ require_relative '../environment/identity'
6
+ require_relative '../environment/git'
7
+
8
+ module Datadog
9
+ module Core
10
+ module Crashtracking
11
+ # This module builds a hash of tags
12
+ module TagBuilder
13
+ def self.call(settings)
14
+ hash = {
15
+ 'host' => Environment::Socket.hostname,
16
+ 'process_id' => Process.pid.to_s,
17
+ 'runtime_engine' => Environment::Identity.lang_engine,
18
+ 'runtime-id' => Environment::Identity.id,
19
+ 'runtime_platform' => Environment::Identity.lang_platform,
20
+ 'runtime_version' => Environment::Identity.lang_version,
21
+ 'env' => settings.env,
22
+ 'service' => settings.service,
23
+ 'version' => settings.version,
24
+ 'git.repository_url' => Environment::Git.git_repository_url,
25
+ 'git.commit.sha' => Environment::Git.git_commit_sha,
26
+ 'is_crash' => 'true',
27
+ 'language' => 'ruby',
28
+ 'library_version' => Core::Environment::Identity.gem_datadog_version,
29
+ }.compact
30
+
31
+ # Make sure everything is an utf-8 string, to avoid encoding issues in downstream
32
+ settings.tags.merge(hash).each_with_object({}) do |(key, value), h|
33
+ h[Utils.utf8_encode(key)] = Utils.utf8_encode(value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -8,11 +8,17 @@ module Datadog
8
8
  module Core
9
9
  module Diagnostics
10
10
  # Base class for EnvironmentLoggers - should allow for easy reporting by users to Datadog support.
11
+ #
12
+ # The EnvironmentLogger should not pollute the logs in a development environment.
11
13
  module EnvironmentLogging
12
14
  def log_configuration!(prefix, data)
13
15
  logger.info("DATADOG CONFIGURATION - #{prefix} - #{data}")
14
16
  end
15
17
 
18
+ def log_debug!(prefix, data)
19
+ logger.debug("DATADOG CONFIGURATION - #{prefix} - #{data}")
20
+ end
21
+
16
22
  def log_error!(prefix, type, error)
17
23
  logger.warn("DATADOG ERROR - #{prefix} - #{type}: #{error}")
18
24
  end
@@ -27,21 +33,12 @@ module Datadog
27
33
  def log?
28
34
  startup_logs_enabled = Datadog.configuration.diagnostics.startup_logs.enabled
29
35
  if startup_logs_enabled.nil?
30
- !repl? && !rspec? # Suppress logs if we are running in a REPL or rspec
36
+ # Do not pollute the logs in a development environment.
37
+ !Datadog::Core::Environment::Execution.development?
31
38
  else
32
39
  startup_logs_enabled
33
40
  end
34
41
  end
35
-
36
- REPL_PROGRAM_NAMES = %w[irb pry].freeze
37
-
38
- def repl?
39
- REPL_PROGRAM_NAMES.include?($PROGRAM_NAME)
40
- end
41
-
42
- def rspec?
43
- $PROGRAM_NAME.end_with?('rspec')
44
- end
45
42
  end
46
43
 
47
44
  # Collects and logs Core diagnostic information
@@ -2,8 +2,11 @@
2
2
 
3
3
  require_relative 'emitter'
4
4
  require_relative 'event'
5
+ require_relative 'http/transport'
5
6
  require_relative 'metrics_manager'
6
7
  require_relative 'worker'
8
+
9
+ require_relative '../configuration/ext'
7
10
  require_relative '../utils/forking'
8
11
 
9
12
  module Datadog
@@ -15,6 +18,41 @@ module Datadog
15
18
 
16
19
  include Core::Utils::Forking
17
20
 
21
+ def self.build(settings, agent_settings, logger)
22
+ enabled = settings.telemetry.enabled
23
+ agentless_enabled = settings.telemetry.agentless_enabled
24
+
25
+ if !agentless_enabled && agent_settings.adapter != Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
26
+ enabled = false
27
+ logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
28
+ end
29
+
30
+ if agentless_enabled && settings.api_key.nil?
31
+ enabled = false
32
+ logger.debug { 'Telemetry disabled. Agentless telemetry requires an DD_API_KEY variable to be set.' }
33
+ end
34
+
35
+ transport = if agentless_enabled
36
+ Datadog::Core::Telemetry::Http::Transport.build_agentless_transport(
37
+ api_key: settings.api_key,
38
+ dd_site: settings.site,
39
+ url_override: settings.telemetry.agentless_url_override
40
+ )
41
+ else
42
+ Datadog::Core::Telemetry::Http::Transport.build_agent_transport(agent_settings)
43
+ end
44
+
45
+ Telemetry::Component.new(
46
+ http_transport: transport,
47
+ enabled: enabled,
48
+ metrics_enabled: enabled && settings.telemetry.metrics_enabled,
49
+ heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
50
+ metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
51
+ dependency_collection: settings.telemetry.dependency_collection,
52
+ shutdown_timeout_seconds: settings.telemetry.shutdown_timeout_seconds,
53
+ )
54
+ end
55
+
18
56
  # @param enabled [Boolean] Determines whether telemetry events should be sent to the API
19
57
  # @param metrics_enabled [Boolean] Determines whether telemetry metrics should be sent to the API
20
58
  # @param heartbeat_interval_seconds [Float] How frequently heartbeats will be reported, in seconds.
@@ -24,6 +62,8 @@ module Datadog
24
62
  heartbeat_interval_seconds:,
25
63
  metrics_aggregation_interval_seconds:,
26
64
  dependency_collection:,
65
+ http_transport:,
66
+ shutdown_timeout_seconds:,
27
67
  enabled: true,
28
68
  metrics_enabled: true
29
69
  )
@@ -39,9 +79,10 @@ module Datadog
39
79
  enabled: @enabled,
40
80
  heartbeat_interval_seconds: heartbeat_interval_seconds,
41
81
  metrics_aggregation_interval_seconds: metrics_aggregation_interval_seconds,
42
- emitter: Emitter.new,
82
+ emitter: Emitter.new(http_transport: http_transport),
43
83
  metrics_manager: @metrics_manager,
44
- dependency_collection: dependency_collection
84
+ dependency_collection: dependency_collection,
85
+ shutdown_timeout: shutdown_timeout_seconds
45
86
  )
46
87
  @worker.start
47
88
  end
@@ -70,6 +111,12 @@ module Datadog
70
111
  @worker.enqueue(Event::AppIntegrationsChange.new)
71
112
  end
72
113
 
114
+ def log!(event)
115
+ return unless @enabled || forked?
116
+
117
+ @worker.enqueue(event)
118
+ end
119
+
73
120
  # Report configuration changes caused by Remote Configuration.
74
121
  def client_configuration_change!(changes)
75
122
  return if !@enabled || forked?
@@ -16,22 +16,20 @@ module Datadog
16
16
 
17
17
  # @param http_transport [Datadog::Core::Telemetry::Http::Transport] Transport object that can be used to send
18
18
  # telemetry requests via the agent
19
- def initialize(http_transport: Datadog::Core::Telemetry::Http::Transport.new)
19
+ def initialize(http_transport:)
20
20
  @http_transport = http_transport
21
21
  end
22
22
 
23
23
  # Retrieves and emits a TelemetryRequest object based on the request type specified
24
24
  def request(event)
25
- begin
26
- seq_id = self.class.sequence.next
27
- payload = Request.build_payload(event, seq_id)
28
- res = @http_transport.request(request_type: event.type, payload: payload.to_json)
29
- Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (code: #{res.code.inspect})" }
30
- res
31
- rescue => e
32
- Datadog.logger.debug("Unable to send telemetry request for event `#{event.type rescue 'unknown'}`: #{e}")
33
- Telemetry::Http::InternalErrorResponse.new(e)
34
- end
25
+ seq_id = self.class.sequence.next
26
+ payload = Request.build_payload(event, seq_id)
27
+ res = @http_transport.request(request_type: event.type, payload: payload.to_json)
28
+ Datadog.logger.debug { "Telemetry sent for event `#{event.type}` (code: #{res.code.inspect})" }
29
+ res
30
+ rescue => e
31
+ Datadog.logger.debug("Unable to send telemetry request for event `#{event.type rescue 'unknown'}`: #{e}")
32
+ Telemetry::Http::InternalErrorResponse.new(e)
35
33
  end
36
34
 
37
35
  # Initializes a Sequence object to track seq_id if not already initialized; else returns stored
@@ -18,7 +18,9 @@ module Datadog
18
18
  # The type of the event.
19
19
  # It must be one of the stings defined in the Telemetry V2
20
20
  # specification for event names.
21
- def type; end
21
+ def type
22
+ raise NotImplementedError, 'Must be implemented by subclass'
23
+ end
22
24
 
23
25
  # The JSON payload for the event.
24
26
  def payload
@@ -332,6 +334,35 @@ module Datadog
332
334
  end
333
335
  end
334
336
 
337
+ # Telemetry class for the 'logs' event
338
+ class Log < Base
339
+ LEVELS = {
340
+ error: 'ERROR',
341
+ debug: 'DEBUG',
342
+ warn: 'WARN',
343
+ }.freeze
344
+
345
+ def type
346
+ 'logs'
347
+ end
348
+
349
+ def initialize(message:, level:)
350
+ super()
351
+ @message = message
352
+ @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
353
+ end
354
+
355
+ def payload
356
+ {
357
+ logs: [{
358
+ message: @message,
359
+ level: @level,
360
+ # More optional fields to be added here...
361
+ }]
362
+ }
363
+ end
364
+ end
365
+
335
366
  # Telemetry class for the 'distributions' event
336
367
  class Distributions < GenerateMetrics
337
368
  def type
@@ -12,6 +12,7 @@ module Datadog
12
12
  ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID'
13
13
  ENV_INSTALL_TYPE = 'DD_INSTRUMENTATION_INSTALL_TYPE'
14
14
  ENV_INSTALL_TIME = 'DD_INSTRUMENTATION_INSTALL_TIME'
15
+ ENV_AGENTLESS_URL_OVERRIDE = 'DD_TELEMETRY_AGENTLESS_URL'
15
16
  end
16
17
  end
17
18
  end
@@ -34,19 +34,17 @@ module Datadog
34
34
  end
35
35
 
36
36
  def post(env)
37
- begin
38
- post = ::Net::HTTP::Post.new(env.path, env.headers)
39
- post.body = env.body
40
-
41
- http_response = open do |http|
42
- http.request(post)
43
- end
44
-
45
- Response.new(http_response)
46
- rescue StandardError => e
47
- Datadog.logger.debug("Unable to send telemetry event to agent: #{e}")
48
- Telemetry::Http::InternalErrorResponse.new(e)
37
+ post = ::Net::HTTP::Post.new(env.path, env.headers)
38
+ post.body = env.body
39
+
40
+ http_response = open do |http|
41
+ http.request(post)
49
42
  end
43
+
44
+ Response.new(http_response)
45
+ rescue StandardError => e
46
+ Datadog.logger.debug("Unable to send telemetry event to agent: #{e}")
47
+ Telemetry::Http::InternalErrorResponse.new(e)
50
48
  end
51
49
 
52
50
  # Data structure for an HTTP Response
@@ -17,7 +17,10 @@ module Datadog
17
17
  CONTENT_TYPE_APPLICATION_JSON = 'application/json'
18
18
  API_VERSION = 'v2'
19
19
 
20
+ AGENTLESS_HOST_PREFIX = 'instrumentation-telemetry-intake'
21
+
20
22
  AGENT_ENDPOINT = '/telemetry/proxy/api/v2/apmtelemetry'
23
+ AGENTLESS_ENDPOINT = '/api/v2/apmtelemetry'
21
24
  end
22
25
  end
23
26
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../configuration/settings'
4
+ require_relative '../../environment/ext'
4
5
  require_relative '../../transport/ext'
5
6
  require_relative 'env'
6
7
  require_relative 'ext'
@@ -13,18 +14,42 @@ module Datadog
13
14
  # Class to send telemetry data to Telemetry API
14
15
  # Currently only supports the HTTP protocol.
15
16
  class Transport
17
+ def self.build_agent_transport(agent_settings)
18
+ Transport.new(
19
+ host: agent_settings.hostname,
20
+ port: agent_settings.port,
21
+ path: Http::Ext::AGENT_ENDPOINT
22
+ )
23
+ end
24
+
25
+ def self.build_agentless_transport(api_key:, dd_site:, url_override: nil)
26
+ url = url_override || "https://#{Http::Ext::AGENTLESS_HOST_PREFIX}.#{dd_site}:443"
27
+
28
+ uri = URI.parse(url)
29
+ raise "Invalid agentless mode URL: #{url}" if uri.host.nil?
30
+
31
+ Transport.new(
32
+ host: uri.host,
33
+ port: uri.port || 80,
34
+ path: Http::Ext::AGENTLESS_ENDPOINT,
35
+ ssl: uri.scheme == 'https' || uri.port == 443,
36
+ api_key: api_key
37
+ )
38
+ end
39
+
16
40
  attr_reader \
17
41
  :host,
18
42
  :port,
19
43
  :ssl,
20
- :path
21
-
22
- def initialize
23
- agent_settings = Configuration::AgentSettingsResolver.call(Datadog.configuration)
24
- @host = agent_settings.hostname
25
- @port = agent_settings.port
26
- @ssl = false
27
- @path = Http::Ext::AGENT_ENDPOINT
44
+ :path,
45
+ :api_key
46
+
47
+ def initialize(host:, port:, path:, ssl: false, api_key: nil)
48
+ @host = host
49
+ @port = port
50
+ @ssl = ssl
51
+ @path = path
52
+ @api_key = api_key
28
53
  end
29
54
 
30
55
  def request(request_type:, payload:)
@@ -38,7 +63,7 @@ module Datadog
38
63
  private
39
64
 
40
65
  def headers(request_type:, api_version: Http::Ext::API_VERSION)
41
- {
66
+ result = {
42
67
  Core::Transport::Ext::HTTP::HEADER_DD_INTERNAL_UNTRACED_REQUEST => '1',
43
68
  Ext::HEADER_CONTENT_TYPE => Http::Ext::CONTENT_TYPE_APPLICATION_JSON,
44
69
  Ext::HEADER_DD_TELEMETRY_API_VERSION => api_version,
@@ -49,6 +74,10 @@ module Datadog
49
74
  # Enable debug mode for telemetry
50
75
  # HEADER_TELEMETRY_DEBUG_ENABLED => 'true',
51
76
  }
77
+
78
+ result[Ext::HEADER_DD_API_KEY] = api_key unless api_key.nil?
79
+
80
+ result
52
81
  end
53
82
 
54
83
  def adapter
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'event'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ # Logging interface for sending telemetry logs.
9
+ #
10
+ # Reporting internal error so that we can fix them.
11
+ # IMPORTANT: Make sure to not log any sensitive information.
12
+ module Logging
13
+ module_function
14
+
15
+ def report(exception, level:)
16
+ # Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
17
+ message = exception.class.name || exception.class.inspect
18
+
19
+ event = Event::Log.new(
20
+ message: message,
21
+ level: level
22
+ )
23
+
24
+ if (telemetry = Datadog.send(:components).telemetry)
25
+ telemetry.log!(event)
26
+ else
27
+ Datadog.logger.debug do
28
+ "Attempting to send telemetry log when telemetry component is not ready: #{message}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Utils
6
+ # Monkey patches `Kernel#fork` and similar functions, adding an `at_fork` callback mechanism which
7
+ # is used to restart observability after the VM forks (e.g. in multiprocess Ruby apps).
8
+ module AtForkMonkeyPatch
9
+ AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant Used to store blocks to run, mutable by design.
10
+ private_constant :AT_FORK_CHILD_BLOCKS
11
+
12
+ def self.supported?
13
+ Process.respond_to?(:fork)
14
+ end
15
+
16
+ def self.apply!
17
+ return false unless supported?
18
+
19
+ if RUBY_VERSION < '3.1'
20
+ [
21
+ ::Process.singleton_class, # Process.fork
22
+ ::Kernel.singleton_class, # Kernel.fork
23
+ ::Object, # fork without explicit receiver (it's defined as a method in ::Kernel)
24
+ # Note: Modifying Object as we do here is irreversible. During tests, this
25
+ # change will stick around even if we otherwise stub `Process` and `Kernel`
26
+ ].each { |target| target.prepend(KernelMonkeyPatch) }
27
+ end
28
+
29
+ ::Process.singleton_class.prepend(ProcessMonkeyPatch)
30
+
31
+ true
32
+ end
33
+
34
+ def self.run_at_fork_blocks(stage)
35
+ raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
36
+
37
+ AT_FORK_CHILD_BLOCKS.each(&:call)
38
+ end
39
+
40
+ def self.at_fork(stage, &block)
41
+ raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
42
+ raise(ArgumentError, 'Missing block argument') unless block
43
+
44
+ AT_FORK_CHILD_BLOCKS << block
45
+
46
+ true
47
+ end
48
+
49
+ # Adds `at_fork` behavior; see parent module for details.
50
+ module KernelMonkeyPatch
51
+ def fork
52
+ # If a block is provided, it must be wrapped to trigger callbacks.
53
+ child_block = if block_given?
54
+ proc do
55
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
56
+
57
+ # Invoke original block
58
+ yield
59
+ end
60
+ end
61
+
62
+ # Start fork
63
+ # If a block is provided, use the wrapped version.
64
+ result = child_block.nil? ? super : super(&child_block)
65
+
66
+ # When fork gets called without a block, it returns twice:
67
+ # If we're in the fork, result = nil: trigger child callbacks.
68
+ # If we're in the parent, result = pid: we do nothing.
69
+ # (If it gets called with a block, it only returns on the parent)
70
+ AtForkMonkeyPatch.run_at_fork_blocks(:child) if result.nil?
71
+
72
+ result
73
+ end
74
+ end
75
+
76
+ # Adds `at_fork` behavior; see parent module for details.
77
+ module ProcessMonkeyPatch
78
+ # Hook provided by Ruby 3.1+ for observability libraries that want to know about fork, see
79
+ # https://github.com/ruby/ruby/pull/5017 and https://bugs.ruby-lang.org/issues/17795
80
+ def _fork
81
+ pid = super
82
+
83
+ AtForkMonkeyPatch.run_at_fork_blocks(:child) if pid == 0
84
+
85
+ pid
86
+ end
87
+
88
+ # A call to Process.daemon ( https://rubyapi.org/3.1/o/process#method-c-daemon ) forks the current process and
89
+ # keeps executing code in the child process, killing off the parent, thus effectively replacing it.
90
+ # This is not covered by `_fork` and thus we have some extra code for it.
91
+ def daemon(*args)
92
+ result = super
93
+
94
+ AtForkMonkeyPatch.run_at_fork_blocks(:child)
95
+
96
+ result
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -53,13 +53,11 @@ module Datadog
53
53
  # @param user_exists [bool] Whether the user id that did a login attempt exists.
54
54
  # @param others [Hash<String || Symbol, String>] Additional free-form
55
55
  # event information to attach to the trace.
56
- def track_login_failure(trace = nil, span = nil, user_id:, user_exists:, **others)
56
+ def track_login_failure(trace = nil, span = nil, user_exists:, user_id: nil, **others)
57
57
  set_trace_and_span_context('track_login_failure', trace, span) do |active_trace, active_span|
58
- raise ArgumentError, 'user_id cannot be nil' if user_id.nil?
59
-
60
58
  track(LOGIN_FAILURE_EVENT, active_trace, active_span, **others)
61
59
 
62
- active_span.set_tag('appsec.events.users.login.failure.usr.id', user_id)
60
+ active_span.set_tag('appsec.events.users.login.failure.usr.id', user_id) if user_id
63
61
  active_span.set_tag('appsec.events.users.login.failure.usr.exists', user_exists)
64
62
  end
65
63
  end