Packages
sentry
10.0.3
13.3.0
13.2.0
13.1.0
13.0.1
13.0.0
12.0.3
12.0.2
12.0.1
12.0.0
11.0.4
11.0.3
11.0.2
11.0.1
11.0.0
10.10.0
10.9.0
10.8.1
10.8.0
10.7.1
10.7.0
10.6.2
10.6.1
10.6.0
10.5.0
10.4.0
10.3.0
10.2.1
10.2.0
10.2.0-rc.2
10.2.0-rc.1
10.1.0
10.0.3
10.0.2
10.0.1
10.0.0
9.1.0
9.0.0
8.1.0
8.0.6
8.0.5
8.0.4
8.0.3
8.0.2
8.0.1
8.0.0
8.0.0-rc.2
8.0.0-rc.1
8.0.0-rc.0
retired
7.2.5
7.2.4
7.2.3
7.2.2
7.2.1
7.2.0
7.1.0
7.0.6
7.0.5
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.4.2
6.4.1
6.4.0
6.3.0
6.2.1
6.2.0
6.1.0
6.0.5
6.0.4
6.0.3
6.0.2
6.0.1
6.0.0
5.0.1
5.0.0
4.0.3
4.0.2
4.0.1
4.0.0
3.0.0
2.2.0
2.1.0
2.0.2
2.0.1
2.0.0
1.1.2
1.1.1
1.1.0
1.0.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
The Official Elixir client for Sentry
Current section
Files
Jump to
Current section
Files
lib/sentry/transport.ex
defmodule Sentry.Transport do
@moduledoc false
# This module is exclusively responsible for POSTing envelopes to Sentry.
alias Sentry.Config
alias Sentry.Envelope
@default_retries [1000, 2000, 4000, 8000]
@sentry_version 5
@sentry_client "sentry-elixir/#{Mix.Project.config()[:version]}"
@spec default_retries() :: [pos_integer(), ...]
def default_retries do
@default_retries
end
# The "retries" parameter is there for better testing.
@spec post_envelope(Envelope.t(), module(), [non_neg_integer()]) ::
{:ok, envelope_id :: String.t()} | {:error, term()}
def post_envelope(%Envelope{} = envelope, client, retries \\ @default_retries)
when is_atom(client) and is_list(retries) do
case Envelope.to_binary(envelope) do
{:ok, body} ->
{endpoint, headers} = get_endpoint_and_headers()
post_envelope_with_retries(client, endpoint, headers, body, retries)
{:error, reason} ->
{:error, {:invalid_json, reason}}
end
end
defp post_envelope_with_retries(client, endpoint, headers, payload, retries_left) do
case request(client, endpoint, headers, payload) do
{:ok, id} ->
{:ok, id}
{:error, _reason} when retries_left != [] ->
[sleep_interval | retries_left] = retries_left
Process.sleep(sleep_interval)
post_envelope_with_retries(client, endpoint, headers, payload, retries_left)
{:error, reason} ->
{:error, {:request_failure, reason}}
end
end
defp request(client, endpoint, headers, body) do
with {:ok, 200, _headers, body} <- client.post(endpoint, headers, body),
{:ok, json} <- Config.json_library().decode(body) do
{:ok, Map.get(json, "id")}
else
{:ok, status, headers, _body} ->
error_header =
:proplists.get_value("X-Sentry-Error", headers, nil) ||
:proplists.get_value("x-sentry-error", headers, nil) || ""
{:error, "Received #{status} from Sentry server: #{error_header}"}
{:error, reason} ->
{:error, reason}
end
catch
kind, data -> {:error, {kind, data, __STACKTRACE__}}
end
defp get_endpoint_and_headers do
{endpoint, public_key, secret_key} = Config.dsn()
auth_query =
[
sentry_version: @sentry_version,
sentry_client: @sentry_client,
sentry_timestamp: System.system_time(:second),
sentry_key: public_key,
sentry_secret: secret_key
]
|> Enum.reject(fn {_, value} -> is_nil(value) end)
|> Enum.map_join(", ", fn {name, value} -> "#{name}=#{value}" end)
auth_headers = [
{"User-Agent", @sentry_client},
{"X-Sentry-Auth", "Sentry " <> auth_query}
]
{endpoint, auth_headers}
end
end