diff --git a/instrumentation/opentelemetry_tesla/lib/middleware/opentelemetry_tesla_middleware.ex b/instrumentation/opentelemetry_tesla/lib/middleware/opentelemetry_tesla_middleware.ex index d041e94..be5d969 100644 --- a/instrumentation/opentelemetry_tesla/lib/middleware/opentelemetry_tesla_middleware.ex +++ b/instrumentation/opentelemetry_tesla/lib/middleware/opentelemetry_tesla_middleware.ex @@ -12,7 +12,8 @@ defmodule Tesla.Middleware.OpenTelemetry do - `:span_name` - override span name. Can be a `String` for a static span name, or a function that takes the `Tesla.Env` and returns a `String` - + - `:mark_status_ok` - configures spans with a list of expected HTTP error codes to be marked as `ok`, + not as an error-containing spans """ alias OpenTelemetry.SemanticConventions.Trace @@ -27,6 +28,7 @@ defmodule Tesla.Middleware.OpenTelemetry do OpenTelemetry.Tracer.with_span span_name, %{kind: :client} do env + |> maybe_put_additional_ok_statuses(opts[:mark_status_ok]) |> Tesla.put_headers(:otel_propagator_text_map.inject([])) |> Tesla.run(next) |> set_span_attributes() @@ -49,6 +51,15 @@ defmodule Tesla.Middleware.OpenTelemetry do end end + defp maybe_put_additional_ok_statuses(env, [_ | _] = additional_ok_statuses) do + case env.opts[:additional_ok_statuses] do + nil -> Tesla.put_opt(env, :additional_ok_statuses, additional_ok_statuses) + _ -> env + end + end + + defp maybe_put_additional_ok_statuses(env, _additional_ok_statuses), do: env + defp set_span_attributes({_, %Tesla.Env{} = env} = result) do OpenTelemetry.Tracer.set_attributes(build_attrs(env)) @@ -59,8 +70,13 @@ defmodule Tesla.Middleware.OpenTelemetry do result end - defp handle_result({:ok, %Tesla.Env{status: status} = env}) when status >= 400 do - OpenTelemetry.Tracer.set_status(OpenTelemetry.status(:error, "")) + defp handle_result({:ok, %Tesla.Env{status: status, opts: opts} = env}) when status >= 400 do + span_status = + if status in Keyword.get(opts, :additional_ok_statuses, []), do: :ok, else: :error + + span_status + |> OpenTelemetry.status("") + |> OpenTelemetry.Tracer.set_status() {:ok, env} end diff --git a/instrumentation/opentelemetry_tesla/test/middleware/opentelemetry_tesla_middleware_test.exs b/instrumentation/opentelemetry_tesla/test/middleware/opentelemetry_tesla_middleware_test.exs index 1f8aaeb..5cfc300 100644 --- a/instrumentation/opentelemetry_tesla/test/middleware/opentelemetry_tesla_middleware_test.exs +++ b/instrumentation/opentelemetry_tesla/test/middleware/opentelemetry_tesla_middleware_test.exs @@ -289,6 +289,64 @@ defmodule Tesla.Middleware.OpenTelemetryTest do assert_receive {:span, span(status: {:status, :error, ""})} end + test "Marks Span status as :error if error status is within `mark_status_ok` opt list", + %{bypass: bypass} do + defmodule TestClient do + def get(client) do + Tesla.get(client, "/users/") + end + + def client(url) do + middleware = [ + {Tesla.Middleware.BaseUrl, url}, + {Tesla.Middleware.OpenTelemetry, mark_status_ok: [404]} + ] + + Tesla.client(middleware) + end + end + + Bypass.expect_once(bypass, "GET", "/users", fn conn -> + Plug.Conn.resp(conn, 404, "") + end) + + bypass.port + |> endpoint_url() + |> TestClient.client() + |> TestClient.get() + + assert_receive {:span, span(status: {:status, :ok, ""})} + end + + test "Marks Span status as :ok unless error status is within `mark_status_ok` opt list", + %{bypass: bypass} do + defmodule TestClient do + def get(client) do + Tesla.get(client, "/users/") + end + + def client(url) do + middleware = [ + {Tesla.Middleware.BaseUrl, url}, + {Tesla.Middleware.OpenTelemetry, mark_status_ok: []} + ] + + Tesla.client(middleware) + end + end + + Bypass.expect_once(bypass, "GET", "/users", fn conn -> + Plug.Conn.resp(conn, 404, "") + end) + + bypass.port + |> endpoint_url() + |> TestClient.client() + |> TestClient.get() + + assert_receive {:span, span(status: {:status, :error, ""})} + end + test "Appends query string parameters to http.url attribute", %{bypass: bypass} do defmodule TestClient do def get(client, id) do