Verified Commit 54c5fb6b authored by Hercules Merscher's avatar Hercules Merscher 🌴
Browse files

fix: RequestInstrumenter propagation

parent f1487339
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ module Labkit

      start_time = ::Labkit::System.monotonic_time

      inject_trace_context(request)

      ActiveSupport::Notifications.instrument ::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, create_request_payload(request) do |payload|
        response =
          begin
@@ -57,7 +59,7 @@ module Labkit

    def create_request_payload(request)
      payload = {
        method: request.method,
        method: request.method
      }

      if request.uri.nil?
@@ -84,5 +86,22 @@ module Labkit

      payload
    end

    def inject_trace_context(request)
      return unless Labkit::Tracing.enabled?

      tracer = Labkit::Tracing::TracingUtils.tracer
      span = tracer.active_span
      return if span.nil?

      carrier = {}
      tracer.inject_context(span, carrier)

      carrier.each do |key, value|
        request[key] = value
      end
    rescue StandardError
      warn "Labkit::NetHttpPublisher: trace context propagation failed"
    end
  end
end
+0 −1
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ module Labkit

          # Core instrumentations
          c.use("OpenTelemetry::Instrumentation::ConcurrentRuby")
          c.use("OpenTelemetry::Instrumentation::Net::HTTP")

          # Rails components that don't conflict with LabKit
          c.use("OpenTelemetry::Instrumentation::ActionPack") if defined?(ActionPack)
+145 −111
Original line number Diff line number Diff line
@@ -6,53 +6,48 @@ require "httparty"
require "rest-client"
require "faraday"

describe Labkit::NetHttpPublisher do
  let(:rack_server) { TestRackServer.new(handler_proc) }
  before do
    described_class.labkit_prepend!
    rack_server.start
  end

  after do
    rack_server.stop
  end
require_relative "../support/test_rack_server"

  let(:successful_handler_proc) do
    ->(_req) {
SUCCESSFUL_HANDLER_PROC = lambda do |_req|
  [
    200,
    { "Content-Type" => "application/json" },
        ['{"calculation" => 123}'],
    ['{"calculation" => 123}']
  ]
    }
end

  let(:successful_response) do
    {
SUCCESSFUL_RESPONSE = {
  method: "GET", code: "200",
      scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
    }
  end
  scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
}.freeze

  let(:bad_request_handler_proc) do
    ->(_req) {
BAD_REQUEST_HANDLER_PROC = lambda do |_req|
  [
    400,
    { "Content-Type" => "application/json" },
        ['{"error" => "calculation field is required"}'],
    ['{"error" => "calculation field is required"}']
  ]
    }
end

  let(:bad_request_response) do
    {
BAD_REQUEST_RESPONSE = {
  method: "GET", code: "400",
      scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
    }
  scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
}.freeze

SERVER_ERROR_HANDLER = lambda do |_req|
  raise "something goes wrong"
end

  let(:server_error_handler) do
    ->(_req) { raise "something goes wrong" }
describe Labkit::NetHttpPublisher do
  let(:rack_server) { TestRackServer.new(handler_proc) }

  before do
    described_class.labkit_prepend!
    rack_server.start
  end

  after do
    rack_server.stop
  end

  describe "requests made by Net/HTTP broadcast request.external_http events" do
