Skip to content

Commit 390caff

Browse files
committed
Add Datadog::Error value-object
This ensures all error-related data is properly encoded.
1 parent 3ccbf91 commit 390caff

File tree

11 files changed

+110
-45
lines changed

11 files changed

+110
-45
lines changed

lib/ddtrace.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'ddtrace/monkey'
22
require 'ddtrace/pin'
33
require 'ddtrace/tracer'
4+
require 'ddtrace/error'
45

56
# \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
67
module Datadog

lib/ddtrace/contrib/rails/action_controller.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,7 @@ def self.process_action(_name, start, finish, _id, payload)
6666
else
6767
status = '500'
6868
end
69-
if status.starts_with?('5')
70-
span.status = 1
71-
span.set_tag(Datadog::Ext::Errors::TYPE, error[0])
72-
span.set_tag(Datadog::Ext::Errors::MSG, error[1])
73-
end
69+
span.set_error(error) if status.starts_with?('5')
7470
end
7571
ensure
7672
span.start_time = start

lib/ddtrace/contrib/rails/action_view.rb

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,7 @@ def self.render_template(_name, start, finish, _id, payload)
7474
template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(payload.fetch(:identifier))
7575
span.set_tag('rails.template_name', template_name)
7676
span.set_tag('rails.layout', payload.fetch(:layout))
77-
78-
if payload[:exception]
79-
error = payload[:exception]
80-
span.status = 1
81-
span.set_tag(Datadog::Ext::Errors::TYPE, error[0])
82-
span.set_tag(Datadog::Ext::Errors::MSG, error[1])
83-
end
77+
span.set_error(payload[:exception]) if payload[:exception]
8478
ensure
8579
span.start_time = start
8680
span.finish(finish)
@@ -102,13 +96,7 @@ def self.render_partial(_name, start, finish, _id, payload)
10296
begin
10397
template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(payload.fetch(:identifier))
10498
span.set_tag('rails.template_name', template_name)
105-
106-
if payload[:exception]
107-
error = payload[:exception]
108-
span.status = 1
109-
span.set_tag(Datadog::Ext::Errors::TYPE, error[0])
110-
span.set_tag(Datadog::Ext::Errors::MSG, error[1])
111-
end
99+
span.set_error(payload[:exception]) if payload[:exception]
112100
ensure
113101
span.start_time = start
114102
span.finish(finish)

lib/ddtrace/contrib/rails/active_support.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,7 @@ def self.trace_cache(resource, _name, start, finish, _id, payload)
9797
store, = *Array.wrap(::Rails.configuration.cache_store).flatten
9898
span.set_tag('rails.cache.backend', store)
9999
span.set_tag('rails.cache.key', payload.fetch(:key))
100-
101-
if payload[:exception]
102-
error = payload[:exception]
103-
span.status = 1
104-
span.set_tag(Datadog::Ext::Errors::TYPE, error[0])
105-
span.set_tag(Datadog::Ext::Errors::MSG, error[1])
106-
end
100+
span.set_error(payload[:exception]) if payload[:exception]
107101
ensure
108102
span.start_time = start
109103
span.finish(finish)

lib/ddtrace/contrib/sinatra/tracer.rb

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,7 @@ def render(engine, data, *)
151151
span.resource = "#{request.request_method} #{@datadog_route}"
152152
span.set_tag('sinatra.route.path', @datadog_route)
153153
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
154-
155-
if response.server_error?
156-
span.status = 1
157-
158-
err = env['sinatra.error']
159-
if err
160-
span.set_tag(Datadog::Ext::Errors::TYPE, err.class)
161-
span.set_tag(Datadog::Ext::Errors::MSG, err.message)
162-
end
163-
end
164-
154+
span.set_error(env['sinatra.error']) if response.server_error?
165155
span.finish()
166156
ensure
167157
@datadog_request_span = nil

