datadog 2.6.0 → 2.7.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 +20 -1
- data/ext/libdatadog_api/crashtracker.c +6 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +4 -4
- data/lib/datadog/di/code_tracker.rb +30 -3
- data/lib/datadog/di/component.rb +108 -0
- data/lib/datadog/di/configuration/settings.rb +69 -44
- data/lib/datadog/di/contrib/active_record.rb +11 -0
- data/lib/datadog/di/error.rb +17 -0
- data/lib/datadog/di/instrumenter.rb +27 -11
- data/lib/datadog/di/probe.rb +23 -1
- data/lib/datadog/di/probe_manager.rb +246 -0
- data/lib/datadog/di/probe_notification_builder.rb +4 -12
- data/lib/datadog/di/probe_notifier_worker.rb +68 -41
- data/lib/datadog/di/serializer.rb +143 -95
- data/lib/datadog/di/transport.rb +22 -9
- data/lib/datadog/di.rb +49 -1
- data/lib/datadog/version.rb +1 -1
- metadata +10 -7
@@ -38,13 +38,42 @@ module Datadog
|
|
38
38
|
#
|
39
39
|
# @api private
|
40
40
|
class Serializer
|
41
|
-
|
41
|
+
# Third-party library integration / custom serializers.
|
42
|
+
#
|
43
|
+
# Dynamic instrumentation has limited payload sizes, and for efficiency
|
44
|
+
# reasons it is not desirable to transmit data to Datadog that will
|
45
|
+
# never contain useful information. Additionally, due to depth limits,
|
46
|
+
# desired data may not even be included in payloads when serialized
|
47
|
+
# with the default, naive serializer. Therefore, custom objects like
|
48
|
+
# ActiveRecord model instances may need custom serializers.
|
49
|
+
#
|
50
|
+
# CUSTOMER NOTE: The API for defining custom serializers is not yet
|
51
|
+
# finalized. Please create an issue at
|
52
|
+
# https://github.com/datadog/dd-trace-rb/issues describing the
|
53
|
+
# object(s) you wish to serialize so that we can ensure your use case
|
54
|
+
# will be supported as the library evolves.
|
55
|
+
#
|
56
|
+
# Note that the current implementation does not permit defining a
|
57
|
+
# serializer for a particular class, which is the simplest use case.
|
58
|
+
# This is because the library itself does not need this functionality
|
59
|
+
# yet, and it won't help for ActiveRecord models (that derive from
|
60
|
+
# a common base class but are all of different classes) or for Mongoid
|
61
|
+
# models (that do not have a common base class at all but include a
|
62
|
+
# standard Mongoid module).
|
63
|
+
@@flat_registry = []
|
64
|
+
def self.register(condition: nil, &block)
|
65
|
+
@@flat_registry << {condition: condition, proc: block}
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize(settings, redactor, telemetry: nil)
|
42
69
|
@settings = settings
|
43
70
|
@redactor = redactor
|
71
|
+
@telemetry = telemetry
|
44
72
|
end
|
45
73
|
|
46
74
|
attr_reader :settings
|
47
75
|
attr_reader :redactor
|
76
|
+
attr_reader :telemetry
|
48
77
|
|
49
78
|
# Serializes positional and keyword arguments to a method,
|
50
79
|
# as obtained by a method probe.
|
@@ -86,116 +115,135 @@ module Datadog
|
|
86
115
|
# (integers, strings, arrays, hashes).
|
87
116
|
#
|
88
117
|
# Respects string length, collection size and traversal depth limits.
|
89
|
-
def serialize_value(value, name: nil, depth: settings.dynamic_instrumentation.max_capture_depth)
|
90
|
-
|
91
|
-
|
92
|
-
|
118
|
+
def serialize_value(value, name: nil, depth: settings.dynamic_instrumentation.max_capture_depth, type: nil)
|
119
|
+
cls = type || value.class
|
120
|
+
begin
|
121
|
+
if redactor.redact_type?(value)
|
122
|
+
return {type: class_name(cls), notCapturedReason: "redactedType"}
|
123
|
+
end
|
93
124
|
|
94
|
-
|
95
|
-
|
96
|
-
|
125
|
+
if name && redactor.redact_identifier?(name)
|
126
|
+
return {type: class_name(cls), notCapturedReason: "redactedIdent"}
|
127
|
+
end
|
97
128
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
serialized.update(value: value.to_s)
|
104
|
-
when String, Symbol
|
105
|
-
need_dup = false
|
106
|
-
value = if String === value
|
107
|
-
# This is the only place where we duplicate the value, currently.
|
108
|
-
# All other values are immutable primitives (e.g. numbers).
|
109
|
-
# However, do not duplicate if the string is frozen, or if
|
110
|
-
# it is later truncated.
|
111
|
-
need_dup = !value.frozen?
|
112
|
-
value
|
113
|
-
else
|
114
|
-
value.to_s
|
129
|
+
@@flat_registry.each do |entry|
|
130
|
+
if (condition = entry[:condition]) && condition.call(value)
|
131
|
+
serializer_proc = entry.fetch(:proc)
|
132
|
+
return serializer_proc.call(self, value, name: nil, depth: depth)
|
133
|
+
end
|
115
134
|
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
|
136
|
+
serialized = {type: class_name(cls)}
|
137
|
+
case value
|
138
|
+
when NilClass
|
139
|
+
serialized.update(isNull: true)
|
140
|
+
when Integer, Float, TrueClass, FalseClass
|
141
|
+
serialized.update(value: value.to_s)
|
142
|
+
when Time
|
143
|
+
# This path also handles DateTime values although they do not need
|
144
|
+
# to be explicitly added to the case statement.
|
145
|
+
serialized.update(value: value.iso8601)
|
146
|
+
when Date
|
147
|
+
serialized.update(value: value.to_s)
|
148
|
+
when String, Symbol
|
120
149
|
need_dup = false
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
serialized.update(notCapturedReason: "collectionSize", size: value.length)
|
131
|
-
# same steep failure with array slices.
|
132
|
-
# https://github.com/soutaro/steep/issues/1219
|
133
|
-
value = value[0...max] || []
|
150
|
+
value = if String === value
|
151
|
+
# This is the only place where we duplicate the value, currently.
|
152
|
+
# All other values are immutable primitives (e.g. numbers).
|
153
|
+
# However, do not duplicate if the string is frozen, or if
|
154
|
+
# it is later truncated.
|
155
|
+
need_dup = !value.frozen?
|
156
|
+
value
|
157
|
+
else
|
158
|
+
value.to_s
|
134
159
|
end
|
135
|
-
|
136
|
-
|
160
|
+
max = settings.dynamic_instrumentation.max_capture_string_length
|
161
|
+
if value.length > max
|
162
|
+
serialized.update(truncated: true, size: value.length)
|
163
|
+
value = value[0...max]
|
164
|
+
need_dup = false
|
137
165
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
entries = []
|
147
|
-
value.each do |k, v|
|
148
|
-
if max != 0 && cur >= max
|
166
|
+
value = value.dup if need_dup
|
167
|
+
serialized.update(value: value)
|
168
|
+
when Array
|
169
|
+
if depth < 0
|
170
|
+
serialized.update(notCapturedReason: "depth")
|
171
|
+
else
|
172
|
+
max = settings.dynamic_instrumentation.max_capture_collection_size
|
173
|
+
if max != 0 && value.length > max
|
149
174
|
serialized.update(notCapturedReason: "collectionSize", size: value.length)
|
150
|
-
|
175
|
+
# same steep failure with array slices.
|
176
|
+
# https://github.com/soutaro/steep/issues/1219
|
177
|
+
value = value[0...max] || []
|
178
|
+
end
|
179
|
+
entries = value.map do |elt|
|
180
|
+
serialize_value(elt, depth: depth - 1)
|
151
181
|
end
|
152
|
-
|
153
|
-
|
182
|
+
serialized.update(elements: entries)
|
183
|
+
end
|
184
|
+
when Hash
|
185
|
+
if depth < 0
|
186
|
+
serialized.update(notCapturedReason: "depth")
|
187
|
+
else
|
188
|
+
max = settings.dynamic_instrumentation.max_capture_collection_size
|
189
|
+
cur = 0
|
190
|
+
entries = []
|
191
|
+
value.each do |k, v|
|
192
|
+
if max != 0 && cur >= max
|
193
|
+
serialized.update(notCapturedReason: "collectionSize", size: value.length)
|
194
|
+
break
|
195
|
+
end
|
196
|
+
cur += 1
|
197
|
+
entries << [serialize_value(k, depth: depth - 1), serialize_value(v, name: k, depth: depth - 1)]
|
198
|
+
end
|
199
|
+
serialized.update(entries: entries)
|
154
200
|
end
|
155
|
-
serialized.update(entries: entries)
|
156
|
-
end
|
157
|
-
else
|
158
|
-
if depth < 0
|
159
|
-
serialized.update(notCapturedReason: "depth")
|
160
201
|
else
|
161
|
-
|
162
|
-
|
163
|
-
|
202
|
+
if depth < 0
|
203
|
+
serialized.update(notCapturedReason: "depth")
|
204
|
+
else
|
205
|
+
fields = {}
|
206
|
+
max = settings.dynamic_instrumentation.max_capture_attribute_count
|
207
|
+
cur = 0
|
164
208
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
209
|
+
# MRI and JRuby 9.4.5+ preserve instance variable definition
|
210
|
+
# order when calling #instance_variables. Previous JRuby versions
|
211
|
+
# did not preserve order and returned the variables in arbitrary
|
212
|
+
# order.
|
213
|
+
#
|
214
|
+
# The arbitrary order is problematic because 1) when there are
|
215
|
+
# fewer instance variables than capture limit, the order in which
|
216
|
+
# the variables are shown in UI will change from one capture to
|
217
|
+
# the next and generally will be arbitrary to the user, and
|
218
|
+
# 2) when there are more instance variables than capture limit,
|
219
|
+
# *which* variables are captured will also change meaning user
|
220
|
+
# looking at the UI may have "new" instance variables appear and
|
221
|
+
# existing ones disappear as they are looking at multiple captures.
|
222
|
+
#
|
223
|
+
# For consistency, we should have some kind of stable order of
|
224
|
+
# instance variables on all supported Ruby runtimes, so that the UI
|
225
|
+
# stays consistent. Given that initial implementation of Ruby DI
|
226
|
+
# does not support JRuby, we don't handle JRuby's lack of ordering
|
227
|
+
# of #instance_variables here, but if JRuby is supported in the
|
228
|
+
# future this may need to be addressed.
|
229
|
+
ivars = value.instance_variables
|
186
230
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
231
|
+
ivars.each do |ivar|
|
232
|
+
if cur >= max
|
233
|
+
serialized.update(notCapturedReason: "fieldCount", fields: fields)
|
234
|
+
break
|
235
|
+
end
|
236
|
+
cur += 1
|
237
|
+
fields[ivar] = serialize_value(value.instance_variable_get(ivar), name: ivar, depth: depth - 1)
|
191
238
|
end
|
192
|
-
|
193
|
-
fields[ivar] = serialize_value(value.instance_variable_get(ivar), name: ivar, depth: depth - 1)
|
239
|
+
serialized.update(fields: fields)
|
194
240
|
end
|
195
|
-
serialized.update(fields: fields)
|
196
241
|
end
|
242
|
+
serialized
|
243
|
+
rescue => exc
|
244
|
+
telemetry&.report(exc, description: "Error serializing")
|
245
|
+
{type: class_name(cls), notSerializedReason: exc.to_s}
|
197
246
|
end
|
198
|
-
serialized
|
199
247
|
end
|
200
248
|
|
201
249
|
private
|
data/lib/datadog/di/transport.rb
CHANGED
@@ -35,31 +35,44 @@ module Datadog
|
|
35
35
|
StringIO.new(JSON.dump(payload)), 'application/json', 'event.json'
|
36
36
|
)
|
37
37
|
payload = {'event' => event_payload}
|
38
|
-
|
38
|
+
# Core transport unconditionally specifies headers to underlying
|
39
|
+
# Net::HTTP client, ends up passing 'nil' as headers if none are
|
40
|
+
# specified by us, which then causes Net::HTTP to die with an exception.
|
41
|
+
send_request('Probe status submission',
|
42
|
+
path: DIAGNOSTICS_PATH, form: payload, headers: {})
|
39
43
|
end
|
40
44
|
|
41
45
|
def send_input(payload)
|
42
|
-
send_request('Probe snapshot submission',
|
46
|
+
send_request('Probe snapshot submission',
|
47
|
+
path: INPUT_PATH, body: payload.to_s,
|
43
48
|
headers: {'content-type' => 'application/json'},)
|
44
49
|
end
|
45
50
|
|
51
|
+
# TODO status should use either input or diagnostics endpoints
|
52
|
+
# depending on agent version.
|
53
|
+
alias_method :send_status, :send_diagnostics
|
54
|
+
|
55
|
+
alias_method :send_snapshot, :send_input
|
56
|
+
|
46
57
|
private
|
47
58
|
|
48
59
|
attr_reader :client
|
49
60
|
|
50
|
-
def send_request(desc,
|
61
|
+
def send_request(desc, **options)
|
51
62
|
# steep:ignore:start
|
52
|
-
env = OpenStruct.new(
|
53
|
-
path: path,
|
54
|
-
form: payload,
|
55
|
-
headers: headers,
|
56
|
-
)
|
63
|
+
env = OpenStruct.new(**options)
|
57
64
|
# steep:ignore:end
|
58
65
|
response = client.post(env)
|
59
66
|
unless response.ok?
|
60
67
|
raise Error::AgentCommunicationError, "#{desc} failed: #{response.code}: #{response.payload}"
|
61
68
|
end
|
62
|
-
|
69
|
+
# Datadog::Core::Transport does not perform any exception mapping,
|
70
|
+
# therefore we could have any exception here from failure to parse
|
71
|
+
# agent URI for example.
|
72
|
+
# If we ever implement retries for network errors, we should distinguish
|
73
|
+
# actual network errors from non-network errors that are raised by
|
74
|
+
# transport code.
|
75
|
+
rescue => exc
|
63
76
|
raise Error::AgentCommunicationError, "#{desc} failed: #{exc.class}: #{exc}"
|
64
77
|
end
|
65
78
|
end
|
data/lib/datadog/di.rb
CHANGED
@@ -1,21 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'di/error'
|
4
|
-
require_relative 'di/configuration'
|
5
4
|
require_relative 'di/code_tracker'
|
5
|
+
require_relative 'di/component'
|
6
|
+
require_relative 'di/configuration'
|
6
7
|
require_relative 'di/extensions'
|
7
8
|
require_relative 'di/instrumenter'
|
8
9
|
require_relative 'di/probe'
|
10
|
+
require_relative 'di/probe_builder'
|
11
|
+
require_relative 'di/probe_manager'
|
12
|
+
require_relative 'di/probe_notification_builder'
|
13
|
+
require_relative 'di/probe_notifier_worker'
|
9
14
|
require_relative 'di/redactor'
|
10
15
|
require_relative 'di/serializer'
|
11
16
|
require_relative 'di/transport'
|
12
17
|
require_relative 'di/utils'
|
13
18
|
|
19
|
+
if defined?(ActiveRecord::Base)
|
20
|
+
# The third-party library integrations need to be loaded after the
|
21
|
+
# third-party libraries are loaded. Tracing and appsec use Railtie
|
22
|
+
# to delay integrations until all of the application's dependencies
|
23
|
+
# are loaded, when running under Rails. We should do the same here in
|
24
|
+
# principle, however DI currently only has an ActiveRecord integration
|
25
|
+
# and AR should be loaded before any application code is loaded, being
|
26
|
+
# part of Rails, therefore for now we should be OK to just require the
|
27
|
+
# AR integration from here.
|
28
|
+
require_relative 'di/contrib/active_record'
|
29
|
+
end
|
30
|
+
|
14
31
|
module Datadog
|
15
32
|
# Namespace for Datadog dynamic instrumentation.
|
16
33
|
#
|
17
34
|
# @api private
|
18
35
|
module DI
|
36
|
+
class << self
|
37
|
+
def enabled?
|
38
|
+
Datadog.configuration.dynamic_instrumentation.enabled
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
19
42
|
# Expose DI to global shared objects
|
20
43
|
Extensions.activate!
|
21
44
|
|
@@ -52,6 +75,31 @@ module Datadog
|
|
52
75
|
def code_tracking_active?
|
53
76
|
code_tracker&.active? || false
|
54
77
|
end
|
78
|
+
|
79
|
+
def component
|
80
|
+
# TODO uncomment when remote is merged
|
81
|
+
#Datadog.send(:components).dynamic_instrumentation
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
=begin not yet enabled
|
88
|
+
# :script_compiled trace point was added in Ruby 2.6.
|
89
|
+
if RUBY_VERSION >= '2.6'
|
90
|
+
begin
|
91
|
+
# Activate code tracking by default because line trace points will not work
|
92
|
+
# without it.
|
93
|
+
Datadog::DI.activate_tracking!
|
94
|
+
rescue => exc
|
95
|
+
if defined?(Datadog.logger)
|
96
|
+
Datadog.logger.warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
|
97
|
+
else
|
98
|
+
# We do not have Datadog logger potentially because DI code tracker is
|
99
|
+
# being loaded early in application boot process and the rest of datadog
|
100
|
+
# wasn't loaded yet. Output to standard error.
|
101
|
+
warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
|
55
102
|
end
|
56
103
|
end
|
57
104
|
end
|
105
|
+
=end
|
data/lib/datadog/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datadog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Datadog, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 14.
|
61
|
+
version: 14.1.0.1.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 14.
|
68
|
+
version: 14.1.0.1.0
|
69
69
|
description: |
|
70
70
|
datadog is Datadog's client library for Ruby. It includes a suite of tools
|
71
71
|
which provide visibility into the performance and security of Ruby applications,
|
@@ -364,13 +364,16 @@ files:
|
|
364
364
|
- lib/datadog/core/workers/runtime_metrics.rb
|
365
365
|
- lib/datadog/di.rb
|
366
366
|
- lib/datadog/di/code_tracker.rb
|
367
|
+
- lib/datadog/di/component.rb
|
367
368
|
- lib/datadog/di/configuration.rb
|
368
369
|
- lib/datadog/di/configuration/settings.rb
|
370
|
+
- lib/datadog/di/contrib/active_record.rb
|
369
371
|
- lib/datadog/di/error.rb
|
370
372
|
- lib/datadog/di/extensions.rb
|
371
373
|
- lib/datadog/di/instrumenter.rb
|
372
374
|
- lib/datadog/di/probe.rb
|
373
375
|
- lib/datadog/di/probe_builder.rb
|
376
|
+
- lib/datadog/di/probe_manager.rb
|
374
377
|
- lib/datadog/di/probe_notification_builder.rb
|
375
378
|
- lib/datadog/di/probe_notifier_worker.rb
|
376
379
|
- lib/datadog/di/redactor.rb
|
@@ -895,8 +898,8 @@ licenses:
|
|
895
898
|
- Apache-2.0
|
896
899
|
metadata:
|
897
900
|
allowed_push_host: https://rubygems.org
|
898
|
-
changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.
|
899
|
-
source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.
|
901
|
+
changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.7.0/CHANGELOG.md
|
902
|
+
source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.7.0
|
900
903
|
post_install_message:
|
901
904
|
rdoc_options: []
|
902
905
|
require_paths:
|
@@ -915,7 +918,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
915
918
|
- !ruby/object:Gem::Version
|
916
919
|
version: 2.0.0
|
917
920
|
requirements: []
|
918
|
-
rubygems_version: 3.
|
921
|
+
rubygems_version: 3.5.16
|
919
922
|
signing_key:
|
920
923
|
specification_version: 4
|
921
924
|
summary: Datadog tracing code for your Ruby applications
|