@@ -64,7 +59,7 @@ describe Labkit::NetHttpPublisher do
            request = Net::HTTP::Get.new("/api/v1/tests")
            http.request(request)
          },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> {
@@ -72,7 +67,7 @@ describe Labkit::NetHttpPublisher do
            request = Net::HTTP::Get.new("http://127.0.0.1:9202/api/v1/tests")
            http.request(request)
          },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> {
@@ -82,7 +77,7 @@ describe Labkit::NetHttpPublisher do
              http.request(request)
            end
          },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> {
@@ -91,14 +86,14 @@ describe Labkit::NetHttpPublisher do
              http.get(URI("http://127.0.0.1:9202/api/v1/tests"))
            end
          },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> {
            Net::HTTP.get(URI("http://127.0.0.1:9202/api/v1/tests"))
          },
          successful_handler_proc,
          [successful_response],
          SUCCESSFUL_HANDLER_PROC,
          [SUCCESSFUL_RESPONSE]
        ],
        [
          -> {
@@ -106,7 +101,7 @@ describe Labkit::NetHttpPublisher do
            request = Net::HTTP::Get.new("/api/v1/tests")
            http.request(request)
          },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> {
@@ -114,7 +109,7 @@ describe Labkit::NetHttpPublisher do
            request = Net::HTTP::Get.new("http://127.0.0.1:9202/api/v1/tests")
            http.request(request)
          },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> {
@@ -124,7 +119,7 @@ describe Labkit::NetHttpPublisher do
              http.request(request)
            end
          },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> {
@@ -133,11 +128,11 @@ describe Labkit::NetHttpPublisher do
              http.get(URI("http://127.0.0.1:9202/api/v1/tests"))
            end
          },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> { Net::HTTP.get(URI("http://127.0.0.1:9202/api/v1/tests")) },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> {
@@ -148,47 +143,47 @@ describe Labkit::NetHttpPublisher do
              http.request(request)
            end
          },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1", fragment: "xyz",
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1", fragment: "xyz"
            }
          ]
        ],
        [
          -> {
            Net::HTTP.get(URI("http://username:sensitvepassword@127.0.0.1:9202/api/v1/tests?abc=1#xyz"))
          },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1", fragment: "xyz",
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1", fragment: "xyz"
            }
          ]
        ],
        [
          -> {
            Net::HTTP.get(URI("http://127.0.0.1:9202/this/is/a/not/found/path"))
          },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "404",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/this/is/a/not/found/path", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/this/is/a/not/found/path", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { Net::HTTP.get(URI("http://127.0.0.1:9202/api/v1/tests")) },
          server_error_handler,
          SERVER_ERROR_HANDLER,
          [
            {
              method: "GET", code: "500",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { Net::HTTP.post(URI("http://127.0.0.1:9202/api/v1/tests"), '{ "a" => 1 }') },
@@ -196,9 +191,9 @@ describe Labkit::NetHttpPublisher do
          [
            {
              method: "POST", code: "204",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> {
@@ -211,16 +206,15 @@ describe Labkit::NetHttpPublisher do
          [
            {
              method: "HEAD", code: "204",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
        ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ]
      ]
    end

    with_them do
      it "broadcasts a correct notification" do
        begin
        subscriber = ActiveSupport::Notifications.subscribe("request.external_http") do |*args|
          event = ActiveSupport::Notifications::Event.new(*args)
          expect(event.name).to eq("request.external_http")
@@ -235,7 +229,6 @@ describe Labkit::NetHttpPublisher do
      end
    end
  end
  end

  describe "failed requests made by Net/HTTP broadcast request.external_http events" do
    context "with request timeout" do
@@ -245,7 +238,7 @@ describe Labkit::NetHttpPublisher do
          [
            200,
            { "Content-Type" => "application/json" },
            ['{"calculation" => 123}'],
            ['{"calculation" => 123}']
          ]
        }
      end
@@ -263,7 +256,7 @@ describe Labkit::NetHttpPublisher do
              method: "GET",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
              exception: ["Net::ReadTimeout", be_a(String)],
              exception_object: be_a(Net::ReadTimeout),
              exception_object: be_a(Net::ReadTimeout)
            )
          end
          http = Net::HTTP.new("127.0.0.1", 9202)
@@ -287,31 +280,31 @@ describe Labkit::NetHttpPublisher do
      [
        [
          -> { HTTParty.get("http://127.0.0.1:9202/api/v1/tests") },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> { HTTParty.get("http://127.0.0.1:9202/api/v1/tests", query: { abc: 1, xyz: 2 }) },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil
            }
          ]
        ],
        [
          -> { HTTParty.get("http://127.0.0.1:9202/api/v1/tests") },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> { HTTParty.get("http://127.0.0.1:9202/api/v1/tests") },
          server_error_handler,
          SERVER_ERROR_HANDLER,
          [
            {
              method: "GET", code: "500",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { HTTParty.post("http://127.0.0.1:9202/api/v1/tests", { "a" => 1 }) },
@@ -319,37 +312,37 @@ describe Labkit::NetHttpPublisher do
          [
            {
              method: "POST", code: "204",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { RestClient.get("http://127.0.0.1:9202/api/v1/tests") },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> { RestClient.get("http://127.0.0.1:9202/api/v1/tests", params: { abc: 1, xyz: 2 }) },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil
            }
          ]
        ],
        [
          -> { RestClient.get("http://127.0.0.1:9202/api/v1/tests") },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> { RestClient.get("http://127.0.0.1:9202/api/v1/tests") },
          server_error_handler,
          SERVER_ERROR_HANDLER,
          [
            {
              method: "GET", code: "500",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { RestClient.post("http://127.0.0.1:9202/api/v1/tests", { "a" => 1 }) },
@@ -357,38 +350,38 @@ describe Labkit::NetHttpPublisher do
          [
            {
              method: "POST", code: "204",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],

        [
          -> { Faraday.get("http://127.0.0.1:9202/api/v1/tests") },
          successful_handler_proc, [successful_response],
          SUCCESSFUL_HANDLER_PROC, [SUCCESSFUL_RESPONSE]
        ],
        [
          -> { Faraday.get("http://127.0.0.1:9202/api/v1/tests", { abc: 1, xyz: 2 }) },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: "abc=1&xyz=2", fragment: nil
            }
          ]
        ],
        [
          -> { Faraday.get("http://127.0.0.1:9202/api/v1/tests") },
          bad_request_handler_proc, [bad_request_response],
          BAD_REQUEST_HANDLER_PROC, [BAD_REQUEST_RESPONSE]
        ],
        [
          -> { Faraday.get("http://127.0.0.1:9202/api/v1/tests") },
          server_error_handler,
          SERVER_ERROR_HANDLER,
          [
            {
              method: "GET", code: "500",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> { Faraday.post("http://127.0.0.1:9202/api/v1/tests", '{ "a" => 1 }') },
@@ -396,24 +389,24 @@ describe Labkit::NetHttpPublisher do
          [
            {
              method: "POST", code: "204",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
            },
          ],
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil
            }
          ]
        ],
        [
          -> {
            connection = Net::HTTP.new("127.0.0.1", 9202, "127.0.0.1", 9202)
            connection.start { |http| http.get("/api/v1/tests") }
          },
          successful_handler_proc,
          SUCCESSFUL_HANDLER_PROC,
          [
            {
              method: "GET", code: "200",
              scheme: "http", host: "127.0.0.1", port: 9202, path: "/api/v1/tests", query: nil, fragment: nil,
              proxy_host: "127.0.0.1", proxy_port: 9202,
            },
          ],
        ],
              proxy_host: "127.0.0.1", proxy_port: 9202
            }
          ]
        ]
      ]
    end

@@ -441,4 +434,45 @@ describe Labkit::NetHttpPublisher do
      end
    end
  end

  describe "trace context propagation" do
    let(:trace_header_key) { "traceparent" }
    let(:trace_header_value) { "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" }

    let(:handler_proc) do
      ->(_req) {
        [
          200,
          { "Content-Type" => "application/json" },
          ['{"calculation" => 123}']
        ]
      }
    end

    let(:tracer) { instance_double(Labkit::Tracing::Adapters::BaseTracer, active_span: active_span) }
    let(:active_span) { instance_double(Labkit::Tracing::Adapters::BaseSpan) }

    before do
      allow(Labkit::Tracing).to receive(:enabled?).and_return(true)
      allow(Labkit::Tracing::TracingUtils).to receive(:tracer).and_return(tracer)
    end

    it "injects trace headers into Net::HTTP requests" do
      allow(tracer).to receive(:inject_context) do |_span, carrier|
        carrier[trace_header_key] = trace_header_value
      end

      Net::HTTP.get(URI("http://127.0.0.1:9202/api/v1/tests"))

      expect(rack_server.last_env["HTTP_TRACEPARENT"]).to eq(trace_header_value)
    end

    it "skips injection when there is no active span" do
      allow(tracer).to receive(:active_span).and_return(nil)

      Net::HTTP.get(URI("http://127.0.0.1:9202/api/v1/tests"))

      expect(rack_server.last_env["HTTP_TRACEPARENT"]).to be_nil
    end
  end
end
+0 −1
Original line number Diff line number Diff line
@@ -131,7 +131,6 @@ describe Labkit::Tracing::AutoInitialize do
        # The configuration block will be executed by Factory.create_tracer
        # Expect only non-conflicting instrumentations to be enabled
        expect(mock_config).to receive(:use).with("OpenTelemetry::Instrumentation::ConcurrentRuby")
        expect(mock_config).to receive(:use).with("OpenTelemetry::Instrumentation::Net::HTTP")

        # Rails components are conditionally enabled based on availability
        expect(mock_config).to receive(:use).with("OpenTelemetry::Instrumentation::ActionPack") if defined?(ActionPack)
+18 −2
Original line number Diff line number Diff line
# frozen_string_literal: true

class TestRackServer
  attr_reader :last_env

  def initialize(handler_proc)
    @handler_proc = handler_proc
    @started = false
    @last_env_mutex = Mutex.new
  end

  def start
@@ -12,9 +15,14 @@ class TestRackServer
      Port: 9202,
      Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::FATAL), AccessLog: [],
      StartCallback: -> { @started = true },
      StopCallback: -> { @started = false },
      StopCallback: -> { @started = false }
    )
    @server.mount "/api/v1/tests", Rack::Handler::WEBrick, @handler_proc
    app = lambda do |env|
      record_env(env)
      @handler_proc.call(env)
    end

    @server.mount "/api/v1/tests", Rack::Handler::WEBrick, app
    @thread = Thread.new { @server.start }
    @thread.abort_on_exception = false
  end
@@ -24,4 +32,12 @@ class TestRackServer
    sleep 0.01 until @started == false
    @thread&.kill
  end

  private

  def record_env(env)
    @last_env_mutex.synchronize do
      @last_env = env
    end
  end
end