lib/elastic_apm/spies/net_http.rb (74 lines of code) (raw):
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# frozen_string_literal: true
module ElasticAPM
# @api private
module Spies
# @api private
class NetHTTPSpy
DISABLE_KEY = :__elastic_apm_net_http_disabled
TYPE = 'external'
SUBTYPE = 'http'
class << self
def disabled=(disabled)
Thread.current[DISABLE_KEY] = disabled
end
def disabled?
Thread.current[DISABLE_KEY] ||= false
end
def disable_in
self.disabled = true
begin
yield
ensure
self.disabled = false
end
end
end
# @api private
module Ext
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
def request(req, body = nil, &block)
unless (transaction = ElasticAPM.current_transaction)
return super(req, body, &block)
end
if ElasticAPM::Spies::NetHTTPSpy.disabled?
return super(req, body, &block)
end
host = req['host']&.split(':')&.first || address || 'localhost'
method = req.method.to_s.upcase
uri_or_path = URI(req.path)
# Support the case where a whole url is passed as a path to a nil host
uri =
if uri_or_path.host
uri_or_path
else
path, query = req.path.split('?')
url = use_ssl? ? +'https://' : +'http://'
url << host
url << ":#{port}" if port
url << path
url << "?#{query}" if query
URI(url)
end
context =
ElasticAPM::Span::Context.new(
http: { url: uri, method: method },
destination: ElasticAPM::Span::Context::Destination.from_uri(uri, type: SUBTYPE)
)
ElasticAPM.with_span(
"#{method} #{host}",
TYPE,
subtype: SUBTYPE,
context: context
) do |span|
trace_context = span&.trace_context || transaction.trace_context
trace_context.apply_headers { |key, value| req[key] = value }
result = super(req, body, &block)
if (http = span&.context&.http)
http.status_code = result.code
end
span&.outcome = Span::Outcome.from_http_status(result.code)
result
end
end
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
end
def install
Net::HTTP.prepend(Ext)
end
end
register 'Net::HTTP', 'net/http', NetHTTPSpy.new
end
end