Class: Datadog::DI::Probe Private

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/di/probe.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Encapsulates probe information (as received via remote config) and state (e.g. whether the probe was installed, or executed).

It is possible that remote configuration will specify an unsupported probe type or attribute, due to new DI functionality being added over time. We want to have predictable behavior in such cases, and since we can't guarantee that there will be enough information in a remote config payload to construct a functional probe, ProbeBuilder and remote config code must be prepared to deal with exceptions raised by Probe constructor in particular. Therefore, Probe constructor will raise an exception if it determines that there is not enough information (or conflicting information) in the arguments to create a functional probe, and upstream code is tasked with not spamming logs with notifications of such errors (and potentially limiting the attempts to construct probe from a given payload).

Note that, while remote configuration provides line numbers as an array, the only supported line number configuration is a single line (this is the case for all languages currently). Therefore Probe only supports one line number, and ProbeBuilder is responsible for extracting that one line number out of the array received from RC.

Note: only some of the parameter/attribute values are currently validated.

Constant Summary collapse

KNOWN_TYPES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[log].freeze
EVALUATE_AT_VALUES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[entry exit].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, type:, file: nil, line_no: nil, type_name: nil, method_name: nil, template: nil, template_segments: nil, capture_snapshot: false, max_capture_depth: nil, max_capture_attribute_count: nil, max_capture_collection_size: nil, max_capture_string_length: nil, capture_expressions: [], evaluate_at: nil, condition: nil, rate_limit: nil) ⇒ Probe

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Probe.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/datadog/di/probe.rb', line 39

def initialize(id:, type:,
  file: nil, line_no: nil, type_name: nil, method_name: nil,
  template: nil, template_segments: nil,
  capture_snapshot: false, max_capture_depth: nil,
  max_capture_attribute_count: nil,
  max_capture_collection_size: nil, max_capture_string_length: nil,
  capture_expressions: [],
  evaluate_at: nil,
  condition: nil,
  rate_limit: nil)
  # Perform some sanity checks here to detect unexpected attribute
  # combinations, in order to not do them in subsequent code.
  unless KNOWN_TYPES.include?(type)
    raise ArgumentError, "Unknown probe type: #{type}"
  end

  # Probe should be inferred to be a line probe if the specification
  # contains a line number. This how Java tracer works and Go tracer
  # is implementing the same behavior, and Go will have all 3 fields
  # (file path, line number and method name) for line probes.
  # Do not raise if line number and method name both exist - instead
  # treat the probe as a line probe.
  #
  # In the future we want to provide type name and method name to line
  # probes, so that the library can verify that the instrumented line
  # is in the method that the frontend showed to the user when the
  # user created the probe.

  if line_no && !file
    raise ArgumentError, "Probe contains line number but not file: #{id}"
  end

  if type_name && !method_name || method_name && !type_name
    raise ArgumentError, "Partial method probe definition: #{id}"
  end

  if line_no.nil? && method_name.nil?
    raise ArgumentError, "Unhandled probe type: neither method nor line probe: #{id}"
  end

  @id = id
  @type = type
  @file = file
  @line_no = line_no
  @type_name = type_name
  @method_name = method_name
  @template = template
  @template_segments = template_segments
  @capture_snapshot = !!capture_snapshot
  @max_capture_depth = max_capture_depth
  @max_capture_attribute_count = max_capture_attribute_count
  @max_capture_collection_size = max_capture_collection_size
  @max_capture_string_length = max_capture_string_length
  @capture_expressions = capture_expressions || []
  evaluate_at = :exit if evaluate_at.nil?
  unless EVALUATE_AT_VALUES.include?(evaluate_at)
    raise ArgumentError, "Unknown evaluate_at value: #{evaluate_at.inspect} (expected one of #{EVALUATE_AT_VALUES.inspect})"
  end
  @evaluate_at = evaluate_at
  @condition = condition

  @rate_limit = rate_limit || (capturing? ? 1 : 5000)
  @rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)

  # At most one report per second.
  # We create the rate limiter here even though it may never be used,
  # to avoid having to synchronize the creation since method probes
  # can be executed on multiple threads concurrently (even if line
  # probes are never executed concurrently since those are done in a
  # trace point).
  if condition
    @condition_evaluation_failed_rate_limiter = Datadog::Core::TokenBucket.new(1)
  end

  @emitting_notified = false
  @enabled = true
end

Instance Attribute Details

#capture_expressionsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



141
142
143
# File 'lib/datadog/di/probe.rb', line 141

def capture_expressions
  @capture_expressions
