Module: Koderift::Rails::NetHttpPatch
- Defined in:
- lib/koderift/rails/instrumentation.rb
Overview
Installed once when the gem loads (see the prepend at the bottom of this file). Writes to the thread-local external_calls array set by Instrumentation.capture; outside of a capture block it is a pass-through.
CONTRACT: exactly one entry per LOGICAL outbound call. Net::HTTP#request re-enters itself whenever it is called on a session that is not started:
def request(req, body = nil, &block)
unless started?
start { req['connection'] ||= 'close'; return request(req, body, &block) }
end
(net-http-0.9.1 lib/net/http.rb:2399-2405). Both frames run this prepended
patch, so one call was recorded twice. That branch also forces
Connection: close and start's ensure closes the socket, so an affected
call site double-counts on EVERY call, not intermittently.
Instance Method Summary collapse
Instance Method Details
#request(req, *args, &block) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/koderift/rails/instrumentation.rb', line 146 def request(req, *args, &block) calls = Thread.current[:koderift_external_calls] config = Thread.current[:koderift_external_config] trace_id = Thread.current[:koderift_trace_id] req['X-Koderift-Trace-ID'] = trace_id if trace_id # Nothing below this line may run before the pass-through return: the # spec stand-in defines only #request, and the header injection must # still happen when instrumentation is not capturing. return super(req, *args, &block) unless calls && config # The stdlib recursion re-invokes with the SAME req object, so request # identity separates "my own re-entry" from a genuinely different call # issued while this one is in flight. Do NOT use `started?` (it keeps # the inner frame, which cannot see connection setup, and the stand-in # does not define it) and do NOT use an object ivar (one Net::HTTP # shared across threads would silently lose most of its spans). in_flight = (Thread.current[:koderift_http_in_flight] ||= []) return super(req, *args, &block) if in_flight.any? { |r| r.equal?(req) } in_flight << req start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) begin response = super(req, *args, &block) ensure # Strictly LIFO: the inner frame returns above without pushing, and # push/pop are paired in one begin/ensure. The ensure also covers a # raise (Net::OpenTimeout, ECONNREFUSED, a raising response block) -- # without it one failure would silence every later call on this thread. in_flight.pop end duration = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round host = address.to_s ignored = ['127.0.0.1', 'localhost', '[::1]'].include?(host) || config.external_call_ignore_hosts.any? { |h| host.include?(h) } unless ignored raw_path = req.path.to_s.split('?').first.to_s segments = raw_path.split('/').reject(&:empty?).first(2) endpoint = segments.empty? ? '/' : '/' + segments.join('/') calls << { host: host, endpoint: endpoint, method: req.method.to_s.upcase, status: response.code.to_i, duration_ms: duration, trace_id: trace_id } end response end |