Packages
Tesla middleware that creates OpenTelemetry spans and injects tracing headers into HTTP requests for Tesla clients.
Current section
Files
Jump to
Current section
Files
lib/opentelemetry_tesla.ex
defmodule OpentelemetryTesla do
@moduledoc """
OpentelemetryTesla uses [telemetry](https://hexdocs.pm/telemetry/) handlers to create `OpenTelemetry` spans from Tesla HTTP client events.
Supported events include request start/stop and also when an exception is raised.
## Usage
In your application start:
def start(_type, _args) do
OpenTelemetry.register_application_tracer(:my_telemetry_api)
OpentelemetryTesla.setup()
children = [
{Phoenix.PubSub, name: MyApp.PubSub},
MyAppWeb.Endpoint
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
"""
@tracer_id :opentelemetry_tesla
@doc """
Initializes and configures telemetry handlers.
"""
@spec setup() :: :ok
def setup() do
{:ok, otel_tracer_vsn} = :application.get_key(@tracer_id, :vsn)
OpenTelemetry.register_tracer(@tracer_id, otel_tracer_vsn)
attach_request_start_handler()
attach_request_stop_handler()
# attach_request_exception_handler()
:ok
end
defp attach_request_start_handler() do
:telemetry.attach(
"#{__MODULE__}.request_start",
[:tesla, :request, :start],
&handle_start/4,
%{}
)
end
defp attach_request_stop_handler() do
:telemetry.attach(
"#{__MODULE__}.request_stop",
[:tesla, :request, :stop],
&handle_stop/4,
%{}
)
end
defp handle_start(_event, _measurements, _metadata, _config) do
OpentelemetryTelemetry.start_telemetry_span(
@tracer_id,
"External HTTP Request",
%{},
%{kind: :server}
)
end
defp handle_stop(_event, %{duration: measurement}, metadata, _config) do
span_args =
metadata
|> headers_span_args()
|> :lists.append(span_args(metadata))
|> :lists.append([{"http.request.measurement", measurement}])
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, %{})
OpenTelemetry.Span.set_attributes(ctx, span_args)
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, %{})
end
defp headers_span_args(metadata) do
case Map.get(metadata, :env) do
nil ->
[]
map ->
map
|> Map.get(:headers)
|> Enum.map(fn {key, value} -> {"http.headers.#{key}", value} end)
end
end
defp span_args(metadata) do
case Map.get(metadata, :env) do
nil ->
[]
map ->
map
|> Map.take([:method, :opts, :query, :status, :url])
|> Enum.map(fn {key, value} -> {"http.#{key}", value} end)
end
end
end