end

#conditionObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The compiled condition for the probe, as a String.



127
128
129
# File 'lib/datadog/di/probe.rb', line 127

def condition
  @condition
end

#condition_evaluation_failed_rate_limiterObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Rate limiter object for sending snapshots with evaluation errors for when probe condition evaluation fails. This rate limit is separate from the "base" rate limit for the probe because when the condition evaluation succeeds we want the "base" rate limit applied, not tainted by any evaluation errors (for example, the condition can be highly selective, and when it does not hold the evaluation may fail - we don't want to use up the probe rate limit for the errors).



159
160
161
# File 'lib/datadog/di/probe.rb', line 159

def condition_evaluation_failed_rate_limiter
  @condition_evaluation_failed_rate_limiter
end

#emitting_notified=(value) ⇒ Object (writeonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO emitting_notified reads and writes should in theory be locked, however since DI is only implemented for MRI in practice the missing locking should not cause issues.



284
285
286
# File 'lib/datadog/di/probe.rb', line 284

def emitting_notified=(value)
  @emitting_notified = value
end

#evaluate_atObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



143
144
145
# File 'lib/datadog/di/probe.rb', line 143

def evaluate_at
  @evaluate_at
end

#fileObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



119
120
121
# File 'lib/datadog/di/probe.rb', line 119

def file
  @file
end

#idObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



117
118
119
# File 'lib/datadog/di/probe.rb', line 117

def id
  @id
end

#instrumentation_moduleObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Instrumentation module for method probes.



270
271
272
# File 'lib/datadog/di/probe.rb', line 270

def instrumentation_module
  @instrumentation_module
end

#instrumentation_trace_pointObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Line trace point for line probes. Normally this would be a targeted trace point.



274
275
276
# File 'lib/datadog/di/probe.rb', line 274

def instrumentation_trace_point
  @instrumentation_trace_point
end

#instrumented_pathObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Actual path to the file instrumented by the probe, for line probes, when code tracking is available and line trace point is targeted. For untargeted line trace points instrumented path will be nil.



279
280
281
# File 'lib/datadog/di/probe.rb', line 279

def instrumented_path
  @instrumented_path
end

#line_noObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



120
121
122
# File 'lib/datadog/di/probe.rb', line 120

def line_no
  @line_no
end

#max_capture_attribute_countObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Configured maximum capture attribute count. Can be nil in which case the global default will be used.



135
136
137
# File 'lib/datadog/di/probe.rb', line 135

def max_capture_attribute_count
  @max_capture_attribute_count
end

#max_capture_collection_sizeObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



137
138
139
# File 'lib/datadog/di/probe.rb', line 137

def max_capture_collection_size
  @max_capture_collection_size
end

#max_capture_depthObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Configured maximum capture depth. Can be nil in which case the global default will be used.



131
132
133
# File 'lib/datadog/di/probe.rb', line 131

def max_capture_depth
  @max_capture_depth
end

#max_capture_string_lengthObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



139
140
141
# File 'lib/datadog/di/probe.rb', line 139

def max_capture_string_length
  @max_capture_string_length
end

#method_nameObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



122
123
124
# File 'lib/datadog/di/probe.rb', line 122

def method_name
  @method_name
end

#rate_limitObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Rate limit in effect, in invocations per second. Always present.



146
147
148
# File 'lib/datadog/di/probe.rb', line 146

def rate_limit
  @rate_limit
end

#rate_limiterObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Rate limiter object. For internal DI use only.



149
150
151
# File 'lib/datadog/di/probe.rb', line 149

def rate_limiter
  @rate_limiter
end

#templateObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



123
124
125
# File 'lib/datadog/di/probe.rb', line 123

def template
  @template
end

#template_segmentsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



124
125
126
# File 'lib/datadog/di/probe.rb', line 124

def template_segments
  @template_segments
end

#typeObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



118
119
120
# File 'lib/datadog/di/probe.rb', line 118

def type
  @type
end

#type_nameObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



121
122
123
# File 'lib/datadog/di/probe.rb', line 121

def type_name
  @type_name
end

Instance Method Details

#capture_entry_expressions?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


188
189
190
# File 'lib/datadog/di/probe.rb', line 188

def capture_entry_expressions?
  capture_expressions_only? && evaluate_at_entry?
end

#capture_expressions?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


165
166
167
# File 'lib/datadog/di/probe.rb', line 165

def capture_expressions?
  !@capture_expressions.empty?
end

