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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -2
- data/ext/datadog_profiling_loader/extconf.rb +15 -15
- data/ext/datadog_profiling_native_extension/clock_id.h +1 -0
- data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -2
- data/ext/datadog_profiling_native_extension/clock_id_noop.c +1 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +113 -43
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +49 -26
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +34 -4
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +4 -0
- data/ext/datadog_profiling_native_extension/collectors_stack.c +49 -37
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +81 -19
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +110 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +57 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +65 -60
- data/ext/datadog_profiling_native_extension/heap_recorder.c +34 -6
- data/ext/datadog_profiling_native_extension/heap_recorder.h +3 -1
- data/ext/datadog_profiling_native_extension/helpers.h +6 -17
- data/ext/datadog_profiling_native_extension/http_transport.c +3 -3
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +0 -86
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +2 -23
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +61 -172
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +64 -138
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +17 -11
- data/ext/datadog_profiling_native_extension/profiling.c +0 -2
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +0 -33
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +1 -26
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +14 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/time_helpers.c +0 -15
- data/ext/datadog_profiling_native_extension/time_helpers.h +36 -6
- data/ext/{datadog_profiling_native_extension → libdatadog_api}/crashtracker.c +19 -6
- data/ext/libdatadog_api/datadog_ruby_common.c +110 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +57 -0
- data/ext/libdatadog_api/extconf.rb +108 -0
- data/ext/libdatadog_api/macos_development.md +26 -0
- data/ext/libdatadog_extconf_helpers.rb +130 -0
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +49 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +73 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +68 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +41 -0
- data/lib/datadog/appsec/contrib/graphql/patcher.rb +37 -0
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +59 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
- data/lib/datadog/appsec/processor/actions.rb +1 -1
- data/lib/datadog/appsec/response.rb +15 -1
- data/lib/datadog/appsec.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +14 -12
- data/lib/datadog/core/configuration/settings.rb +54 -7
- data/lib/datadog/core/crashtracking/agent_base_url.rb +21 -0
- data/lib/datadog/core/crashtracking/component.rb +111 -0
- data/lib/datadog/core/crashtracking/tag_builder.rb +39 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- data/lib/datadog/core/telemetry/component.rb +49 -2
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +32 -1
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +10 -12
- data/lib/datadog/core/telemetry/http/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/transport.rb +38 -9
- data/lib/datadog/core/telemetry/logging.rb +35 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +10 -0
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +7 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +17 -17
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +3 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +4 -2
- data/lib/datadog/profiling/component.rb +69 -91
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/profiling/ext.rb +21 -21
- data/lib/datadog/profiling/flush.rb +1 -1
- data/lib/datadog/profiling/http_transport.rb +8 -6
- data/lib/datadog/profiling/load_native_extension.rb +5 -5
- data/lib/datadog/profiling/preload.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +5 -8
- data/lib/datadog/profiling/scheduler.rb +31 -25
- data/lib/datadog/profiling/tag_builder.rb +2 -2
- data/lib/datadog/profiling/tasks/exec.rb +5 -5
- data/lib/datadog/profiling/tasks/setup.rb +16 -35
- data/lib/datadog/profiling.rb +4 -5
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +1 -1
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +4 -1
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +16 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +17 -13
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +5 -1
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +22 -10
- data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +4 -1
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +14 -16
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +4 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/span.rb +9 -2
- data/lib/datadog/tracing/span_event.rb +41 -0
- data/lib/datadog/tracing/span_operation.rb +6 -2
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/version.rb +1 -1
- metadata +28 -10
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- 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
|
-
|
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:
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
rescue
|
32
|
-
|
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
|
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
|
@@ -34,19 +34,17 @@ module Datadog
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def post(env)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
23
|
-
|
24
|
-
@host =
|
25
|
-
@port =
|
26
|
-
@ssl =
|
27
|
-
@path =
|
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,
|
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
|