require 'faraday' require 'ddtrace/ext/http' require 'ddtrace/ext/integration' require 'ddtrace/ext/net' require 'ddtrace/propagation/http_propagator' require 'ddtrace/contrib/analytics' require 'ddtrace/contrib/faraday/ext' require 'ddtrace/contrib/http_annotation_helper' module Datadog module Contrib module Faraday # Middleware implements a faraday-middleware for ddtrace instrumentation class Middleware < ::Faraday::Middleware include Datadog::Ext::DistributedTracing include Datadog::Contrib::HttpAnnotationHelper def initialize(app, options = {}) super(app) @options = options end def call(env) # Resolve configuration settings to use for this request. # Do this once to reduce expensive regex calls. request_options = build_request_options!(env) request_options[:tracer].trace(Ext::SPAN_REQUEST) do |span| annotate!(span, env, request_options) propagate!(span, env) if request_options[:distributed_tracing] && request_options[:tracer].enabled app.call(env).on_complete { |resp| handle_response(span, resp, request_options) } end end private attr_reader :app def annotate!(span, env, options) span.resource = resource_name(env) service_name(env[:url].host, options) span.service = options[:split_by_domain] ? env[:url].host : options[:service_name] span.span_type = Datadog::Ext::HTTP::TYPE_OUTBOUND # Tag as an external peer service span.set_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE, span.service) # Set analytics sample rate if Contrib::Analytics.enabled?(options[:analytics_enabled]) Contrib::Analytics.set_sample_rate(span, options[:analytics_sample_rate]) end span.set_tag(Datadog::Ext::HTTP::URL, env[:url].path) span.set_tag(Datadog::Ext::HTTP::METHOD, env[:method].to_s.upcase) span.set_tag(Datadog::Ext::NET::TARGET_HOST, env[:url].host) span.set_tag(Datadog::Ext::NET::TARGET_PORT, env[:url].port) end def handle_response(span, env, options) if options.fetch(:error_handler).call(env) span.set_error(["Error #{env[:status]}", env[:body]]) end span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, env[:status]) end def propagate!(span, env) Datadog::HTTPPropagator.inject!(span.context, env[:request_headers]) end def resource_name(env) env[:method].to_s.upcase end def build_request_options!(env) datadog_configuration.options_hash # integration level settings .merge(datadog_configuration(env[:url].host).options_hash) # per-host override .merge(@options) # middleware instance override end def datadog_configuration(host = :default) Datadog.configuration[:faraday, host] end end end end end