Current section

Files

Jump to
opentelemetry_tesla lib opentelemetry_tesla.ex
Raw

lib/opentelemetry_tesla.ex

defmodule OpentelemetryTesla do
alias OpenTelemetry.Span
@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 attach_request_exception_handler() do
:telemetry.attach(
"#{__MODULE__}.request_exception",
[:tesla, :request, :exception],
&handle_exception/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 handle_exception(
_event,
%{duration: native_time},
%{kind: kind, reason: reason, stacktrace: stacktrace},
_config
) do
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, %{})
exception = Exception.normalize(kind, reason, stacktrace)
Span.record_exception(ctx, exception, stacktrace, duration: native_time)
Span.set_status(ctx, OpenTelemetry.status(:error, ""))
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