lib/ddtrace/error.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Datadog global namespace
2+
module Datadog
3+
# Error is a value-object responsible for sanitizing/encapsulating error data
4+
class Error
5+
attr_reader :type, :message, :backtrace
6+
7+
def self.build_from(value)
8+
case value
9+
when Error then value
10+
when Array then new(*value)
11+
when Exception then new(value.class, value.message, value.backtrace)
12+
when Net::HTTPResponse then new(value.code, value.message)
13+
end
14+
end
15+
16+
def initialize(type, message, backtrace = [])
17+
backtrace = Array(backtrace).join("\n")
18+
19+
@type = sanitize(type)
20+
@message = sanitize(message)
21+
@backtrace = sanitize(backtrace)
22+
end
23+
24+
private
25+
26+
def sanitize(value)
27+
value = value.to_s
28+
29+
return value if value.encoding == ::Encoding::UTF_8
30+
31+
value.encode(::Encoding::UTF_8)
32+
end
33+
end
34+
end

lib/ddtrace/ext/errors.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Datadog
22
module Ext
33
module Errors
4+
STATUS = 1
45
MSG = 'error.msg'.freeze
56
TYPE = 'error.type'.freeze
67
STACK = 'error.stack'.freeze

lib/ddtrace/span.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,14 @@ def get_metric(key)
9292

9393
# Mark the span with the given error.
9494
def set_error(e)
95-
return if e.nil?
96-
@status = 1
97-
@meta[Datadog::Ext::Errors::MSG] = e.message if e.respond_to?(:message) && e.message
98-
@meta[Datadog::Ext::Errors::TYPE] = e.class.to_s
99-
@meta[Datadog::Ext::Errors::STACK] = e.backtrace.join("\n") if e.respond_to?(:backtrace) && e.backtrace
95+
e = Error.build_from(e)
96+
97+
return unless e
98+
99+
@status = Ext::Errors::STATUS
100+
set_tag(Ext::Errors::TYPE, e.type)
101+
set_tag(Ext::Errors::MSG, e.message)
102+
set_tag(Ext::Errors::STACK, e.backtrace)
100103
end
101104

102105
# Mark the span finished at the current time and submit it.

test/contrib/rails/controller_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class TracingControllerTest < ActionController::TestCase
108108
assert_equal(1, span.status, 'span should be flagged as an error')
109109
assert_equal('ZeroDivisionError', span.get_tag('error.type'), 'type should contain the class name of the error')
110110
assert_equal('divided by 0', span.get_tag('error.msg'), 'msg should state we tried to divided by 0')
111-
assert_nil(span.get_tag('error.stack'))
111+
assert_empty(span.get_tag('error.stack'))
112112
end
113113

114114
test 'http error code should be trapped and reported as such, even with no exception' do

test/contrib/rails/rack_middleware_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class FullStackTest < ActionDispatch::IntegrationTest
8787
assert_equal(controller_span.status, 1)
8888
assert_equal(controller_span.get_tag('error.type'), 'ZeroDivisionError')
8989
assert_equal(controller_span.get_tag('error.msg'), 'divided by 0')
90-
assert_nil(controller_span.get_tag('error.stack')) # error stack is in rack span
90+
assert_empty(controller_span.get_tag('error.stack')) # error stack is in rack span
9191

9292
assert_equal('rack.request', request_span.name)
9393
assert_equal(request_span.span_type, 'http')
@@ -141,7 +141,7 @@ class FullStackTest < ActionDispatch::IntegrationTest
141141
assert_equal(controller_span.status, 1)
142142
assert_equal(controller_span.get_tag('error.type'), 'ZeroDivisionError')
143143
assert_equal(controller_span.get_tag('error.msg'), 'divided by 0')
144-
assert_nil(controller_span.get_tag('error.stack')) # error stack is in rack span
144+
assert_empty(controller_span.get_tag('error.stack')) # error stack is in rack span
145145

146146
assert_equal('rack.request', request_span.name)
147147
assert_equal(request_span.span_type, 'http')

0 commit comments

Comments
 (0)