Skip to content

Commit 501c769

Browse files
committed
feat: add OpentelemetryTesla.setup/1
1 parent adf9c5d commit 501c769

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
defmodule OpentelemetryTesla do
2+
require OpenTelemetry.SemanticConventions.Trace, as: Trace
3+
4+
alias OpenTelemetry.Span
5+
alias OpenTelemetry.Tracer
6+
7+
@tracer_id __MODULE__
8+
9+
def setup(_opts \\ []) do
10+
:telemetry.attach(
11+
{__MODULE__, :request_start},
12+
[:tesla, :request, :start],
13+
&__MODULE__.handle_request_start/4,
14+
%{}
15+
)
16+
17+
:telemetry.attach(
18+
{__MODULE__, :request_stop},
19+
[:tesla, :request, :stop],
20+
&__MODULE__.handle_request_stop/4,
21+
%{}
22+
)
23+
24+
:telemetry.attach(
25+
{__MODULE__, :request_exception},
26+
[:tesla, :request, :exception],
27+
&__MODULE__.handle_request_exception/4,
28+
%{}
29+
)
30+
end
31+
32+
@doc false
33+
def handle_request_start(_event, measurements, metadata, config) do
34+
url = Tesla.build_url(metadata.env.url, metadata.env.query)
35+
uri = URI.parse(url)
36+
method = String.upcase(Atom.to_string(metadata.env.method))
37+
38+
OpentelemetryTelemetry.start_telemetry_span(@tracer_id, "HTTP #{method}", metadata, %{
39+
start_time: measurements.system_time,
40+
kind: :client,
41+
attributes: %{
42+
Trace.http_method() => method,
43+
Trace.http_scheme() => uri.scheme,
44+
Trace.net_host_name() => uri.host,
45+
Trace.net_peer_name() => uri.host,
46+
Trace.net_peer_port() => uri.port,
47+
Trace.http_target() => uri.path,
48+
Trace.http_retry_count() => metadata.env.opts[:retry_count],
49+
Trace.http_url() => sanitize_url(uri),
50+
Trace.http_request_content_length() => get_content_length(metadata.env)
51+
}
52+
})
53+
end
54+
55+
@doc false
56+
def handle_request_stop(_event, measurements, metadata, _config) do
57+
OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
58+
59+
status = if metadata.env.status >= 400, do: :error, else: :ok
60+
61+
Tracer.set_status(status)
62+
63+
Tracer.set_attributes(%{
64+
:duration => measurements.duration,
65+
Trace.http_status_code() => metadata.env.status,
66+
Trace.http_response_content_length() => get_content_length(metadata.env)
67+
})
68+
69+
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
70+
end
71+
72+
@doc false
73+
def handle_request_exception(_event, measurements, metadata, _config) do
74+
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
75+
status = OpenTelemetry.status(:error, inspect(metadata.reason))
76+
77+
Tracer.set_status(status)
78+
79+
Span.record_exception(
80+
ctx,
81+
metadata.kind,
82+
metadata.stacktrace,
83+
%{duration: measurements.duration}
84+
)
85+
86+
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
87+
end
88+
89+
defp sanitize_url(uri) do
90+
%{uri | userinfo: nil}
91+
|> URI.to_string()
92+
end
93+
94+
defp get_content_length(env) do
95+
case Enum.find(env.headers, fn {k, _v} -> k == "content-length" end) do
96+
nil ->
97+
body_byte_size(env.body)
98+
99+
{_key, value} ->
100+
value
101+
end
102+
end
103+
104+
defp body_byte_size(nil), do: 0
105+
defp body_byte_size(body) when is_binary(body), do: byte_size(body)
106+
defp body_byte_size(_body), do: nil
107+
end

0 commit comments

Comments
 (0)