-
Notifications
You must be signed in to change notification settings - Fork 369
/
middleware.rb
161 lines (134 loc) · 5.07 KB
/
middleware.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
require 'excon'
require 'ddtrace/ext/http'
require 'ddtrace/ext/integration'
require 'ddtrace/ext/net'
require 'ddtrace/ext/distributed'
require 'ddtrace/propagation/http_propagator'
require 'ddtrace/contrib/analytics'
require 'ddtrace/contrib/excon/ext'
require 'ddtrace/contrib/http_annotation_helper'
module Datadog
module Contrib
module Excon
# Middleware implements an excon-middleware for ddtrace instrumentation
class Middleware < ::Excon::Middleware::Base
include Datadog::Contrib::HttpAnnotationHelper
DEFAULT_ERROR_HANDLER = lambda do |response|
Datadog::Ext::HTTP::ERROR_RANGE.cover?(response[:status])
end
def initialize(stack, options = {})
super(stack)
@default_options = datadog_configuration.options_hash.merge(options)
end
def request_call(datum)
begin
unless datum.key?(:datadog_span)
@options = build_request_options!(datum)
tracer.trace(Ext::SPAN_REQUEST).tap do |span|
datum[:datadog_span] = span
annotate!(span, datum)
propagate!(span, datum) if distributed_tracing?
end
end
rescue StandardError => e
Datadog.logger.debug(e.message)
end
@stack.request_call(datum)
end
def response_call(datum)
@stack.response_call(datum).tap do |d|
handle_response(d)
end
end
def error_call(datum)
handle_response(datum)
@stack.error_call(datum)
end
# Returns a child class of this trace middleware
# With options given as defaults.
def self.with(options = {})
Class.new(self) do
@options = options
# rubocop:disable Style/TrivialAccessors
def self.options
@options
end
def initialize(stack)
super(stack, self.class.options)
end
end
end
# Returns a copy of the default stack with the trace middleware injected
def self.around_default_stack
::Excon.defaults[:middlewares].dup.tap do |default_stack|
# If the default stack contains a version of the trace middleware already...
existing_trace_middleware = default_stack.find { |m| m <= Middleware }
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
# Inject after the ResponseParser middleware
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
default_stack.insert(response_middleware_index + 1, self)
end
end
private
def tracer
@options[:tracer]
end
def analytics_enabled?
Contrib::Analytics.enabled?(@options[:analytics_enabled])
end
def analytics_sample_rate
@options[:analytics_sample_rate]
end
def distributed_tracing?
@options[:distributed_tracing] == true && tracer.enabled
end
def error_handler
@options[:error_handler] || DEFAULT_ERROR_HANDLER
end
def annotate!(span, datum)
span.resource = datum[:method].to_s.upcase
span.service = service_name(datum[:host], @options)
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 analytics_enabled?
Contrib::Analytics.set_sample_rate(span, analytics_sample_rate)
end
span.set_tag(Datadog::Ext::HTTP::URL, datum[:path])
span.set_tag(Datadog::Ext::HTTP::METHOD, datum[:method].to_s.upcase)
span.set_tag(Datadog::Ext::NET::TARGET_HOST, datum[:host])
span.set_tag(Datadog::Ext::NET::TARGET_PORT, datum[:port])
end
def handle_response(datum)
if datum.key?(:datadog_span)
datum[:datadog_span].tap do |span|
return span if span.finished?
if datum.key?(:response)
response = datum[:response]
if error_handler.call(response)
span.set_error(["Error #{response[:status]}", response[:body]])
end
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response[:status])
end
span.set_error(datum[:error]) if datum.key?(:error)
span.finish
datum.delete(:datadog_span)
end
end
rescue StandardError => e
Datadog.logger.debug(e.message)
end
def propagate!(span, datum)
Datadog::HTTPPropagator.inject!(span.context, datum[:headers])
end
def build_request_options!(datum)
datadog_configuration(datum[:host]).options_hash.merge(@default_options)
end
def datadog_configuration(host = :default)
Datadog.configuration[:excon, host]
end
end
end
end
end