mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-10-04 00:00:37 -04:00
323 lines
9.4 KiB
Ruby
323 lines
9.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "test_helper"
|
|
require "support/http_helpers"
|
|
require "ddtrace"
|
|
require "httpx/adapters/datadog"
|
|
|
|
class DatadogTest < Minitest::Test
|
|
include HTTPHelpers
|
|
|
|
def test_datadog_successful_get_request
|
|
set_datadog
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, verb: "GET", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_successful_post_request
|
|
set_datadog
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.post(uri, body: "bla")
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, verb: "POST", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_successful_multiple_requests
|
|
set_datadog
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
get_response, post_response = HTTPX.request([[:get, uri], [:post, uri]])
|
|
verify_status(get_response, 200)
|
|
verify_status(post_response, 200)
|
|
|
|
assert fetch_spans.size == 2, "expected to have 2 spans"
|
|
get_span, post_span = fetch_spans
|
|
verify_instrumented_request(get_response, span: get_span, verb: "GET", uri: uri)
|
|
verify_instrumented_request(post_response, span: post_span, verb: "POST", uri: uri)
|
|
verify_distributed_headers(get_response, span: get_span)
|
|
verify_distributed_headers(post_response, span: post_span)
|
|
verify_analytics_headers(get_span)
|
|
verify_analytics_headers(post_span)
|
|
end
|
|
|
|
def test_datadog_server_error_request
|
|
set_datadog
|
|
uri = URI(build_uri("/status/500", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 500)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, verb: "GET", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_client_error_request
|
|
set_datadog
|
|
uri = URI(build_uri("/status/404", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 404)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, verb: "GET", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_some_other_error
|
|
set_datadog
|
|
uri = URI("http://unexisting/")
|
|
|
|
response = HTTPX.get(uri)
|
|
assert response.is_a?(HTTPX::ErrorResponse), "response should contain errors"
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, verb: "GET", uri: uri, error: "HTTPX::NativeResolveError")
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_host_config
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
set_datadog(describe: /#{uri.host}/) do |http|
|
|
http.service_name = "httpbin"
|
|
http.split_by_domain = false
|
|
end
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, service: "httpbin", verb: "GET", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_split_by_domain
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
set_datadog do |http|
|
|
http.split_by_domain = true
|
|
end
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
verify_instrumented_request(response, service: uri.host, verb: "GET", uri: uri)
|
|
verify_distributed_headers(response)
|
|
end
|
|
|
|
def test_datadog_distributed_headers_disabled
|
|
set_datadog(distributed_tracing: false)
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
sampling_priority = 10
|
|
response = trace_with_sampling_priority(sampling_priority) do
|
|
HTTPX.get(uri)
|
|
end
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
span = fetch_spans.last
|
|
verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
|
|
verify_no_distributed_headers(response)
|
|
verify_analytics_headers(span)
|
|
end
|
|
|
|
def test_datadog_distributed_headers_sampling_priority
|
|
set_datadog
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
sampling_priority = 10
|
|
response = trace_with_sampling_priority(sampling_priority) do
|
|
HTTPX.get(uri)
|
|
end
|
|
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
span = fetch_spans.last
|
|
verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
|
|
verify_distributed_headers(response, span: span, sampling_priority: sampling_priority)
|
|
verify_analytics_headers(span)
|
|
end
|
|
|
|
def test_datadog_analytics_enabled
|
|
set_datadog(analytics_enabled: true)
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
span = fetch_spans.last
|
|
verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
|
|
verify_analytics_headers(span, sample_rate: 1.0)
|
|
end
|
|
|
|
def test_datadog_analytics_sample_rate
|
|
set_datadog(analytics_enabled: true, analytics_sample_rate: 0.5)
|
|
uri = URI(build_uri("/status/200", "http://#{httpbin}"))
|
|
|
|
response = HTTPX.get(uri)
|
|
verify_status(response, 200)
|
|
|
|
assert !fetch_spans.empty?, "expected to have spans"
|
|
span = fetch_spans.last
|
|
verify_instrumented_request(response, span: span, verb: "GET", uri: uri)
|
|
verify_analytics_headers(span, sample_rate: 0.5)
|
|
end
|
|
|
|
private
|
|
|
|
def setup
|
|
super
|
|
Datadog.registry[:httpx].reset_configuration!
|
|
end
|
|
|
|
def teardown
|
|
super
|
|
Datadog.registry[:httpx].reset_configuration!
|
|
end
|
|
|
|
def verify_instrumented_request(response, verb:, uri:, span: fetch_spans.first, service: "httpx", error: nil)
|
|
assert span.span_type == "http"
|
|
assert span.name == "httpx.request"
|
|
assert span.service == service
|
|
|
|
assert span.get_tag("out.host") == uri.host
|
|
assert span.get_tag("out.port") == "80"
|
|
assert span.get_tag("http.method") == verb
|
|
assert span.get_tag("http.url") == uri.path
|
|
if error
|
|
assert span.get_tag("error.type") == error
|
|
assert !span.get_tag("error.msg").nil?
|
|
assert span.status == 1
|
|
elsif response.status >= 400
|
|
assert span.get_tag("http.status_code") == response.status.to_s
|
|
assert span.get_tag("error.type") == "HTTPX::HTTPError"
|
|
assert !span.get_tag("error.msg").nil?
|
|
assert span.status == 1
|
|
else
|
|
assert span.status.zero?
|
|
assert span.get_tag("http.status_code") == response.status.to_s
|
|
# peer service
|
|
assert span.get_tag("peer.service") == span.service
|
|
end
|
|
end
|
|
|
|
def verify_no_distributed_headers(response)
|
|
request = response.instance_variable_get(:@request)
|
|
|
|
assert !request.headers.key?("x-datadog-parent-id")
|
|
assert !request.headers.key?("x-datadog-trace-id")
|
|
assert !request.headers.key?("x-datadog-sampling-priority")
|
|
end
|
|
|
|
def verify_distributed_headers(response, span: fetch_spans.first, sampling_priority: 1)
|
|
request = response.instance_variable_get(:@request)
|
|
|
|
assert request.headers["x-datadog-parent-id"] == span.span_id.to_s
|
|
assert request.headers["x-datadog-trace-id"] == span.trace_id.to_s
|
|
assert request.headers["x-datadog-sampling-priority"] == sampling_priority.to_s
|
|
end
|
|
|
|
def verify_analytics_headers(span, sample_rate: nil)
|
|
assert span.get_metric("_dd1.sr.eausr") == sample_rate
|
|
end
|
|
|
|
if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
|
|
|
|
def set_datadog(options = {}, &blk)
|
|
Datadog.configure do |c|
|
|
c.tracing.instrument(:httpx, options, &blk)
|
|
end
|
|
|
|
tracer # initialize tracer patches
|
|
end
|
|
|
|
def tracer
|
|
@tracer ||= begin
|
|
tr = Datadog::Tracing.send(:tracer)
|
|
def tr.write(trace)
|
|
@traces ||= []
|
|
@traces << trace
|
|
end
|
|
tr
|
|
end
|
|
end
|
|
|
|
def trace_with_sampling_priority(priority)
|
|
tracer.trace("foo.bar") do
|
|
tracer.active_trace.sampling_priority = priority
|
|
yield
|
|
end
|
|
end
|
|
else
|
|
|
|
def set_datadog(options = {}, &blk)
|
|
Datadog.configure do |c|
|
|
c.use(:httpx, options, &blk)
|
|
end
|
|
|
|
tracer # initialize tracer patches
|
|
end
|
|
|
|
def tracer
|
|
@tracer ||= begin
|
|
tr = Datadog.tracer
|
|
def tr.write(trace)
|
|
@spans ||= []
|
|
@spans << trace
|
|
end
|
|
tr
|
|
end
|
|
end
|
|
|
|
def trace_with_sampling_priority(priority)
|
|
tracer.trace("foo.bar") do |span|
|
|
span.context.sampling_priority = priority
|
|
yield
|
|
end
|
|
end
|
|
end
|
|
|
|
# Returns spans and caches it (similar to +let(:spans)+).
|
|
def spans
|
|
@spans ||= fetch_spans
|
|
end
|
|
|
|
# Retrieves and sorts all spans in the current tracer instance.
|
|
# This method does not cache its results.
|
|
def fetch_spans
|
|
spans = if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
|
|
(tracer.instance_variable_get(:@traces) || []).map(&:spans)
|
|
else
|
|
tracer.instance_variable_get(:@spans) || []
|
|
end
|
|
spans.flatten.sort! do |a, b|
|
|
if a.name == b.name
|
|
if a.resource == b.resource
|
|
if a.start_time == b.start_time
|
|
a.end_time <=> b.end_time
|
|
else
|
|
a.start_time <=> b.start_time
|
|
end
|
|
else
|
|
a.resource <=> b.resource
|
|
end
|
|
else
|
|
a.name <=> b.name
|
|
end
|
|
end
|
|
end
|
|
end
|