lib/ddtrace/contrib/sinatra/tracer.rb in ddtrace-0.34.2 vs lib/ddtrace/contrib/sinatra/tracer.rb in ddtrace-0.35.0
- old
+ new
@@ -19,10 +19,14 @@
# Keep track of the route name when the app is instantiated for an
# incoming request.
condition do
# If the option to prepend script names is enabled, then
# prepend the script name from the request onto the action.
+ #
+ # DEV: env['sinatra.route'] already exists with very similar information,
+ # DEV: but doesn't account for our `resource_script_names` logic.
+ #
@datadog_route = if Datadog.configuration[:sinatra][:resource_script_names]
"#{request.script_name}#{action}"
else
action
end
@@ -30,56 +34,115 @@
super
end
def self.registered(app)
- ::Sinatra::Base.module_eval do
- def render(engine, data, *)
- output = ''
- tracer = Datadog.configuration[:sinatra][:tracer]
- if tracer.enabled
- tracer.trace(Ext::SPAN_RENDER_TEMPLATE, span_type: Datadog::Ext::HTTP::TEMPLATE) do |span|
- # If data is a string, it is a literal template and we don't
- # want to record it.
- span.set_tag(Ext::TAG_TEMPLATE_NAME, data) if data.is_a? Symbol
+ app.use TracerMiddleware
- # Measure service stats
- Contrib::Analytics.set_measured(span)
+ app.after do
+ configuration = Datadog.configuration[:sinatra]
+ next unless configuration[:tracer].enabled
- output = super
- end
- else
- output = super
- end
+ # Ensures we only create a span for the top-most Sinatra middleware.
+ #
+ # If we traced all Sinatra middleware apps, we would have a chain
+ # of nested spans that were not responsible for handling this request,
+ # adding little value to users.
+ next if Sinatra::Env.middleware_traced?(env)
- output
- end
- end
+ span = Sinatra::Env.datadog_span(env) || Sinatra::Tracer.create_middleware_span(env, configuration)
- app.use TracerMiddleware
+ route = if defined?(@datadog_route)
+ @datadog_route
+ else
+ # Fallback in case no routes have matched
+ request.path
+ end
- app.before do
- return unless Datadog.configuration[:sinatra][:tracer].enabled
+ span.resource = "#{request.request_method} #{route}"
- span = Sinatra::Env.datadog_span(env)
span.set_tag(Datadog::Ext::HTTP::URL, request.path)
span.set_tag(Datadog::Ext::HTTP::METHOD, request.request_method)
+ span.set_tag(Ext::TAG_APP_NAME, app.settings.name)
+ span.set_tag(Ext::TAG_ROUTE_PATH, route)
+ if request.script_name && !request.script_name.empty?
+ span.set_tag(Ext::TAG_SCRIPT_NAME, request.script_name)
+ end
+
+ span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
+ span.set_error(env['sinatra.error']) if response.server_error?
+
+ Sinatra::Env.set_middleware_traced(env, true)
end
+ end
- app.after do
- return unless Datadog.configuration[:sinatra][:tracer].enabled
+ # Initializes a span for the top-most Sinatra middleware.
+ def self.create_middleware_span(env, configuration)
+ tracer = configuration[:tracer]
+ span = tracer.trace(
+ Ext::SPAN_REQUEST,
+ service: configuration[:service_name],
+ span_type: Datadog::Ext::HTTP::TYPE_INBOUND,
+ start_time: Sinatra::Env.middleware_start_time(env)
+ )
- span = Sinatra::Env.datadog_span(env)
+ Sinatra::Env.set_datadog_span(env, span)
+ span
+ end
- unless span
- Datadog::Logger.log.error('missing request span in :after hook')
- return
+ # Method overrides for Sinatra::Base
+ module Base
+ def render(engine, data, *)
+ tracer = Datadog.configuration[:sinatra][:tracer]
+ return super unless tracer.enabled
+
+ tracer.trace(Ext::SPAN_RENDER_TEMPLATE, span_type: Datadog::Ext::HTTP::TEMPLATE) do |span|
+ span.set_tag(Ext::TAG_TEMPLATE_ENGINE, engine)
+
+ # If data is a string, it is a literal template and we don't
+ # want to record it.
+ span.set_tag(Ext::TAG_TEMPLATE_NAME, data) if data.is_a? Symbol
+
+ # Measure service stats
+ Contrib::Analytics.set_measured(span)
+
+ super
end
+ end
- span.resource = "#{request.request_method} #{@datadog_route}"
- span.set_tag(Ext::TAG_ROUTE_PATH, @datadog_route)
- span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
- span.set_error(env['sinatra.error']) if response.server_error?
+ # Invoked when a matching route is found.
+ # This method yields directly to user code.
+ def route_eval
+ configuration = Datadog.configuration[:sinatra]
+ tracer = configuration[:tracer]
+
+ return super unless tracer.enabled
+
+ # For initialization of Sinatra middleware span in order
+ # guarantee that the Ext::SPAN_ROUTE span being created below
+ # has the middleware span as its parent.
+ Sinatra::Tracer.create_middleware_span(env, configuration) unless Sinatra::Env.datadog_span(env)
+
+ tracer.trace(
+ Ext::SPAN_ROUTE,
+ service: configuration[:service_name],
+ span_type: Datadog::Ext::HTTP::TYPE_INBOUND
+ ) do |span|
+ span.resource = "#{request.request_method} #{@datadog_route}"
+
+ span.set_tag(Ext::TAG_APP_NAME, settings.name || settings.superclass.name)
+ span.set_tag(Ext::TAG_ROUTE_PATH, @datadog_route)
+ if request.script_name && !request.script_name.empty?
+ span.set_tag(Ext::TAG_SCRIPT_NAME, request.script_name)
+ end
+
+ rack_request_span = env[Datadog::Contrib::Rack::TraceMiddleware::RACK_REQUEST_SPAN]
+ rack_request_span.resource = span.resource if rack_request_span
+
+ Contrib::Analytics.set_measured(span)
+
+ super
+ end
end
end
end
end
end