#capture_expressions_only?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Capture-expression mode: the probe captures expressions but not a full snapshot. Snapshot capture serializes its own values, so expression capture only runs when a snapshot is not being taken.

Returns:

  • (Boolean)


184
185
186
# File 'lib/datadog/di/probe.rb', line 184

def capture_expressions_only?
  capture_expressions? && !capture_snapshot?
end

#capture_snapshot?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


161
162
163
# File 'lib/datadog/di/probe.rb', line 161

def capture_snapshot?
  @capture_snapshot
end

#capturing?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whether this probe captures user data, either as a full snapshot or via capture expressions.

Returns:

  • (Boolean)


173
174
175
# File 'lib/datadog/di/probe.rb', line 173

def capturing?
  capture_snapshot? || capture_expressions?
end

#disable!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



302
303
304
# File 'lib/datadog/di/probe.rb', line 302

def disable!
  @enabled = false
end

#emitting_notified?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


285
286
287
# File 'lib/datadog/di/probe.rb', line 285

def emitting_notified?
  !!@emitting_notified
end

#enabled?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


298
299
300
# File 'lib/datadog/di/probe.rb', line 298

def enabled?
  @enabled
end

#evaluate_at_entry?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


177
178
179
# File 'lib/datadog/di/probe.rb', line 177

def evaluate_at_entry?
  evaluate_at == :entry
end

#executed_on_line!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



293
294
295
296
# File 'lib/datadog/di/probe.rb', line 293

def executed_on_line!
  # TODO lock?
  @executed_on_line = true
end

#executed_on_line?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


289
290
291
# File 'lib/datadog/di/probe.rb', line 289

def executed_on_line?
  !!(defined?(@executed_on_line) && @executed_on_line)
end

#file_matches?(path) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns whether the provided path matches the user-designated file (of a line probe).

Delegates to Utils.path_can_match_spec? which performs fuzzy matching. See the comments in utils.rb for details.

Returns:

  • (Boolean)


259
260
261
262
263
264
265
266
267
# File 'lib/datadog/di/probe.rb', line 259

def file_matches?(path)
  if path.nil?
    raise ArgumentError, "Cannot match against a nil path"
  end
  unless file
    raise ArgumentError, "Probe does not have a file to match against"
  end
  Utils.path_can_match_spec?(path, file)
end

#line?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns whether the probe is a line probe.

Method probes may still specify a file name (to aid in locating the method or for stack traversal purposes?), therefore we do not check for file name/path presence here and just consider the line number.

Returns:

  • (Boolean)


201
202
203
204
205
206
# File 'lib/datadog/di/probe.rb', line 201

def line?
  # Constructor checks that file is given if line number is given,
  # but for safety, check again here since we somehow got a probe with
  # a line number but no file in the wild.
  !!(file && line_no)
end

#line_no!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the line number associated with the probe, raising Error::MissingLineNumber if the probe does not have a line number associated with it.

This method is used by instrumentation driver to ensure a line number that is passed into the instrumentation logic is actually a line number and not nil.



220
221
222
223
224
225
# File 'lib/datadog/di/probe.rb', line 220

def line_no!
  if line_no.nil?
    raise Error::MissingLineNumber, "Probe #{id} does not have a line number associated with it"
  end
  line_no
end

#locationObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Source code location of the probe, for diagnostic reporting.



242
243
244
245
246
247
248
249
250
251
252
# File 'lib/datadog/di/probe.rb', line 242

def location
  if method?
    "#{type_name}.#{method_name}"
  elsif line?
    "#{file}:#{line_no}"
  else
    # This case should not be possible because constructor verifies that
    # the probe is a method or a line probe.
    raise NotImplementedError
  end
end

#method?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns whether the probe is a method probe.

Returns:

  • (Boolean)


209
210
211
# File 'lib/datadog/di/probe.rb', line 209

def method?
  line_no.nil?
end

#method_name!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the method name associated with the probe, raising Error::MissingMethodName if the probe does not have a method name associated with it.

This method is used by instrumentation driver to ensure a method name that is passed into the instrumentation logic is actually a method name and not nil.



234
235
236
237
238
239
# File 'lib/datadog/di/probe.rb', line 234

def method_name!
  if method_name.nil?
    raise Error::MissingMethodName, "Probe #{id} does not have a method name associated with it"
  end
  method_name
end

#snapshot_serializer_limits(settings) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



192
193
194
# File 'lib/datadog/di/probe.rb', line 192

def snapshot_serializer_limits(settings)
  CaptureLimits.resolve(expr_limits: nil, probe: self, settings: settings)
end