datadog 2.2.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -2
- data/ext/datadog_profiling_loader/datadog_profiling_loader.c +9 -1
- data/ext/datadog_profiling_loader/extconf.rb +14 -26
- 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 +257 -69
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +53 -28
- 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 +136 -81
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +661 -48
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -1
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +83 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +53 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +91 -69
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +50 -0
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +75 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +54 -12
- 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 +41 -9
- 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 +116 -139
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +20 -11
- data/ext/datadog_profiling_native_extension/profiling.c +1 -3
- 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 +2 -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 +37 -22
- data/ext/libdatadog_api/datadog_ruby_common.c +83 -0
- data/ext/libdatadog_api/datadog_ruby_common.h +53 -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/assets/waf_rules/recommended.json +2184 -108
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1430 -2
- data/lib/datadog/appsec/component.rb +29 -8
- data/lib/datadog/appsec/configuration/settings.rb +2 -2
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +1 -0
- data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +21 -0
- data/lib/datadog/appsec/contrib/devise/patcher.rb +12 -2
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +35 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +109 -0
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +71 -0
- data/lib/datadog/appsec/contrib/graphql/integration.rb +54 -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 +3 -6
- data/lib/datadog/appsec/event.rb +1 -1
- data/lib/datadog/appsec/processor/actions.rb +1 -1
- data/lib/datadog/appsec/processor/rule_loader.rb +3 -1
- data/lib/datadog/appsec/processor/rule_merger.rb +33 -15
- data/lib/datadog/appsec/processor.rb +36 -37
- data/lib/datadog/appsec/rate_limiter.rb +25 -40
- data/lib/datadog/appsec/remote.rb +7 -3
- data/lib/datadog/appsec/response.rb +15 -1
- data/lib/datadog/appsec.rb +3 -2
- data/lib/datadog/core/configuration/components.rb +18 -15
- data/lib/datadog/core/configuration/settings.rb +135 -9
- 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/environment/execution.rb +5 -5
- data/lib/datadog/core/metrics/client.rb +7 -0
- data/lib/datadog/core/rate_limiter.rb +183 -0
- data/lib/datadog/core/remote/client/capabilities.rb +4 -3
- data/lib/datadog/core/remote/component.rb +4 -2
- data/lib/datadog/core/remote/negotiation.rb +4 -4
- data/lib/datadog/core/remote/tie.rb +2 -0
- data/lib/datadog/core/runtime/metrics.rb +1 -1
- data/lib/datadog/core/telemetry/component.rb +51 -2
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +37 -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/logger.rb +51 -0
- data/lib/datadog/core/telemetry/logging.rb +71 -0
- data/lib/datadog/core/telemetry/request.rb +13 -1
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/di/code_tracker.rb +168 -0
- data/lib/datadog/di/configuration/settings.rb +163 -0
- data/lib/datadog/di/configuration.rb +11 -0
- data/lib/datadog/di/error.rb +31 -0
- data/lib/datadog/di/extensions.rb +16 -0
- data/lib/datadog/di/probe.rb +133 -0
- data/lib/datadog/di/probe_builder.rb +41 -0
- data/lib/datadog/di/redactor.rb +188 -0
- data/lib/datadog/di/serializer.rb +193 -0
- data/lib/datadog/di.rb +14 -0
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/propagator.rb +2 -0
- 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 +28 -26
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +11 -13
- data/lib/datadog/profiling/collectors/info.rb +15 -6
- data/lib/datadog/profiling/collectors/thread_context.rb +30 -2
- data/lib/datadog/profiling/component.rb +89 -95
- 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 +14 -7
- 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 +33 -25
- data/lib/datadog/profiling/stack_recorder.rb +3 -0
- 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/single_step_instrument.rb +12 -0
- data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +8 -12
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +78 -0
- data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/action_pack/patcher.rb +2 -0
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +4 -0
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +9 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +19 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +9 -12
- data/lib/datadog/tracing/contrib/graphql/trace_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/tracing_patcher.rb +3 -3
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +14 -10
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +10 -4
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +18 -15
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -5
- data/lib/datadog/tracing/contrib/httpclient/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +5 -0
- data/lib/datadog/tracing/contrib/httprb/patcher.rb +1 -14
- data/lib/datadog/tracing/contrib/lograge/patcher.rb +15 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -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/opensearch/patcher.rb +13 -6
- data/lib/datadog/tracing/contrib/patcher.rb +2 -1
- 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/presto/patcher.rb +1 -13
- 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/rack/middlewares.rb +27 -0
- data/lib/datadog/tracing/contrib/redis/tags.rb +4 -0
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +4 -0
- data/lib/datadog/tracing/contrib/stripe/request.rb +3 -2
- 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/distributed/propagation.rb +7 -0
- data/lib/datadog/tracing/metadata/errors.rb +9 -1
- data/lib/datadog/tracing/metadata/ext.rb +6 -0
- data/lib/datadog/tracing/pipeline/span_filter.rb +2 -2
- data/lib/datadog/tracing/remote.rb +5 -2
- data/lib/datadog/tracing/sampling/matcher.rb +6 -1
- data/lib/datadog/tracing/sampling/rate_sampler.rb +1 -1
- data/lib/datadog/tracing/sampling/rule.rb +2 -0
- data/lib/datadog/tracing/sampling/rule_sampler.rb +9 -5
- data/lib/datadog/tracing/sampling/span/ext.rb +1 -1
- data/lib/datadog/tracing/sampling/span/rule.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/trace_operation.rb +26 -2
- data/lib/datadog/tracing/tracer.rb +14 -12
- data/lib/datadog/tracing/transport/http/client.rb +1 -0
- data/lib/datadog/tracing/transport/io/client.rb +1 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/tracing/workers/trace_writer.rb +1 -1
- data/lib/datadog/tracing/workers.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +46 -11
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
- data/lib/datadog/tracing/sampling/rate_limiter.rb +0 -185
@@ -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,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Core
|
5
|
+
module Telemetry
|
6
|
+
# === INTERNAL USAGE ONLY ===
|
7
|
+
#
|
8
|
+
# Report telemetry logs via delegating to the telemetry component instance via mutex.
|
9
|
+
#
|
10
|
+
# IMPORTANT: Invoking this method during the lifecycle of component initialization will
|
11
|
+
# be no-op instead.
|
12
|
+
#
|
13
|
+
# For developer using this module:
|
14
|
+
# read: lib/datadog/core/telemetry/logging.rb
|
15
|
+
module Logger
|
16
|
+
class << self
|
17
|
+
def report(exception, level: :error, description: nil)
|
18
|
+
instance&.report(exception, level: level, description: description)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(description)
|
22
|
+
instance&.error(description)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def instance
|
28
|
+
# Component initialization uses a mutex to avoid having concurrent initialization.
|
29
|
+
# Trying to access the telemetry component during initialization (specifically:
|
30
|
+
# from the thread that's actually doing the initialization) would cause a deadlock,
|
31
|
+
# since accessing the components would try to recursively lock the mutex.
|
32
|
+
#
|
33
|
+
# To work around this, we use allow_initialization: false to avoid triggering this issue.
|
34
|
+
#
|
35
|
+
# The downside is: this leaves us unable to report telemetry during component initialization.
|
36
|
+
components = Datadog.send(:components, allow_initialization: false)
|
37
|
+
|
38
|
+
if components && components.telemetry
|
39
|
+
components.telemetry
|
40
|
+
else
|
41
|
+
Datadog.logger.warn(
|
42
|
+
'Failed to send telemetry before components initialization or within components lifecycle'
|
43
|
+
)
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'event'
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module Datadog
|
8
|
+
module Core
|
9
|
+
module Telemetry
|
10
|
+
# === INTERNAL USAGE ONLY ===
|
11
|
+
#
|
12
|
+
# Logging interface for sending telemetry logs... so we can fix them.
|
13
|
+
#
|
14
|
+
# For developer using this module:
|
15
|
+
# - MUST NOT provide any sensitive information (PII)
|
16
|
+
# - SHOULD reduce the data cardinality for batching/aggregation
|
17
|
+
#
|
18
|
+
# Before using it, ask yourself:
|
19
|
+
# - Do we need to know about this (ie. internal error or client error)?
|
20
|
+
# - How severe/critical is this error? (ie. error, warning, fatal)
|
21
|
+
# - What information needed to make it actionable?
|
22
|
+
#
|
23
|
+
module Logging
|
24
|
+
# Extract datadog stack trace from the exception
|
25
|
+
module DatadogStackTrace
|
26
|
+
GEM_ROOT = Pathname.new("#{__dir__}/../../../..").cleanpath.to_s
|
27
|
+
|
28
|
+
def self.from(exception)
|
29
|
+
backtrace = exception.backtrace
|
30
|
+
|
31
|
+
return unless backtrace
|
32
|
+
return if backtrace.empty?
|
33
|
+
|
34
|
+
stack_trace = +''
|
35
|
+
backtrace.each do |line|
|
36
|
+
stack_trace << if line.start_with?(GEM_ROOT)
|
37
|
+
line[GEM_ROOT.length..-1] || ''
|
38
|
+
else
|
39
|
+
'REDACTED'
|
40
|
+
end
|
41
|
+
stack_trace << ','
|
42
|
+
end
|
43
|
+
|
44
|
+
stack_trace.chomp(',')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def report(exception, level: :error, description: nil)
|
49
|
+
# Annoymous exceptions to be logged as <Class:0x00007f8b1c0b3b40>
|
50
|
+
message = +''
|
51
|
+
message << (exception.class.name || exception.class.inspect)
|
52
|
+
message << ':' << description if description
|
53
|
+
|
54
|
+
event = Event::Log.new(
|
55
|
+
message: message,
|
56
|
+
level: level,
|
57
|
+
stack_trace: DatadogStackTrace.from(exception)
|
58
|
+
)
|
59
|
+
|
60
|
+
log!(event)
|
61
|
+
end
|
62
|
+
|
63
|
+
def error(description)
|
64
|
+
event = Event::Log.new(message: description, level: :error)
|
65
|
+
|
66
|
+
log!(event)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -31,6 +31,18 @@ module Datadog
|
|
31
31
|
|
32
32
|
def application
|
33
33
|
config = Datadog.configuration
|
34
|
+
|
35
|
+
tracer_version = Core::Environment::Identity.gem_datadog_version_semver2
|
36
|
+
|
37
|
+
# We need some to distinguish datadog-ci gem versions
|
38
|
+
# when examining telemetry metrics emitted from the datadog-ci gem.
|
39
|
+
#
|
40
|
+
# This code checks that Datadog::CI is loaded and ci mode is enabled and adds
|
41
|
+
# "-ci-X.Y.Z" suffix to the tracer version.
|
42
|
+
if defined?(::Datadog::CI::VERSION) && config.respond_to?(:ci) && config.ci.enabled
|
43
|
+
tracer_version = "#{tracer_version}-ci-#{::Datadog::CI::VERSION::STRING}"
|
44
|
+
end
|
45
|
+
|
34
46
|
{
|
35
47
|
env: config.env,
|
36
48
|
language_name: Core::Environment::Ext::LANG,
|
@@ -39,7 +51,7 @@ module Datadog
|
|
39
51
|
runtime_version: Core::Environment::Ext::ENGINE_VERSION,
|
40
52
|
service_name: config.service,
|
41
53
|
service_version: config.version,
|
42
|
-
tracer_version:
|
54
|
+
tracer_version: tracer_version
|
43
55
|
}
|
44
56
|
end
|
45
57
|
|
@@ -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
|
@@ -34,6 +34,18 @@ module Datadog
|
|
34
34
|
define_singleton_method(:now, &block)
|
35
35
|
end
|
36
36
|
|
37
|
+
# Overrides the implementation of `#get_time
|
38
|
+
# with the provided callable.
|
39
|
+
#
|
40
|
+
# Overriding the method `#get_time` instead of
|
41
|
+
# indirectly calling `block` removes
|
42
|
+
# one level of method call overhead.
|
43
|
+
#
|
44
|
+
# @param block [Proc] block that accepts unit and returns timestamp in the requested unit
|
45
|
+
def get_time_provider=(block)
|
46
|
+
define_singleton_method(:get_time, &block)
|
47
|
+
end
|
48
|
+
|
37
49
|
def measure(unit = :float_second)
|
38
50
|
before = get_time(unit)
|
39
51
|
yield
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module DI
|
5
|
+
# Tracks loaded Ruby code by source file and maintains a map from
|
6
|
+
# source file to the loaded code (instruction sequences).
|
7
|
+
# Also arranges for code in the loaded files to be instrumented by
|
8
|
+
# line probes that have already been received by the library.
|
9
|
+
#
|
10
|
+
# The loaded code is used to target line trace points when installing
|
11
|
+
# line probes which dramatically improves efficiency of line trace points.
|
12
|
+
#
|
13
|
+
# Note that, since most files will only be loaded one time (via the
|
14
|
+
# "require" mechanism), the code tracker needs to be global and not be
|
15
|
+
# recreated when the DI component is created.
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
class CodeTracker
|
19
|
+
def initialize
|
20
|
+
@registry = {}
|
21
|
+
@trace_point_lock = Mutex.new
|
22
|
+
@registry_lock = Mutex.new
|
23
|
+
@compiled_trace_point = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Starts tracking loaded code.
|
27
|
+
#
|
28
|
+
# This method should generally be called early in application boot
|
29
|
+
# process, because any code loaded before code tracking is enabled
|
30
|
+
# will not be instrumentable via line probes.
|
31
|
+
#
|
32
|
+
# Normally tracking should remain active for the lifetime of the
|
33
|
+
# process and would not be ever stopped.
|
34
|
+
def start
|
35
|
+
trace_point_lock.synchronize do
|
36
|
+
# If this code tracker is already running, we can do nothing or
|
37
|
+
# restart it (by disabling the trace point and recreating it).
|
38
|
+
# It is likely that some applications will attempt to activate
|
39
|
+
# DI more than once where the intention is to just activate DI;
|
40
|
+
# do not break such applications by clearing out the registry.
|
41
|
+
# For now, until there is a use case for recreating the trace point,
|
42
|
+
# do nothing if the code tracker has already started.
|
43
|
+
return if @compiled_trace_point
|
44
|
+
|
45
|
+
# Note: .trace enables the trace point.
|
46
|
+
@compiled_trace_point = TracePoint.trace(:script_compiled) do |tp|
|
47
|
+
# Useful attributes of the trace point object here:
|
48
|
+
# .instruction_sequence
|
49
|
+
# .instruction_sequence.path (either absolute file path for
|
50
|
+
# loaded or required code, or for eval'd code, if filename
|
51
|
+
# is specified as argument to eval, then this is the provided
|
52
|
+
# filename, otherwise this is a synthesized
|
53
|
+
# "(eval at <definition-file>:<line>)" string)
|
54
|
+
# .instruction_sequence.absolute_path (absolute file path when
|
55
|
+
# load or require are used to load code, nil for eval'd code
|
56
|
+
# regardless of whether filename was specified as an argument
|
57
|
+
# to eval on ruby 3.1+, same as path for eval'd code on ruby 3.0
|
58
|
+
# and lower)
|
59
|
+
# .method_id
|
60
|
+
# .path (refers to the code location that called the require/eval/etc.,
|
61
|
+
# not where the loaded code is; use .path on the instruction sequence
|
62
|
+
# to obtain the location of the compiled code)
|
63
|
+
# .eval_script
|
64
|
+
#
|
65
|
+
# For now just map the path to the instruction sequence.
|
66
|
+
path = tp.instruction_sequence.absolute_path
|
67
|
+
# Do not store mapping for eval'd code, since there is no way
|
68
|
+
# to target such code from dynamic instrumentation UI.
|
69
|
+
# eval'd code always sets tp.eval_script.
|
70
|
+
# When tp.eval_script is nil, code is either 'load'ed or 'require'd.
|
71
|
+
# steep, of course, complains about indexing with +path+
|
72
|
+
# without checking that it is not nil, so here, maybe there is
|
73
|
+
# some situation where path would in fact be nil and
|
74
|
+
# steep would end up saving the day.
|
75
|
+
if path && !tp.eval_script
|
76
|
+
registry_lock.synchronize do
|
77
|
+
registry[path] = tp.instruction_sequence
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns whether this code tracker has been activated and is
|
85
|
+
# tracking.
|
86
|
+
def active?
|
87
|
+
trace_point_lock.synchronize do
|
88
|
+
!!@compiled_trace_point
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns an array of RubVM::InstructionSequence (i.e. the compiled code)
|
93
|
+
# for the provided path.
|
94
|
+
#
|
95
|
+
# The argument can be a full path to a Ruby source code file or a
|
96
|
+
# suffix (basename + one or more directories preceding the basename).
|
97
|
+
# The idea with suffix matches is that file paths are likely to
|
98
|
+
# be different between development and production environments and
|
99
|
+
# the source control system uses relative paths and doesn't have
|
100
|
+
# absolute paths at all.
|
101
|
+
#
|
102
|
+
# Suffix matches are not guaranteed to be correct, meaning there may
|
103
|
+
# be multiple files with the same basename and they may all match a
|
104
|
+
# given suffix. In such cases, this method will return all matching
|
105
|
+
# paths (and all of these paths will be attempted to be instrumented
|
106
|
+
# by upstream code).
|
107
|
+
#
|
108
|
+
# If the suffix matches one of the paths completely (which requires it
|
109
|
+
# to be an absolute path), only the exactly matching path is returned.
|
110
|
+
# Otherwise all known paths that end in the suffix are returned.
|
111
|
+
# If no paths match, an empty array is returned.
|
112
|
+
def iseqs_for_path(suffix)
|
113
|
+
registry_lock.synchronize do
|
114
|
+
exact = registry[suffix]
|
115
|
+
return [exact] if exact
|
116
|
+
|
117
|
+
inexact = []
|
118
|
+
registry.each do |path, iseq|
|
119
|
+
# Exact match is not possible here, meaning any matching path
|
120
|
+
# has to be longer than the suffix. Require full component matches,
|
121
|
+
# meaning either the first character of the suffix is a slash
|
122
|
+
# or the previous character in the path is a slash.
|
123
|
+
# For now only check for forward slashes for Unix-like OSes;
|
124
|
+
# backslash is a legitimate character of a file name in Unix
|
125
|
+
# therefore simply permitting forward or back slash is not
|
126
|
+
# sufficient, we need to perform an OS check to know which
|
127
|
+
# path separator to use.
|
128
|
+
if path.length > suffix.length && path.end_with?(suffix)
|
129
|
+
previous_char = path[path.length - suffix.length - 1]
|
130
|
+
inexact << iseq if previous_char == "/" || suffix[0] == "/"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
inexact
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Stops tracking code that is being loaded.
|
138
|
+
#
|
139
|
+
# This method should ordinarily never be called - if a file is loaded
|
140
|
+
# when code tracking is not active, this file will not be instrumentable
|
141
|
+
# by line probes.
|
142
|
+
#
|
143
|
+
# This method is intended for test suite use only, where multiple
|
144
|
+
# code tracker instances are created, to fully clean up the old instances.
|
145
|
+
def stop
|
146
|
+
# Permit multiple stop calls.
|
147
|
+
trace_point_lock.synchronize do
|
148
|
+
@compiled_trace_point&.disable
|
149
|
+
# Clear the instance variable so that the trace point may be
|
150
|
+
# reinstated in the future.
|
151
|
+
@compiled_trace_point = nil
|
152
|
+
end
|
153
|
+
registry_lock.synchronize do
|
154
|
+
registry.clear
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# Mapping from paths of loaded files to RubyVM::InstructionSequence
|
161
|
+
# objects representing compiled code of those files.
|
162
|
+
attr_reader :registry
|
163
|
+
|
164
|
+
attr_reader :trace_point_lock
|
165
|
+
attr_reader :registry_lock
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|