From 821b191358d70446e382bec171184f876305a4af Mon Sep 17 00:00:00 2001 From: Oleg Pudeyev Date: Wed, 20 Nov 2024 15:21:50 -0500 Subject: [PATCH] DEBUG-2334 Set duration to 0 for DI line probes Line probes do not have a meaningful duration, however schema validation in system tests demands an integer value. Follow the behavior of other tracers and send 0. --- lib/datadog/di/probe_notification_builder.rb | 4 +- .../di/probe_notification_builder_spec.rb | 254 +++++++++++++++++- 2 files changed, 243 insertions(+), 15 deletions(-) diff --git a/lib/datadog/di/probe_notification_builder.rb b/lib/datadog/di/probe_notification_builder.rb index c8634ac4364..2f058eaadd7 100644 --- a/lib/datadog/di/probe_notification_builder.rb +++ b/lib/datadog/di/probe_notification_builder.rb @@ -121,7 +121,7 @@ def build_snapshot(probe, rv: nil, snapshot: nil, path: nil, }, # 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 : nil, + duration: duration ? (duration * 10**9).to_i : 0, host: nil, logger: { name: probe.file, @@ -139,7 +139,7 @@ def build_snapshot(probe, rv: nil, snapshot: nil, path: nil, "dd.span_id": Datadog::Tracing.active_span&.id&.to_s, ddsource: 'dd_debugger', message: probe.template && evaluate_template(probe.template, - duration: duration ? duration * 1000 : nil), + duration: duration ? duration * 1000 : 0), timestamp: timestamp, } end diff --git a/spec/datadog/di/probe_notification_builder_spec.rb b/spec/datadog/di/probe_notification_builder_spec.rb index 7d1b0804703..80043b51f12 100644 --- a/spec/datadog/di/probe_notification_builder_spec.rb +++ b/spec/datadog/di/probe_notification_builder_spec.rb @@ -42,32 +42,144 @@ end describe '#build_received' do - it 'returns a hash' do - expect(builder.build_received(probe)).to be_a(Hash) + + let(:payload) do + builder.build_received(probe) + end + + let(:expected) do + { + ddsource: 'dd_debugger', + debugger: { + diagnostics: { + parentId: nil, + probeId: '123', + probeVersion: 0, + runtimeId: String, + status: 'RECEIVED', + }, + }, + message: "Probe 123 has been received correctly", + service: 'test service', + timestamp: Integer, + } + end + + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) end end describe '#build_installed' do - it 'returns a hash' do - expect(builder.build_installed(probe)).to be_a(Hash) + + let(:payload) do + builder.build_installed(probe) + end + + let(:expected) do + { + ddsource: 'dd_debugger', + debugger: { + diagnostics: { + parentId: nil, + probeId: '123', + probeVersion: 0, + runtimeId: String, + status: 'INSTALLED', + }, + }, + message: "Probe 123 has been instrumented correctly", + service: 'test service', + timestamp: Integer, + } + end + + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) end end describe '#build_emitting' do - it 'returns a hash' do - expect(builder.build_emitting(probe)).to be_a(Hash) + + let(:payload) do + builder.build_emitting(probe) + end + + let(:expected) do + { + ddsource: 'dd_debugger', + debugger: { + diagnostics: { + parentId: nil, + probeId: '123', + probeVersion: 0, + runtimeId: String, + status: 'EMITTING', + }, + }, + message: "Probe 123 is emitting", + service: 'test service', + timestamp: Integer, + } + end + + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) end end describe '#build_executed' do + + let(:payload) { builder.build_executed(probe) } + context 'with template' do let(:probe) do Datadog::DI::Probe.new(id: '123', type: :log, file: 'X', line_no: 1, template: 'hello world') end - it 'returns a hash' do - expect(builder.build_executed(probe)).to be_a(Hash) + let(:expected) do + { + ddsource: 'dd_debugger', + 'dd.span_id': nil, + 'dd.trace_id': nil, + 'debugger.snapshot': { + captures: nil, + evaluationErrors: [], + id: String, + language: 'ruby', + probe: { + id: '123', + location: { + file: nil, + lines: [1], + }, + version: 0, + }, + stack: nil, + timestamp: Integer, + }, + message: "hello world", + service: 'test service', + timestamp: Integer, + logger: { + method: 'no_method', + name: 'X', + thread_id: nil, + thread_name: 'Thread.main', + version: 2, + }, + duration: 0, + host: nil, + } + end + + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) end end @@ -77,8 +189,45 @@ capture_snapshot: false) end - it 'returns a hash' do - expect(builder.build_executed(probe)).to be_a(Hash) + let(:expected) do + { + ddsource: 'dd_debugger', + 'dd.span_id': nil, + 'dd.trace_id': nil, + 'debugger.snapshot': { + captures: nil, + evaluationErrors: [], + id: String, + language: 'ruby', + probe: { + id: '123', + location: { + file: nil, + lines: [1], + }, + version: 0, + }, + stack: nil, + timestamp: Integer, + }, + message: nil, + service: 'test service', + timestamp: Integer, + logger: { + method: 'no_method', + name: 'X', + thread_id: nil, + thread_name: 'Thread.main', + version: 2, + }, + duration: 0, + host: nil, + } + end + + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) end end @@ -91,13 +240,92 @@ let(:trace_point) do instance_double(TracePoint).tap do |tp| # Returns an empty binding - expect(tp).to receive(:binding).and_return(binding) + expect(tp).to receive(:binding).and_return(get_binding) expect(tp).to receive(:path).and_return('/foo.rb') end end - it 'returns a hash' do - expect(builder.build_executed(probe, trace_point: trace_point)).to be_a(Hash) + let(:get_binding) do + x = 1 + binding + end + + let(:payload) do + builder.build_executed(probe, trace_point: trace_point) + end + + let(:expected) do + { + ddsource: 'dd_debugger', + 'dd.span_id': nil, + 'dd.trace_id': nil, + 'debugger.snapshot': { + captures: { + lines: { + 1 => { + locals: local_captures, + }, + }, + }, + evaluationErrors: [], + id: String, + language: 'ruby', + probe: { + id: '123', + location: { + file: '/foo.rb', + lines: [1], + }, + version: 0, + }, + stack: nil, + timestamp: Integer, + }, + message: nil, + service: 'test service', + timestamp: Integer, + logger: { + method: 'no_method', + name: 'X', + thread_id: nil, + thread_name: 'Thread.main', + version: 2, + }, + duration: 0, + host: nil, + } + end + + shared_examples 'returns a hash with expected contents' do + it 'returns a hash with expected contents' do + expect(payload).to be_a(Hash) + expect(payload).to match(expected) + end + end + + context 'when binding is empty' do + let(:get_binding) do + binding + end + + let(:local_captures) do + {} + end + + include_examples 'returns a hash with expected contents' + end + + context 'when binding is not empty' do + let(:get_binding) do + x = 1 + binding + end + + let(:local_captures) do + {x: {type: 'Integer', value: '1'}} + end + + include_examples 'returns a hash with expected contents' end end end