datadog 2.16.0 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +12 -46
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +227 -49
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +19 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +63 -12
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  8. data/ext/datadog_profiling_native_extension/encoded_profile.c +22 -12
  9. data/ext/datadog_profiling_native_extension/encoded_profile.h +1 -0
  10. data/ext/datadog_profiling_native_extension/extconf.rb +7 -0
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +239 -363
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +4 -6
  13. data/ext/datadog_profiling_native_extension/http_transport.c +45 -72
  14. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +22 -0
  15. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +8 -5
  16. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +1 -0
  17. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -3
  18. data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -13
  19. data/ext/datadog_profiling_native_extension/ruby_helpers.h +2 -10
  20. data/ext/datadog_profiling_native_extension/stack_recorder.c +156 -60
  21. data/ext/libdatadog_api/crashtracker.c +10 -3
  22. data/ext/libdatadog_api/extconf.rb +2 -2
  23. data/ext/libdatadog_api/library_config.c +54 -12
  24. data/ext/libdatadog_api/library_config.h +6 -0
  25. data/ext/libdatadog_api/macos_development.md +3 -3
  26. data/ext/libdatadog_api/process_discovery.c +2 -7
  27. data/ext/libdatadog_extconf_helpers.rb +2 -2
  28. data/lib/datadog/appsec/api_security/lru_cache.rb +56 -0
  29. data/lib/datadog/appsec/api_security/route_extractor.rb +65 -0
  30. data/lib/datadog/appsec/api_security/sampler.rb +59 -0
  31. data/lib/datadog/appsec/api_security.rb +23 -0
  32. data/lib/datadog/appsec/assets/waf_rules/recommended.json +257 -85
  33. data/lib/datadog/appsec/assets/waf_rules/strict.json +10 -78
  34. data/lib/datadog/appsec/component.rb +30 -54
  35. data/lib/datadog/appsec/configuration/settings.rb +60 -2
  36. data/lib/datadog/appsec/context.rb +6 -6
  37. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
  38. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +27 -16
  39. data/lib/datadog/appsec/processor/rule_loader.rb +5 -6
  40. data/lib/datadog/appsec/remote.rb +15 -55
  41. data/lib/datadog/appsec/security_engine/engine.rb +194 -0
  42. data/lib/datadog/appsec/security_engine/runner.rb +10 -11
  43. data/lib/datadog/appsec.rb +4 -7
  44. data/lib/datadog/core/buffer/random.rb +18 -2
  45. data/lib/datadog/core/configuration/agent_settings.rb +52 -0
  46. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -46
  47. data/lib/datadog/core/configuration/components.rb +31 -24
  48. data/lib/datadog/core/configuration/components_state.rb +23 -0
  49. data/lib/datadog/core/configuration/option.rb +27 -27
  50. data/lib/datadog/core/configuration/option_definition.rb +4 -4
  51. data/lib/datadog/core/configuration/options.rb +1 -1
  52. data/lib/datadog/core/configuration/settings.rb +32 -20
  53. data/lib/datadog/core/configuration/stable_config.rb +1 -2
  54. data/lib/datadog/core/configuration.rb +16 -16
  55. data/lib/datadog/core/crashtracking/component.rb +2 -1
  56. data/lib/datadog/core/crashtracking/tag_builder.rb +4 -22
  57. data/lib/datadog/core/encoding.rb +1 -1
  58. data/lib/datadog/core/environment/cgroup.rb +10 -12
  59. data/lib/datadog/core/environment/container.rb +38 -40
  60. data/lib/datadog/core/environment/ext.rb +6 -6
  61. data/lib/datadog/core/environment/identity.rb +3 -3
  62. data/lib/datadog/core/environment/platform.rb +3 -3
  63. data/lib/datadog/core/error.rb +11 -9
  64. data/lib/datadog/core/logger.rb +2 -2
  65. data/lib/datadog/core/metrics/client.rb +12 -14
  66. data/lib/datadog/core/metrics/logging.rb +5 -5
  67. data/lib/datadog/core/process_discovery/tracer_memfd.rb +15 -0
  68. data/lib/datadog/core/process_discovery.rb +5 -1
  69. data/lib/datadog/core/rate_limiter.rb +4 -2
  70. data/lib/datadog/core/remote/client.rb +32 -31
  71. data/lib/datadog/core/remote/component.rb +3 -3
  72. data/lib/datadog/core/remote/configuration/digest.rb +7 -7
  73. data/lib/datadog/core/remote/configuration/path.rb +1 -1
  74. data/lib/datadog/core/remote/configuration/repository.rb +12 -0
  75. data/lib/datadog/core/remote/transport/http/client.rb +1 -1
  76. data/lib/datadog/core/remote/transport/http/config.rb +21 -5
  77. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -1
  78. data/lib/datadog/core/runtime/metrics.rb +3 -3
  79. data/lib/datadog/core/tag_builder.rb +56 -0
  80. data/lib/datadog/core/telemetry/component.rb +39 -24
  81. data/lib/datadog/core/telemetry/emitter.rb +7 -1
  82. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +66 -0
  83. data/lib/datadog/core/telemetry/event/app_closing.rb +18 -0
  84. data/lib/datadog/core/telemetry/event/app_dependencies_loaded.rb +33 -0
  85. data/lib/datadog/core/telemetry/event/app_heartbeat.rb +18 -0
  86. data/lib/datadog/core/telemetry/event/app_integrations_change.rb +58 -0
  87. data/lib/datadog/core/telemetry/event/app_started.rb +269 -0
  88. data/lib/datadog/core/telemetry/event/base.rb +40 -0
  89. data/lib/datadog/core/telemetry/event/distributions.rb +18 -0
  90. data/lib/datadog/core/telemetry/event/generate_metrics.rb +43 -0
  91. data/lib/datadog/core/telemetry/event/log.rb +76 -0
  92. data/lib/datadog/core/telemetry/event/message_batch.rb +42 -0
  93. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +43 -0
  94. data/lib/datadog/core/telemetry/event.rb +17 -475
  95. data/lib/datadog/core/telemetry/logger.rb +5 -4
  96. data/lib/datadog/core/telemetry/logging.rb +11 -5
  97. data/lib/datadog/core/telemetry/metric.rb +3 -3
  98. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -2
  99. data/lib/datadog/core/telemetry/transport/telemetry.rb +0 -1
  100. data/lib/datadog/core/telemetry/worker.rb +48 -27
  101. data/lib/datadog/core/transport/http/adapters/net.rb +17 -2
  102. data/lib/datadog/core/transport/http/adapters/test.rb +2 -1
  103. data/lib/datadog/core/transport/http/builder.rb +14 -14
  104. data/lib/datadog/core/transport/http/env.rb +8 -0
  105. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +6 -6
  106. data/lib/datadog/core/utils/duration.rb +32 -32
  107. data/lib/datadog/core/utils/forking.rb +2 -2
  108. data/lib/datadog/core/utils/network.rb +6 -6
  109. data/lib/datadog/core/utils/only_once_successful.rb +16 -5
  110. data/lib/datadog/core/utils/time.rb +10 -2
  111. data/lib/datadog/core/utils/truncation.rb +21 -0
  112. data/lib/datadog/core/utils.rb +7 -0
  113. data/lib/datadog/core/vendor/multipart-post/multipart/post/composite_read_io.rb +1 -1
  114. data/lib/datadog/core/vendor/multipart-post/multipart/post/multipartable.rb +8 -8
  115. data/lib/datadog/core/vendor/multipart-post/multipart/post/parts.rb +7 -7
  116. data/lib/datadog/core/worker.rb +1 -1
  117. data/lib/datadog/core/workers/async.rb +9 -10
  118. data/lib/datadog/di/instrumenter.rb +52 -2
  119. data/lib/datadog/di/probe_notification_builder.rb +31 -41
  120. data/lib/datadog/di/probe_notifier_worker.rb +9 -1
  121. data/lib/datadog/di/serializer.rb +6 -2
  122. data/lib/datadog/di/transport/http/input.rb +10 -0
  123. data/lib/datadog/di/transport/input.rb +10 -2
  124. data/lib/datadog/error_tracking/component.rb +2 -2
  125. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -9
  126. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -0
  127. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  128. data/lib/datadog/profiling/collectors/thread_context.rb +16 -1
  129. data/lib/datadog/profiling/component.rb +7 -9
  130. data/lib/datadog/profiling/ext.rb +0 -13
  131. data/lib/datadog/profiling/flush.rb +1 -1
  132. data/lib/datadog/profiling/http_transport.rb +3 -8
  133. data/lib/datadog/profiling/profiler.rb +2 -0
  134. data/lib/datadog/profiling/scheduler.rb +10 -2
  135. data/lib/datadog/profiling/stack_recorder.rb +5 -5
  136. data/lib/datadog/profiling/tag_builder.rb +5 -41
  137. data/lib/datadog/profiling/tasks/setup.rb +2 -0
  138. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +15 -0
  139. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +19 -12
  140. data/lib/datadog/tracing/contrib/action_pack/ext.rb +2 -0
  141. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +4 -1
  142. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +33 -0
  143. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +4 -0
  144. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +2 -4
  145. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +10 -0
  146. data/lib/datadog/tracing/contrib/aws/parsed_context.rb +5 -1
  147. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -5
  148. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +1 -5
  149. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -5
  150. data/lib/datadog/tracing/contrib/lograge/patcher.rb +4 -2
  151. data/lib/datadog/tracing/contrib/patcher.rb +5 -2
  152. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  153. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -2
  154. data/lib/datadog/tracing/contrib/support.rb +28 -0
  155. data/lib/datadog/tracing/metadata/errors.rb +4 -4
  156. data/lib/datadog/tracing/sync_writer.rb +1 -1
  157. data/lib/datadog/tracing/trace_operation.rb +12 -4
  158. data/lib/datadog/tracing/tracer.rb +6 -2
  159. data/lib/datadog/version.rb +1 -1
  160. metadata +31 -12
  161. data/lib/datadog/appsec/assets/waf_rules/processors.json +0 -321
  162. data/lib/datadog/appsec/assets/waf_rules/scanners.json +0 -1023
  163. data/lib/datadog/appsec/processor/rule_merger.rb +0 -171
  164. data/lib/datadog/appsec/processor.rb +0 -107
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-client-configuration-change' event
10
+ class AppClientConfigurationChange < Base
11
+ attr_reader :changes, :origin
12
+
13
+ def type
14
+ 'app-client-configuration-change'
15
+ end
16
+
17
+ def initialize(changes, origin)
18
+ super()
19
+ @changes = changes
20
+ @origin = origin
21
+ end
22
+
23
+ def payload
24
+ {configuration: configuration}
25
+ end
26
+
27
+ def configuration
28
+ config = Datadog.configuration
29
+ seq_id = Event.configuration_sequence.next
30
+
31
+ res = @changes.map do |name, value|
32
+ {
33
+ name: name,
34
+ value: value,
35
+ origin: @origin,
36
+ seq_id: seq_id,
37
+ }
38
+ end
39
+
40
+ # DEV: This seems unnecessary (we send the state of sca_enabled for each remote config change)
41
+ unless config.dig('appsec', 'sca_enabled').nil?
42
+ res << {
43
+ name: 'appsec.sca_enabled',
44
+ value: config.appsec.sca_enabled,
45
+ origin: 'code',
46
+ seq_id: seq_id,
47
+ }
48
+ end
49
+
50
+ res
51
+ end
52
+
53
+ def ==(other)
54
+ other.is_a?(AppClientConfigurationChange) && other.changes == @changes && other.origin == @origin
55
+ end
56
+
57
+ alias_method :eql?, :==
58
+
59
+ def hash
60
+ [self.class, @changes, @origin].hash
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-closing' event
10
+ class AppClosing < Base
11
+ def type
12
+ 'app-closing'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-dependencies-loaded' event
10
+ class AppDependenciesLoaded < Base
11
+ def type
12
+ 'app-dependencies-loaded'
13
+ end
14
+
15
+ def payload
16
+ {dependencies: dependencies}
17
+ end
18
+
19
+ private
20
+
21
+ def dependencies
22
+ Gem.loaded_specs.collect do |name, gem|
23
+ {
24
+ name: name,
25
+ version: gem.version.to_s,
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-heartbeat' event
10
+ class AppHeartbeat < Base
11
+ def type
12
+ 'app-heartbeat'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-integrations-change' event
10
+ class AppIntegrationsChange < Base
11
+ def type
12
+ 'app-integrations-change'
13
+ end
14
+
15
+ def payload
16
+ {integrations: integrations}
17
+ end
18
+
19
+ private
20
+
21
+ def integrations
22
+ instrumented_integrations = Datadog.configuration.tracing.instrumented_integrations
23
+ Datadog.registry.map do |integration|
24
+ is_instrumented = instrumented_integrations.include?(integration.name)
25
+
26
+ is_enabled = is_instrumented && integration.klass.patcher.patch_successful
27
+
28
+ version = integration.klass.class.version&.to_s
29
+
30
+ res = {
31
+ name: integration.name.to_s,
32
+ enabled: is_enabled,
33
+ version: version,
34
+ compatible: integration.klass.class.compatible?,
35
+ error: (patch_error(integration) if is_instrumented && !is_enabled),
36
+ # TODO: Track if integration is instrumented by manual configuration or by auto instrumentation
37
+ # auto_enabled: is_enabled && ???,
38
+ }
39
+ res.reject! { |_, v| v.nil? }
40
+ res
41
+ end
42
+ end
43
+
44
+ def patch_error(integration)
45
+ patch_error_result = integration.klass.patcher.patch_error_result
46
+ return patch_error_result.compact.to_s if patch_error_result
47
+
48
+ # If no error occurred during patching, but integration is still not instrumented
49
+ "Available?: #{integration.klass.class.available?}" \
50
+ ", Loaded? #{integration.klass.class.loaded?}" \
51
+ ", Compatible? #{integration.klass.class.compatible?}" \
52
+ ", Patchable? #{integration.klass.class.patchable?}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,269 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-started' event
10
+ class AppStarted < Base
11
+ def type
12
+ 'app-started'
13
+ end
14
+
15
+ def payload
16
+ {
17
+ products: products,
18
+ configuration: configuration,
19
+ install_signature: install_signature,
20
+ # DEV: Not implemented yet
21
+ # error: error, # Start-up errors
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def products
28
+ # @type var products: Hash[Symbol, Hash[Symbol, Hash[Symbol, String | Integer] | bool | nil]]
29
+ products = {
30
+ appsec: {
31
+ enabled: Datadog::AppSec.enabled?,
32
+ },
33
+ profiler: {
34
+ enabled: Datadog::Profiling.enabled?,
35
+ },
36
+ dynamic_instrumentation: {
37
+ enabled: defined?(Datadog::DI) && Datadog::DI.respond_to?(:enabled?) && Datadog::DI.enabled?,
38
+ }
39
+ }
40
+
41
+ if (unsupported_reason = Datadog::Profiling.unsupported_reason)
42
+ products[:profiler][:error] = {
43
+ code: 1, # Error code. 0 if no error.
44
+ message: unsupported_reason,
45
+ }
46
+ end
47
+
48
+ products
49
+ end
50
+
51
+ TARGET_OPTIONS = %w[
52
+ dynamic_instrumentation.enabled
53
+ logger.level
54
+ profiling.advanced.code_provenance_enabled
55
+ profiling.advanced.endpoint.collection.enabled
56
+ profiling.enabled
57
+ runtime_metrics.enabled
58
+ tracing.analytics.enabled
59
+ tracing.propagation_style_extract
60
+ tracing.propagation_style_inject
61
+ tracing.enabled
62
+ tracing.log_injection
63
+ tracing.partial_flush.enabled
64
+ tracing.partial_flush.min_spans_threshold
65
+ tracing.report_hostname
66
+ tracing.sampling.rate_limit
67
+ apm.tracing.enabled
68
+ ].freeze
69
+
70
+ # standard:disable Metrics/AbcSize
71
+ # standard:disable Metrics/MethodLength
72
+ def configuration
73
+ config = Datadog.configuration
74
+ seq_id = Event.configuration_sequence.next
75
+
76
+ # tracing.writer_options.buffer_size and tracing.writer_options.flush_interval have the same origin.
77
+ writer_option_origin = get_telemetry_origin(config, 'tracing.writer_options')
78
+
79
+ list = [
80
+ # Only set using env var as of June 2025
81
+ conf_value('DD_GIT_REPOSITORY_URL', Core::Environment::Git.git_repository_url, seq_id, 'env_var'),
82
+ conf_value('DD_GIT_COMMIT_SHA', Core::Environment::Git.git_commit_sha, seq_id, 'env_var'),
83
+
84
+ # Set by the customer application (eg. `require 'datadog/auto_instrument'`)
85
+ conf_value(
86
+ 'tracing.auto_instrument.enabled',
87
+ !defined?(Datadog::AutoInstrument::LOADED).nil?,
88
+ seq_id,
89
+ 'code'
90
+ ),
91
+ conf_value(
92
+ 'tracing.opentelemetry.enabled',
93
+ !defined?(Datadog::OpenTelemetry::LOADED).nil?,
94
+ seq_id,
95
+ 'code'
96
+ ),
97
+
98
+ # Mix of env var, programmatic and default config, so we use unknown
99
+ conf_value('DD_AGENT_TRANSPORT', agent_transport(config), seq_id, 'unknown'),
100
+
101
+ # writer_options is defined as an option that has a Hash value.
102
+ conf_value(
103
+ 'tracing.writer_options.buffer_size',
104
+ to_value(config.tracing.writer_options[:buffer_size]),
105
+ seq_id,
106
+ writer_option_origin
107
+ ),
108
+ conf_value(
109
+ 'tracing.writer_options.flush_interval',
110
+ to_value(config.tracing.writer_options[:flush_interval]),
111
+ seq_id,
112
+ writer_option_origin
113
+ ),
114
+
115
+ conf_value('DD_AGENT_HOST', config.agent.host, seq_id, get_telemetry_origin(config, 'agent.host')),
116
+ conf_value(
117
+ 'DD_TRACE_SAMPLE_RATE',
118
+ to_value(config.tracing.sampling.default_rate),
119
+ seq_id,
120
+ get_telemetry_origin(config, 'tracing.sampling.default_rate')
121
+ ),
122
+ conf_value(
123
+ 'DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED',
124
+ config.tracing.contrib.global_default_service_name.enabled,
125
+ seq_id,
126
+ get_telemetry_origin(config, 'tracing.contrib.global_default_service_name.enabled')
127
+ ),
128
+ conf_value(
129
+ 'DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED',
130
+ config.tracing.contrib.peer_service_defaults,
131
+ seq_id,
132
+ get_telemetry_origin(config, 'tracing.contrib.peer_service_defaults')
133
+ ),
134
+ conf_value(
135
+ 'DD_TRACE_DEBUG',
136
+ config.diagnostics.debug,
137
+ seq_id,
138
+ get_telemetry_origin(config, 'diagnostics.debug')
139
+ )
140
+ ]
141
+
142
+ peer_service_mapping_str = ''
143
+ unless config.tracing.contrib.peer_service_mapping.empty?
144
+ peer_service_mapping = config.tracing.contrib.peer_service_mapping
145
+ peer_service_mapping_str = peer_service_mapping.map { |key, value| "#{key}:#{value}" }.join(',')
146
+ end
147
+ list << conf_value(
148
+ 'DD_TRACE_PEER_SERVICE_MAPPING',
149
+ peer_service_mapping_str,
150
+ seq_id,
151
+ get_telemetry_origin(config, 'tracing.contrib.peer_service_mapping')
152
+ )
153
+
154
+ # Whitelist of configuration options to send in additional payload object
155
+ TARGET_OPTIONS.each do |option_path|
156
+ split_option = option_path.split('.')
157
+ list << conf_value(
158
+ option_path,
159
+ to_value(config.dig(*split_option)),
160
+ seq_id,
161
+ get_telemetry_origin(config, option_path)
162
+ )
163
+ end
164
+
165
+ # Add some more custom additional payload values here
166
+ if config.logger.instance
167
+ list << conf_value(
168
+ 'logger.instance',
169
+ config.logger.instance.class.to_s,
170
+ seq_id,
171
+ get_telemetry_origin(config, 'logger.instance')
172
+ )
173
+ end
174
+ if config.respond_to?('appsec')
175
+ list << conf_value(
176
+ 'appsec.enabled',
177
+ config.dig('appsec', 'enabled'),
178
+ seq_id,
179
+ get_telemetry_origin(config, 'appsec.enabled')
180
+ )
181
+ list << conf_value(
182
+ 'appsec.sca_enabled',
183
+ config.dig('appsec', 'sca_enabled'),
184
+ seq_id,
185
+ get_telemetry_origin(config, 'appsec.sca_enabled')
186
+ )
187
+ end
188
+ if config.respond_to?('ci')
189
+ list << conf_value(
190
+ 'ci.enabled',
191
+ config.dig('ci', 'enabled'),
192
+ seq_id,
193
+ get_telemetry_origin(config, 'ci.enabled')
194
+ )
195
+ end
196
+
197
+ list.reject! { |entry| entry[:value].nil? }
198
+ list
199
+ end
200
+ # standard:enable Metrics/AbcSize
201
+ # standard:enable Metrics/MethodLength
202
+
203
+ def agent_transport(config)
204
+ adapter = Core::Configuration::AgentSettingsResolver.call(config).adapter
205
+ if adapter == Datadog::Core::Transport::Ext::UnixSocket::ADAPTER
206
+ 'UDS'
207
+ else
208
+ 'TCP'
209
+ end
210
+ end
211
+
212
+ # `origin`: Source of the configuration. One of :
213
+ # - `fleet_stable_config`: configuration is set via the fleet automation Datadog UI
214
+ # - `local_stable_config`: configuration set via a user-managed file
215
+ # - `env_var`: configurations that are set through environment variables
216
+ # - `jvm_prop`: JVM system properties passed on the command line
217
+ # - `code`: configurations that are set through the customer application
218
+ # - `dd_config`: set by the dd.yaml file or json
219
+ # - `remote_config`: values that are set using remote config
220
+ # - `app.config`: only applies to .NET
221
+ # - `default`: set when the user has not set any configuration for the key (defaults to a value)
222
+ # - `unknown`: set for cases where it is difficult/not possible to determine the source of a config.
223
+ def conf_value(name, value, seq_id, origin)
224
+ result = {name: name, value: value, origin: origin, seq_id: seq_id}
225
+ if origin == 'fleet_stable_config'
226
+ fleet_id = Core::Configuration::StableConfig.configuration.dig(:fleet, :id)
227
+ result[:config_id] = fleet_id if fleet_id
228
+ elsif origin == 'local_stable_config'
229
+ local_id = Core::Configuration::StableConfig.configuration.dig(:local, :id)
230
+ result[:config_id] = local_id if local_id
231
+ end
232
+ result
233
+ end
234
+
235
+ def to_value(value)
236
+ # TODO: Add float if telemetry starts accepting it
237
+ case value
238
+ when Integer, String, true, false, nil
239
+ value
240
+ else
241
+ value.to_s
242
+ end
243
+ end
244
+
245
+ def install_signature
246
+ config = Datadog.configuration
247
+ {
248
+ install_id: config.dig('telemetry', 'install_id'),
249
+ install_type: config.dig('telemetry', 'install_type'),
250
+ install_time: config.dig('telemetry', 'install_time'),
251
+ }
252
+ end
253
+
254
+ def get_telemetry_origin(config, config_path)
255
+ split_option = config_path.split('.')
256
+ option_name = split_option.pop
257
+ return 'unknown' if option_name.nil?
258
+
259
+ # @type var parent_setting: Core::Configuration::Options
260
+ # @type var option: Core::Configuration::Option
261
+ parent_setting = config.dig(*split_option)
262
+ option = parent_setting.send(:resolve_option, option_name.to_sym)
263
+ option.precedence_set&.origin || 'unknown'
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Telemetry
6
+ module Event
7
+ # Base class for all Telemetry V2 events.
8
+ class Base
9
+ # The type of the event.
10
+ # It must be one of the stings defined in the Telemetry V2
11
+ # specification for event names.
12
+ def type
13
+ raise NotImplementedError, 'Must be implemented by subclass'
14
+ end
15
+
16
+ # The JSON payload for the event.
17
+ def payload
18
+ {}
19
+ end
20
+
21
+ # Override equality to allow for deduplication
22
+ # The basic implementation is to check if the other object is an instance of the same class.
23
+ # This works for events that have no attributes.
24
+ # For events with attributes, you should override this method to compare the attributes.
25
+ def ==(other)
26
+ other.is_a?(self.class)
27
+ end
28
+
29
+ # @see #==
30
+ alias_method :eql?, :==
31
+
32
+ # @see #==
33
+ def hash
34
+ self.class.hash
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generate_metrics'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'distributions' event
10
+ class Distributions < GenerateMetrics
11
+ def type
12
+ 'distributions'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'generate-metrics' event
10
+ class GenerateMetrics < Base
11
+ attr_reader :namespace, :metric_series
12
+
13
+ def type
14
+ 'generate-metrics'
15
+ end
16
+
17
+ def initialize(namespace, metric_series)
18
+ super()
19
+ @namespace = namespace
20
+ @metric_series = metric_series
21
+ end
22
+
23
+ def payload
24
+ {
25
+ namespace: @namespace,
26
+ series: @metric_series.map(&:to_h)
27
+ }
28
+ end
29
+
30
+ def ==(other)
31
+ other.is_a?(GenerateMetrics) && other.namespace == @namespace && other.metric_series == @metric_series
32
+ end
33
+
34
+ alias_method :eql?, :==
35
+
36
+ def hash
37
+ [self.class, @namespace, @metric_series].hash
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'logs' event.
10
+ # Logs with the same content are deduplicated at flush time.
11
+ class Log < Base
12
+ LEVELS = {
13
+ error: 'ERROR',
14
+ warn: 'WARN',
15
+ }.freeze
16
+
17
+ LEVELS_STRING = LEVELS.values.freeze
18
+
19
+ attr_reader :message, :level, :stack_trace, :count
20
+
21
+ def type
22
+ 'logs'
23
+ end
24
+
25
+ # @param message [String] the log message
26
+ # @param level [Symbol, String] the log level. Either :error, :warn, 'ERROR', or 'WARN'.
27
+ # @param stack_trace [String, nil] the stack trace
28
+ # @param count [Integer] the number of times the log was emitted. Used for deduplication.
29
+ def initialize(message:, level:, stack_trace: nil, count: 1)
30
+ super()
31
+ @message = message
32
+ @stack_trace = stack_trace
33
+
34
+ if level.is_a?(String) && LEVELS_STRING.include?(level)
35
+ # String level is used during object copy for deduplication
36
+ @level = level
37
+ elsif level.is_a?(Symbol)
38
+ # Symbol level is used by the regular log emitter user
39
+ @level = LEVELS.fetch(level) { |k| raise ArgumentError, "Invalid log level :#{k}" }
40
+ else
41
+ raise ArgumentError, "Invalid log level #{level}"
42
+ end
43
+
44
+ @count = count
45
+ end
46
+
47
+ def payload
48
+ {
49
+ logs: [
50
+ {
51
+ message: @message,
52
+ level: @level,
53
+ stack_trace: @stack_trace,
54
+ count: @count,
55
+ }.compact
56
+ ]
57
+ }
58
+ end
59
+
60
+ # override equality to allow for deduplication
61
+ def ==(other)
62
+ other.is_a?(Log) &&
63
+ other.message == @message &&
64
+ other.level == @level && other.stack_trace == @stack_trace && other.count == @count
65
+ end
66
+
67
+ alias_method :eql?, :==
68
+
69
+ def hash
70
+ [self.class, @message, @level, @stack_trace, @count].hash
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Telemetry
6
+ module Event
7
+ # Telemetry class for the 'message-batch' event.
8
+ class MessageBatch < Base
9
+ attr_reader :events
10
+
11
+ def type
12
+ 'message-batch'
13
+ end
14
+
15
+ def initialize(events)
16
+ super()
17
+ @events = events
18
+ end
19
+
20
+ def payload
21
+ @events.map do |event|
22
+ {
23
+ request_type: event.type,
24
+ payload: event.payload,
25
+ }
26
+ end
27
+ end
28
+
29
+ def ==(other)
30
+ other.is_a?(MessageBatch) && other.events == @events
31
+ end
32
+
33
+ alias_method :eql?, :==
34
+
35
+ def hash
36
+ [self.class, @events].hash
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end