Method: Datadog::DI::ProbeNotificationBuilder#build_snapshot

Defined in:
lib/datadog/di/probe_notification_builder.rb

#build_snapshot(probe, rv: nil, snapshot: nil, path: nil, duration: nil, caller_locations: nil, args: nil, kwargs: nil, serialized_entry_args: nil) ⇒ 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.



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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/datadog/di/probe_notification_builder.rb', line 61

def build_snapshot(probe, rv: nil, snapshot: nil, path: nil,
  duration: nil, caller_locations: nil, args: nil, kwargs: nil,
  serialized_entry_args: nil)
  # TODO also verify that non-capturing probe does not pass
  # snapshot or vars/args into this method
  captures = if probe.capture_snapshot?
    if probe.method?
      {
        entry: {
          # standard:disable all
          arguments: if serialized_entry_args
            serialized_entry_args
          else
            (args || kwargs) && serializer.serialize_args(args, kwargs,
              depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
              attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
          end,
          throwable: nil,
          # standard:enable all
        },
        return: {
          arguments: {
            "@return": serializer.serialize_value(rv,
              depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
              attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count),
          },
          throwable: nil,
        },
      }
    elsif probe.line?
      {
        lines: snapshot && {
          probe.line_no => {locals: serializer.serialize_vars(snapshot,
            depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
            attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count,)},
        },
      }
    end
  end

  location = if probe.line?
    {
      file: path,
      lines: [probe.line_no],
    }
  elsif probe.method?
    {
      method: probe.method_name,
      type: probe.type_name,
    }
  end

  stack = if caller_locations
    format_caller_locations(caller_locations)
  end

  timestamp = timestamp_now
  {
    service: settings.service,
    "debugger.snapshot": {
      id: SecureRandom.uuid,
      timestamp: timestamp,
      evaluationErrors: [],
      probe: {
        id: probe.id,
        version: 0,
        location: location,
      },
      language: 'ruby',
      # TODO add test coverage for callers being nil
      stack: stack,
      captures: captures,
    },
    # In python tracer duration is under debugger.snapshot,
    # but UI appears to expect it here at top level.
    duration: duration ? (duration * 10**9).to_i : 0,
    host: nil,
    logger: {
      name: probe.file,
      method: probe.method_name || 'no_method',
      thread_name: Thread.current.name,
      # Dynamic instrumentation currently does not need thread_id for
      # anything. It can be sent if a customer requests it at which point
      # we can also determine which thread identifier to send
      # (Thread#native_thread_id or something else).
      thread_id: nil,
      version: 2,
    },
    # TODO add tests that the trace/span id is correctly propagated
    "dd.trace_id": active_trace&.id&.to_s,
    "dd.span_id": active_span&.id&.to_s,
    ddsource: 'dd_debugger',
    message: probe.template && evaluate_template(probe.template,
      duration: duration ? duration * 1000 : 0),
    timestamp: timestamp,
  }
end