Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/mix/tasks/sentry.send_test_event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ defmodule Mix.Tasks.Sentry.SendTestEvent do
Mix.shell().info("Client configuration:")

if Config.dsn() do
{endpoint, public_key, secret_key} = Config.dsn()
Mix.shell().info("server: #{endpoint}")
{base_uri, public_key, secret_key} = Config.dsn()
Mix.shell().info("server: #{base_uri}")
Mix.shell().info("public_key: #{public_key}")
Mix.shell().info("secret_key: #{secret_key}")
end
Expand Down
4 changes: 4 additions & 0 deletions lib/sentry/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,9 @@ defmodule Sentry.Application do
if config[:quantum][:cron][:enabled] do
Sentry.Integrations.Quantum.Cron.attach_telemetry_handler()
end

if config[:opentelemetry] do
Sentry.Integrations.Opentelemetry.setup()
end
end
end
58 changes: 55 additions & 3 deletions lib/sentry/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ defmodule Sentry.Config do
]
]
]
],
opentelemetry: [
type: :boolean,
doc: """
A boolean switch that sets up [OpenTelemetry](https://opentelemetry.io/docs/languages/erlang/)
automatically to instrument your application and export the spans to Sentry.
*Available since vTODO*.
"""
]
]

Expand Down Expand Up @@ -389,6 +397,9 @@ defmodule Sentry.Config do
@opts_schema NimbleOptions.new!(@raw_opts_schema)
@valid_keys Keyword.keys(@raw_opts_schema)

@sentry_version 5
@sentry_client "sentry-elixir/#{Mix.Project.config()[:version]}"

@spec validate!() :: keyword()
def validate! do
:sentry
Expand Down Expand Up @@ -461,6 +472,47 @@ defmodule Sentry.Config do
@spec dsn() :: nil | {String.t(), String.t(), String.t()}
def dsn, do: get(:dsn)

@spec envelope_endpoint() :: nil | String.t()
def envelope_endpoint do
case dsn() do
{base_uri, _, _} -> base_uri <> "envelope/"
_ -> nil
end
end

@spec spans_endpoint() :: nil | String.t()
def spans_endpoint do
case dsn() do
{base_uri, _, _} -> base_uri <> "spans/"
_ -> nil
end
end

@spec auth_headers() :: nil | [{String.t(), String.t()}]
def auth_headers do
case dsn() do
{_, public_key, secret_key} ->
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)

[
{"User-Agent", @sentry_client},
{"X-Sentry-Auth", "Sentry " <> auth_query}
]

_ ->
nil
end
end

# TODO: remove me on v11.0.0, :included_environments has been deprecated
# in v10.0.0.
@spec included_environments() :: :all | [String.t()]
Expand Down Expand Up @@ -710,10 +762,10 @@ defmodule Sentry.Config do
end

with {:ok, {base_path, project_id}} <- pop_project_id(uri.path) do
new_path = Enum.join([base_path, "api", project_id, "envelope"], "/") <> "/"
endpoint_uri = URI.merge(%URI{uri | userinfo: nil}, new_path)
new_path = Enum.join([base_path, "api", project_id], "/") <> "/"
base_uri = URI.merge(%URI{uri | userinfo: nil}, new_path)

{:ok, {URI.to_string(endpoint_uri), public_key, secret_key}}
{:ok, {URI.to_string(base_uri), public_key, secret_key}}
end
catch
message -> {:error, message}
Expand Down
16 changes: 16 additions & 0 deletions lib/sentry/integrations/opentelemetry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule Sentry.Integrations.Opentelemetry do
@moduledoc false

alias Sentry.Config

def setup do
if Config.dsn() do
Application.put_env(:opentelemetry, :traces_exporter, :otlp)
Application.put_env(:opentelemetry_exporter, :otlp_protocol, :http_protobuf)
Application.put_env(:opentelemetry_exporter, :otlp_traces_endpoint, Config.spans_endpoint())
Application.put_env(:opentelemetry_exporter, :otlp_headers, Config.auth_headers())
end
Comment on lines +7 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly believe this is a suboptimal idea.

The OTEL spec expects environment configuration to work correctly, among other things. DevOps wouldn't like this, and troubleshooting will require knowing this code exists instead of, for the most part, checking the config directory.
I have already encountered problems with adding or expecting global environment configuration, which has caused significant headaches.

Proper documentation significantly goes a long way when these configurations barely change.


:ok
end
end
33 changes: 7 additions & 26 deletions lib/sentry/transport.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ defmodule Sentry.Transport do
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
Expand All @@ -22,8 +20,13 @@ defmodule Sentry.Transport do
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)
post_envelope_with_retries(
client,
Config.envelope_endpoint(),
Config.auth_headers(),
body,
retries
)

{:error, reason} ->
{:error, {:invalid_json, reason}}
Expand Down Expand Up @@ -63,26 +66,4 @@ defmodule Sentry.Transport do
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
2 changes: 1 addition & 1 deletion test/mix/sentry.send_test_event_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ defmodule Mix.Tasks.Sentry.SendTestEventTest do

assert output =~ """
Client configuration:
server: http://localhost:#{bypass.port}/api/1/envelope/
server: http://localhost:#{bypass.port}/api/1/
public_key: public
secret_key: secret
current environment_name: "test"
Expand Down
6 changes: 3 additions & 3 deletions test/sentry/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ defmodule Sentry.ConfigTest do
describe "validate!/0" do
test ":dsn from option" do
assert Config.validate!(dsn: "https://public:[email protected]/1")[:dsn] ==
{"https://app.getsentry.com/api/1/envelope/", "public", "secret"}
{"https://app.getsentry.com/api/1/", "public", "secret"}

assert Config.validate!(dsn: nil)[:dsn] == nil
end

test ":dsn from system environment" do
with_system_env("SENTRY_DSN", "https://public:[email protected]/1", fn ->
assert Config.validate!([])[:dsn] ==
{"https://app.getsentry.com/api/1/envelope/", "public", "secret"}
{"https://app.getsentry.com/api/1/", "public", "secret"}
end)
end

Expand Down Expand Up @@ -213,7 +213,7 @@ defmodule Sentry.ConfigTest do
assert :ok = Config.put_config(:dsn, new_dsn)

assert Config.dsn() ==
{"https://app.getsentry.com/api/2/envelope/", "public", "secret"}
{"https://app.getsentry.com/api/2/", "public", "secret"}
end

test "validates the given key" do
Expand Down