datadog 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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