datadog 2.1.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 +101 -1
- 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 +132 -44
- 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 +90 -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 +69 -62
- 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 -126
- 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.c +1 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -0
- data/ext/datadog_profiling_native_extension/stack_recorder.c +27 -8
- 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 +20 -7
- 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/contrib/sinatra/patcher.rb +1 -1
- data/lib/datadog/appsec/extensions.rb +1 -0
- 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 +17 -12
- data/lib/datadog/core/configuration/settings.rb +93 -7
- data/lib/datadog/core/configuration.rb +3 -17
- 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/deprecations.rb +58 -0
- data/lib/datadog/core/diagnostics/environment_logger.rb +8 -11
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/runtime/ext.rb +1 -0
- data/lib/datadog/core/runtime/metrics.rb +6 -0
- data/lib/datadog/core/telemetry/component.rb +154 -0
- data/lib/datadog/core/telemetry/emitter.rb +9 -11
- data/lib/datadog/core/telemetry/event.rb +132 -26
- data/lib/datadog/core/telemetry/ext.rb +3 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +11 -13
- 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/telemetry/metric.rb +167 -0
- data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
- data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
- data/lib/datadog/core/telemetry/request.rb +1 -1
- data/lib/datadog/core/telemetry/worker.rb +173 -0
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +102 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/core.rb +2 -19
- data/lib/datadog/kit/appsec/events.rb +2 -4
- data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +15 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +23 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +24 -11
- 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 +85 -90
- data/lib/datadog/profiling/exporter.rb +3 -3
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
- 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 +5 -5
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +2 -1
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
- data/lib/datadog/tracing/contrib/analytics.rb +5 -0
- data/lib/datadog/tracing/contrib/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +28 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
- 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/racecar/event.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
- 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/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
- data/lib/datadog/tracing/distributed/datadog.rb +2 -2
- data/lib/datadog/tracing/distributed/propagation.rb +9 -2
- data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
- 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 +9 -4
- data/lib/datadog/tracing/trace_operation.rb +7 -3
- data/lib/datadog/tracing/trace_segment.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +9 -2
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -0
- data/lib/datadog/tracing.rb +5 -1
- data/lib/datadog/version.rb +2 -2
- metadata +43 -12
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
- data/lib/datadog/profiling/crashtracker.rb +0 -91
- data/lib/datadog/profiling/ext/forking.rb +0 -98
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'event'
|
4
|
+
require_relative 'metric'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module Core
|
8
|
+
module Telemetry
|
9
|
+
# MetricsCollection is a thread-safe collection of metrics per namespace
|
10
|
+
class MetricsCollection
|
11
|
+
attr_reader :namespace, :interval
|
12
|
+
|
13
|
+
def initialize(namespace, aggregation_interval:)
|
14
|
+
@namespace = namespace
|
15
|
+
@interval = aggregation_interval
|
16
|
+
|
17
|
+
@mutex = Mutex.new
|
18
|
+
|
19
|
+
@metrics = {}
|
20
|
+
@distributions = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def inc(metric_name, value, tags: {}, common: true)
|
24
|
+
metric = Metric::Count.new(metric_name, tags: tags, common: common)
|
25
|
+
fetch_or_add_metric(metric, value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def dec(metric_name, value, tags: {}, common: true)
|
29
|
+
metric = Metric::Count.new(metric_name, tags: tags, common: common)
|
30
|
+
fetch_or_add_metric(metric, -value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def gauge(metric_name, value, tags: {}, common: true)
|
34
|
+
metric = Metric::Gauge.new(metric_name, tags: tags, common: common, interval: @interval)
|
35
|
+
fetch_or_add_metric(metric, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def rate(metric_name, value, tags: {}, common: true)
|
39
|
+
metric = Metric::Rate.new(metric_name, tags: tags, common: common, interval: @interval)
|
40
|
+
fetch_or_add_metric(metric, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def distribution(metric_name, value, tags: {}, common: true)
|
44
|
+
metric = Metric::Distribution.new(metric_name, tags: tags, common: common)
|
45
|
+
fetch_or_add_distribution(metric, value)
|
46
|
+
end
|
47
|
+
|
48
|
+
def flush!
|
49
|
+
@mutex.synchronize do
|
50
|
+
events = []
|
51
|
+
events << Event::GenerateMetrics.new(@namespace, @metrics.values) if @metrics.any?
|
52
|
+
events << Event::Distributions.new(@namespace, @distributions.values) if @distributions.any?
|
53
|
+
|
54
|
+
@metrics = {}
|
55
|
+
@distributions = {}
|
56
|
+
|
57
|
+
events
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def fetch_or_add_metric(metric, value)
|
64
|
+
@mutex.synchronize do
|
65
|
+
m = (@metrics[metric.id] ||= metric)
|
66
|
+
m.track(value)
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch_or_add_distribution(metric, value)
|
72
|
+
@mutex.synchronize do
|
73
|
+
m = (@distributions[metric.id] ||= metric)
|
74
|
+
m.track(value)
|
75
|
+
end
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'metrics_collection'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Core
|
7
|
+
module Telemetry
|
8
|
+
# MetricsManager aggregates and flushes metrics and distributions
|
9
|
+
class MetricsManager
|
10
|
+
attr_reader :enabled
|
11
|
+
|
12
|
+
def initialize(aggregation_interval:, enabled:)
|
13
|
+
@interval = aggregation_interval
|
14
|
+
@enabled = enabled
|
15
|
+
@mutex = Mutex.new
|
16
|
+
|
17
|
+
@collections = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def inc(namespace, metric_name, value, tags: {}, common: true)
|
21
|
+
return unless @enabled
|
22
|
+
|
23
|
+
# collection is thread-safe internally
|
24
|
+
collection = fetch_or_create_collection(namespace)
|
25
|
+
collection.inc(metric_name, value, tags: tags, common: common)
|
26
|
+
end
|
27
|
+
|
28
|
+
def dec(namespace, metric_name, value, tags: {}, common: true)
|
29
|
+
return unless @enabled
|
30
|
+
|
31
|
+
# collection is thread-safe internally
|
32
|
+
collection = fetch_or_create_collection(namespace)
|
33
|
+
collection.dec(metric_name, value, tags: tags, common: common)
|
34
|
+
end
|
35
|
+
|
36
|
+
def gauge(namespace, metric_name, value, tags: {}, common: true)
|
37
|
+
return unless @enabled
|
38
|
+
|
39
|
+
# collection is thread-safe internally
|
40
|
+
collection = fetch_or_create_collection(namespace)
|
41
|
+
collection.gauge(metric_name, value, tags: tags, common: common)
|
42
|
+
end
|
43
|
+
|
44
|
+
def rate(namespace, metric_name, value, tags: {}, common: true)
|
45
|
+
return unless @enabled
|
46
|
+
|
47
|
+
# collection is thread-safe internally
|
48
|
+
collection = fetch_or_create_collection(namespace)
|
49
|
+
collection.rate(metric_name, value, tags: tags, common: common)
|
50
|
+
end
|
51
|
+
|
52
|
+
def distribution(namespace, metric_name, value, tags: {}, common: true)
|
53
|
+
return unless @enabled
|
54
|
+
|
55
|
+
# collection is thread-safe internally
|
56
|
+
collection = fetch_or_create_collection(namespace)
|
57
|
+
collection.distribution(metric_name, value, tags: tags, common: common)
|
58
|
+
end
|
59
|
+
|
60
|
+
def flush!
|
61
|
+
return [] unless @enabled
|
62
|
+
|
63
|
+
collections = @mutex.synchronize { @collections.values }
|
64
|
+
collections.reduce([]) { |events, collection| events + collection.flush! }
|
65
|
+
end
|
66
|
+
|
67
|
+
def disable!
|
68
|
+
@enabled = false
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def fetch_or_create_collection(namespace)
|
74
|
+
@mutex.synchronize do
|
75
|
+
@collections[namespace] ||= MetricsCollection.new(namespace, aggregation_interval: @interval)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'event'
|
4
|
+
|
5
|
+
require_relative '../utils/only_once_successful'
|
6
|
+
require_relative '../workers/polling'
|
7
|
+
require_relative '../workers/queue'
|
8
|
+
|
9
|
+
module Datadog
|
10
|
+
module Core
|
11
|
+
module Telemetry
|
12
|
+
# Accumulates events and sends them to the API at a regular interval, including heartbeat event.
|
13
|
+
class Worker
|
14
|
+
include Core::Workers::Queue
|
15
|
+
include Core::Workers::Polling
|
16
|
+
|
17
|
+
DEFAULT_BUFFER_MAX_SIZE = 1000
|
18
|
+
APP_STARTED_EVENT_RETRIES = 10
|
19
|
+
|
20
|
+
TELEMETRY_STARTED_ONCE = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
|
21
|
+
|
22
|
+
def initialize(
|
23
|
+
heartbeat_interval_seconds:,
|
24
|
+
metrics_aggregation_interval_seconds:,
|
25
|
+
emitter:,
|
26
|
+
metrics_manager:,
|
27
|
+
dependency_collection:,
|
28
|
+
enabled: true,
|
29
|
+
shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT,
|
30
|
+
buffer_size: DEFAULT_BUFFER_MAX_SIZE
|
31
|
+
)
|
32
|
+
@emitter = emitter
|
33
|
+
@metrics_manager = metrics_manager
|
34
|
+
@dependency_collection = dependency_collection
|
35
|
+
|
36
|
+
@ticks_per_heartbeat = (heartbeat_interval_seconds / metrics_aggregation_interval_seconds).to_i
|
37
|
+
@current_ticks = 0
|
38
|
+
|
39
|
+
# Workers::Polling settings
|
40
|
+
self.enabled = enabled
|
41
|
+
# Workers::IntervalLoop settings
|
42
|
+
self.loop_base_interval = metrics_aggregation_interval_seconds
|
43
|
+
self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP
|
44
|
+
|
45
|
+
@shutdown_timeout = shutdown_timeout
|
46
|
+
@buffer_size = buffer_size
|
47
|
+
|
48
|
+
self.buffer = buffer_klass.new(@buffer_size)
|
49
|
+
end
|
50
|
+
|
51
|
+
def start
|
52
|
+
return if !enabled? || forked?
|
53
|
+
|
54
|
+
# starts async worker
|
55
|
+
perform
|
56
|
+
end
|
57
|
+
|
58
|
+
def stop(force_stop = false, timeout = @shutdown_timeout)
|
59
|
+
buffer.close if running?
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def enqueue(event)
|
65
|
+
return if !enabled? || forked?
|
66
|
+
|
67
|
+
buffer.push(event)
|
68
|
+
end
|
69
|
+
|
70
|
+
def sent_started_event?
|
71
|
+
TELEMETRY_STARTED_ONCE.success?
|
72
|
+
end
|
73
|
+
|
74
|
+
def failed_to_start?
|
75
|
+
TELEMETRY_STARTED_ONCE.failed?
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def perform(*events)
|
81
|
+
return if !enabled? || forked?
|
82
|
+
|
83
|
+
started! unless sent_started_event?
|
84
|
+
|
85
|
+
metric_events = @metrics_manager.flush!
|
86
|
+
events = [] if events.nil?
|
87
|
+
flush_events(events + metric_events)
|
88
|
+
|
89
|
+
@current_ticks += 1
|
90
|
+
return if @current_ticks < @ticks_per_heartbeat
|
91
|
+
|
92
|
+
@current_ticks = 0
|
93
|
+
heartbeat!
|
94
|
+
end
|
95
|
+
|
96
|
+
def flush_events(events)
|
97
|
+
return if events.empty?
|
98
|
+
return if !enabled? || !sent_started_event?
|
99
|
+
|
100
|
+
Datadog.logger.debug { "Sending #{events&.count} telemetry events" }
|
101
|
+
send_event(Event::MessageBatch.new(events))
|
102
|
+
end
|
103
|
+
|
104
|
+
def heartbeat!
|
105
|
+
return if !enabled? || !sent_started_event?
|
106
|
+
|
107
|
+
send_event(Event::AppHeartbeat.new)
|
108
|
+
end
|
109
|
+
|
110
|
+
def started!
|
111
|
+
return unless enabled?
|
112
|
+
|
113
|
+
if failed_to_start?
|
114
|
+
Datadog.logger.debug('Telemetry app-started event exhausted retries, disabling telemetry worker')
|
115
|
+
disable!
|
116
|
+
return
|
117
|
+
end
|
118
|
+
|
119
|
+
TELEMETRY_STARTED_ONCE.run do
|
120
|
+
res = send_event(Event::AppStarted.new)
|
121
|
+
|
122
|
+
if res.ok?
|
123
|
+
Datadog.logger.debug('Telemetry app-started event is successfully sent')
|
124
|
+
|
125
|
+
send_event(Event::AppDependenciesLoaded.new) if @dependency_collection
|
126
|
+
|
127
|
+
true
|
128
|
+
else
|
129
|
+
Datadog.logger.debug('Error sending telemetry app-started event, retry after heartbeat interval...')
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def send_event(event)
|
136
|
+
res = @emitter.request(event)
|
137
|
+
|
138
|
+
disable_on_not_found!(res)
|
139
|
+
|
140
|
+
res
|
141
|
+
end
|
142
|
+
|
143
|
+
def dequeue
|
144
|
+
buffer.pop
|
145
|
+
end
|
146
|
+
|
147
|
+
def work_pending?
|
148
|
+
run_loop? || !buffer.empty?
|
149
|
+
end
|
150
|
+
|
151
|
+
def buffer_klass
|
152
|
+
if Core::Environment::Ext::RUBY_ENGINE == 'ruby'
|
153
|
+
Core::Buffer::CRuby
|
154
|
+
else
|
155
|
+
Core::Buffer::ThreadSafe
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def disable!
|
160
|
+
self.enabled = false
|
161
|
+
@metrics_manager.disable!
|
162
|
+
end
|
163
|
+
|
164
|
+
def disable_on_not_found!(response)
|
165
|
+
return unless response.not_found?
|
166
|
+
|
167
|
+
Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
|
168
|
+
disable!
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
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
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'only_once'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Core
|
7
|
+
module Utils
|
8
|
+
# Helper class to execute something with only one success.
|
9
|
+
#
|
10
|
+
# This is useful for cases where we want to ensure that a block of code is only executed once, and only if it
|
11
|
+
# succeeds. One such example is sending app-started telemetry event.
|
12
|
+
#
|
13
|
+
# Successful execution is determined by the return value of the block: any truthy value is considered success.
|
14
|
+
#
|
15
|
+
# Thread-safe when used correctly (e.g. be careful of races when lazily initializing instances of this class).
|
16
|
+
#
|
17
|
+
# Note: In its current state, this class is not Ractor-safe.
|
18
|
+
# In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
|
19
|
+
# including an alternative implementation that is Ractor-safe once spent.
|
20
|
+
class OnlyOnceSuccessful < OnlyOnce
|
21
|
+
def initialize(limit = 0)
|
22
|
+
super()
|
23
|
+
|
24
|
+
@limit = limit
|
25
|
+
@failed = false
|
26
|
+
@retries = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
@mutex.synchronize do
|
31
|
+
return if @ran_once
|
32
|
+
|
33
|
+
result = yield
|
34
|
+
@ran_once = !!result
|
35
|
+
|
36
|
+
if !@ran_once && limited?
|
37
|
+
@retries += 1
|
38
|
+
check_limit!
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def success?
|
46
|
+
@mutex.synchronize { @ran_once && !@failed }
|
47
|
+
end
|
48
|
+
|
49
|
+
def failed?
|
50
|
+
@mutex.synchronize { @ran_once && @failed }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def check_limit!
|
56
|
+
if @retries >= @limit
|
57
|
+
@failed = true
|
58
|
+
@ran_once = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def limited?
|
63
|
+
!@limit.nil? && @limit.positive?
|
64
|
+
end
|
65
|
+
|
66
|
+
def reset_ran_once_state_for_tests
|
67
|
+
@mutex.synchronize do
|
68
|
+
@ran_once = false
|
69
|
+
@failed = false
|
70
|
+
@retries = 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/datadog/core.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'core/deprecations'
|
3
4
|
require_relative 'core/extensions'
|
4
5
|
|
5
6
|
# We must load core extensions to make certain global APIs
|
@@ -9,25 +10,7 @@ module Datadog
|
|
9
10
|
# products. It is a dependency of each product. Contrast with Datadog::Kit
|
10
11
|
# for higher-level features.
|
11
12
|
module Core
|
12
|
-
|
13
|
-
# Records the occurrence of a deprecated operation in this library.
|
14
|
-
#
|
15
|
-
# Currently, these operations are logged to `Datadog.logger` at `warn` level.
|
16
|
-
#
|
17
|
-
# `disallowed_next_major` adds a message informing that the deprecated operation
|
18
|
-
# won't be allowed in the next major release.
|
19
|
-
#
|
20
|
-
# @yieldreturn [String] a String with the lazily evaluated deprecation message.
|
21
|
-
# @param [Boolean] disallowed_next_major whether this deprecation will be enforced in the next major release.
|
22
|
-
def log_deprecation(disallowed_next_major: true)
|
23
|
-
Datadog.logger.warn do
|
24
|
-
message = yield
|
25
|
-
message += ' This will be enforced in the next major release.' if disallowed_next_major
|
26
|
-
message
|
27
|
-
end
|
28
|
-
nil
|
29
|
-
end
|
30
|
-
end
|
13
|
+
extend Core::Deprecations
|
31
14
|
end
|
32
15
|
|
33
16
|
extend Core::Extensions
|
@@ -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
|
@@ -41,8 +41,11 @@ module Datadog
|
|
41
41
|
digest = @datadog_propagator.extract(carrier)
|
42
42
|
return context unless digest
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
# Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
|
45
|
+
# 128-bit unsigned, big-endian integer
|
46
|
+
trace_id = [digest.trace_id >> 64, digest.trace_id & 0xFFFFFFFFFFFFFFFF].pack('Q>Q>')
|
47
|
+
# 64-bit unsigned, big-endian integer
|
48
|
+
span_id = [digest.span_id].pack('Q>')
|
46
49
|
|
47
50
|
if digest.trace_state || digest.trace_flags
|
48
51
|
trace_flags = ::OpenTelemetry::Trace::TraceFlags.from_byte(digest.trace_flags)
|
@@ -78,14 +81,6 @@ module Datadog
|
|
78
81
|
def fields
|
79
82
|
[]
|
80
83
|
end
|
81
|
-
|
82
|
-
private
|
83
|
-
|
84
|
-
# Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
|
85
|
-
# This method currently converts an unsigned 64-bit Integer to a binary String.
|
86
|
-
def to_otel_id(dd_id)
|
87
|
-
Array(dd_id).pack('Q')
|
88
|
-
end
|
89
84
|
end
|
90
85
|
end
|
91
86
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'trace/span'
|
4
4
|
require_relative '../../tracing/span_link'
|
5
|
+
require_relative '../../tracing/span_event'
|
5
6
|
require_relative '../../tracing/trace_digest'
|
6
7
|
|
7
8
|
module Datadog
|
@@ -31,6 +32,15 @@ module Datadog
|
|
31
32
|
#
|
32
33
|
# @param [Span] span the {Span} that just ended.
|
33
34
|
def on_finish(span)
|
35
|
+
unless span.events.nil?
|
36
|
+
span.datadog_span.span_events = span.events.map do |event|
|
37
|
+
Datadog::Tracing::SpanEvent.new(
|
38
|
+
event.name,
|
39
|
+
attributes: event.attributes,
|
40
|
+
time_unix_nano: event.timestamp
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
34
44
|
span.datadog_span.finish(ns_to_time(span.end_timestamp))
|
35
45
|
end
|
36
46
|
|
@@ -70,7 +80,8 @@ module Datadog
|
|
70
80
|
if parent_context.trace
|
71
81
|
Tracing.send(:tracer).send(:call_context).activate!(parent_context.ensure_trace)
|
72
82
|
else
|
73
|
-
|
83
|
+
otel_trace_id = span.context.hex_trace_id.to_i(16)
|
84
|
+
Tracing.continue_trace!(Datadog::Tracing::TraceDigest.new(trace_id: otel_trace_id, span_remote: false))
|
74
85
|
end
|
75
86
|
|
76
87
|
datadog_span = start_datadog_span(span)
|
@@ -85,7 +96,6 @@ module Datadog
|
|
85
96
|
name, kwargs = span_arguments(span, attributes)
|
86
97
|
|
87
98
|
datadog_span = Tracing.trace(name, **kwargs)
|
88
|
-
|
89
99
|
datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
|
90
100
|
datadog_span.set_tags(span.attributes)
|
91
101
|
|
@@ -143,6 +153,9 @@ module Datadog
|
|
143
153
|
|
144
154
|
kwargs[:tags] = attributes.to_h
|
145
155
|
|
156
|
+
# DEV: The datadog span must have the same ID as the OpenTelemetry span
|
157
|
+
kwargs[:id] = span.context.hex_span_id.to_i(16)
|
158
|
+
|
146
159
|
[name, kwargs]
|
147
160
|
end
|
148
161
|
|
@@ -15,6 +15,29 @@ module Datadog
|
|
15
15
|
res
|
16
16
|
end
|
17
17
|
|
18
|
+
# Record an exception during the execution of this span. Multiple exceptions
|
19
|
+
# can be recorded on a span.
|
20
|
+
#
|
21
|
+
# @param [Exception] exception The exception to recorded
|
22
|
+
# @param [optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}]
|
23
|
+
# attributes One or more key:value pairs, where the keys must be
|
24
|
+
# strings and the values may be (array of) string, boolean or numeric
|
25
|
+
# type.
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
def record_exception(exception, attributes: nil)
|
29
|
+
res = super
|
30
|
+
if (span = datadog_span)
|
31
|
+
# Sets the exception attributes as span error tags. The values in the attribute hash MUST
|
32
|
+
# take precedence over the type, message and stacktrace inferred from the exception object
|
33
|
+
type = attributes&.[]('exception.type') || exception.class.to_s
|
34
|
+
message = attributes&.[]('exception.message') || exception.message
|
35
|
+
stacktrace = attributes&.[]('exception.stacktrace') || exception.full_message(highlight: false, order: :top)
|
36
|
+
span.set_error_tags([type, message, stacktrace])
|
37
|
+
end
|
38
|
+
res
|
39
|
+
end
|
40
|
+
|
18
41
|
# `alias` performed to match {OpenTelemetry::SDK::Trace::Span} aliasing upstream
|
19
42
|
alias []= set_attribute
|
20
43
|
|