diff --git a/lib/datadog/di/probe_notification_builder.rb b/lib/datadog/di/probe_notification_builder.rb index 5bdb7ad8fdb..c1d74fdd48a 100644 --- a/lib/datadog/di/probe_notification_builder.rb +++ b/lib/datadog/di/probe_notification_builder.rb @@ -101,12 +101,17 @@ def build_snapshot(context) end timestamp = timestamp_now + message = nil + evaluation_errors = [] + if segments = probe.template_segments + message, evaluation_errors = evaluate_template(segments, context) + end { service: settings.service, "debugger.snapshot": { id: SecureRandom.uuid, timestamp: timestamp, - evaluationErrors: [], + evaluationErrors: evaluation_errors, probe: { id: probe.id, version: 0, @@ -136,10 +141,7 @@ def build_snapshot(context) "dd.trace_id": active_trace&.id&.to_s, "dd.span_id": active_span&.id&.to_s, ddsource: 'dd_debugger', - message: probe.template_segments && evaluate_template( - probe.template_segments, - context - ), + message: message, #duration: duration ? duration * 1000 : 0), timestamp: timestamp, } @@ -172,7 +174,8 @@ def format_caller_locations(caller_locations) end def evaluate_template(template_segments, context) - template_segments.map do |segment| + evaluation_errors = [] + message = template_segments.map do |segment| case segment when String segment @@ -181,7 +184,11 @@ def evaluate_template(template_segments, context) else raise ArgumentError, "Invalid template segment type: #{segment}" end - end.join('') + rescue => exc + evaluation_errors << "#{exc.class}: #{exc}" + '[evaluation error]' + end.join + [message, evaluation_errors] end def timestamp_now diff --git a/sig/datadog/di/probe_notification_builder.rbs b/sig/datadog/di/probe_notification_builder.rbs index f5fb1410b99..05f5f919a57 100644 --- a/sig/datadog/di/probe_notification_builder.rbs +++ b/sig/datadog/di/probe_notification_builder.rbs @@ -24,7 +24,7 @@ module Datadog def format_caller_locations: (Array[untyped] callers) -> Array[Hash[Symbol,untyped]] - def evaluate_template: (untyped template, EL::Context context) -> String + def evaluate_template: (untyped template, EL::Context context) -> [String, Array[String]] def timestamp_now: () -> Integer diff --git a/spec/datadog/di/integration/probe_notification_builder_spec.rb b/spec/datadog/di/integration/probe_notification_builder_spec.rb index 948b06c2b22..8f55fdcd5be 100644 --- a/spec/datadog/di/integration/probe_notification_builder_spec.rb +++ b/spec/datadog/di/integration/probe_notification_builder_spec.rb @@ -135,10 +135,14 @@ {id: '11', name: 'bar', type: 'LOG_PROBE', where: { typeName: 'Foo', methodName: 'bar' }, - segments: [ - {str: 'hello'}, - {json: {ref: 'bar'}}, - ],} + segments: segments} + end + + let(:segments) do + [ + {str: 'hello'}, + {json: {ref: 'bar'}}, + ] end let(:probe) do @@ -165,6 +169,49 @@ expect(payload.fetch(:"debugger.snapshot").fetch(:captures)).to be nil end + context 'when there is an evaluation error' do + let(:segments) do + [ + {str: 'hello'}, + {json: {substring: ['bar', 'baz', 3]}}, + ] + end + + it 'replaces bogus expressions with [evaluation error] and fills out evaluation errors' do + payload = builder.build_snapshot(context) + expect(payload).to be_a(Hash) + expect(payload[:message]).to eq "hello[evaluation error]" + expect(payload[:"debugger.snapshot"][:evaluationErrors]).to eq ['ArgumentError: bad value for range'] + + # We asked to not create a snapshot + expect(payload.fetch(:"debugger.snapshot").fetch(:captures)).to be nil + end + end + + context 'when there are multiple evaluation errors' do + let(:segments) do + [ + {str: 'hello'}, + {json: {substring: ['bar', 'baz', 3]}}, + {json: {filter: ['bar', 'baz']}}, + {str: 'hello'}, + ] + end + + it 'attempts to evaluate all expressions' do + payload = builder.build_snapshot(context) + expect(payload).to be_a(Hash) + expect(payload[:message]).to eq "hello[evaluation error][evaluation error]hello" + expect(payload[:"debugger.snapshot"][:evaluationErrors]).to eq [ + 'ArgumentError: bad value for range', + 'Datadog::DI::Error::ExpressionEvaluationError: Bad collection type for filter: String', + ] + + # We asked to not create a snapshot + expect(payload.fetch(:"debugger.snapshot").fetch(:captures)).to be nil + end + end + context 'when variables are referenced but none are passed in' do let(:context) do Datadog::DI::EL::Context.new( diff --git a/spec/datadog/di/probe_notification_builder_spec.rb b/spec/datadog/di/probe_notification_builder_spec.rb index 8e8ff8b4937..ef4320c4a21 100644 --- a/spec/datadog/di/probe_notification_builder_spec.rb +++ b/spec/datadog/di/probe_notification_builder_spec.rb @@ -371,7 +371,7 @@ let(:expected) { %(test "'\\\\a\#{value}) } it 'substitutes correctly' do - expect(builder.send(:evaluate_template, template_segments, context)).to eq(expected) + expect(builder.send(:evaluate_template, template_segments, context)).to eq([expected, []]) end end end