Skip to content

Commit ec57fdb

Browse files
Merge pull request #180 from getsentry/config
Configuration [WIP]
2 parents 6647fa2 + 9d87140 commit ec57fdb

File tree

9 files changed

+231
-47
lines changed

9 files changed

+231
-47
lines changed

lib/mix/tasks/sentry.send_test_event.ex

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule Mix.Tasks.Sentry.SendTestEvent do
22
use Mix.Task
3+
alias Sentry.Config
34

45
@shortdoc "Attempts to send a test event to check Sentry configuration"
56
@moduledoc """
@@ -21,8 +22,8 @@ defmodule Mix.Tasks.Sentry.SendTestEvent do
2122
Mix.shell.info "public_key: #{public_key}"
2223
Mix.shell.info "secret_key: #{secret_key}"
2324
Mix.shell.info "included_environments: #{inspect included_environments()}"
24-
Mix.shell.info "current environment_name: #{inspect environment_name()}"
25-
Mix.shell.info "hackney_opts: #{inspect hackney_opts()}\n"
25+
Mix.shell.info "current environment_name: #{inspect Config.environment_name()}"
26+
Mix.shell.info "hackney_opts: #{inspect Config.hackney_opts()}\n"
2627
end
2728

2829
defp included_environments do
@@ -33,12 +34,8 @@ defmodule Mix.Tasks.Sentry.SendTestEvent do
3334
end
3435
end
3536

36-
defp environment_name, do: Application.get_env(:sentry, :environment_name)
37-
38-
defp hackney_opts, do: Application.get_env(:sentry, :hackney_opts, [])
39-
4037
defp maybe_send_event do
41-
env_name = environment_name()
38+
env_name = Config.environment_name()
4239
included_envs = included_environments()
4340

4441
if env_name in included_envs do

lib/sentry.ex

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Sentry do
22
use Application
33
import Supervisor.Spec
4-
alias Sentry.Event
4+
alias Sentry.{Event, Config}
55
require Logger
66

77
@moduledoc """
@@ -91,17 +91,14 @@ defmodule Sentry do
9191
See `Sentry.Logger`
9292
"""
9393

94-
@use_error_logger Application.get_env(:sentry, :use_error_logger, false)
95-
@default_environment_name Mix.env
96-
@max_hackney_connections Application.get_env(:sentry, :hackney_pool_max_connections, 50)
97-
@hackney_timeout Application.get_env(:sentry, :hackney_pool_timeout, 5000)
94+
@use_error_logger Config.use_error_logger()
9895

9996
@type task :: {:ok, Task.t} | :error | :excluded | :ignored
10097

10198
def start(_type, _opts) do
10299
children = [
103100
supervisor(Task.Supervisor, [[name: Sentry.TaskSupervisor]]),
104-
:hackney_pool.child_spec(Sentry.Client.hackney_pool_name(), [timeout: @hackney_timeout, max_connections: @max_hackney_connections])
101+
:hackney_pool.child_spec(Sentry.Client.hackney_pool_name(), [timeout: Config.hackney_timeout(), max_connections: Config.max_hackney_connections()])
105102
]
106103
opts = [strategy: :one_for_one, name: Sentry.Supervisor]
107104

@@ -118,7 +115,7 @@ defmodule Sentry do
118115
"""
119116
@spec capture_exception(Exception.t, Keyword.t) :: task
120117
def capture_exception(exception, opts \\ []) do
121-
filter_module = Application.get_env(:sentry, :filter, Sentry.DefaultEventFilter)
118+
filter_module = Config.filter()
122119
{source, opts} = Keyword.pop(opts, :event_source)
123120

124121
if filter_module.exclude_exception?(exception, source) do
@@ -156,9 +153,9 @@ defmodule Sentry do
156153
:ignored
157154
end
158155
def send_event(%Event{} = event, opts) do
159-
included_environments = Application.get_env(:sentry, :included_environments, [:dev, :test, :prod])
160-
environment_name = Application.get_env(:sentry, :environment_name, @default_environment_name)
161-
client = Application.get_env(:sentry, :client, Sentry.Client)
156+
included_environments = Config.included_environments()
157+
environment_name = Config.environment_name()
158+
client = Config.client()
162159

163160
if environment_name in included_environments do
164161
client.send_event(event, opts)

lib/sentry/client.ex

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ defmodule Sentry.Client do
3434
end
3535
"""
3636

37-
alias Sentry.{Event, Util}
37+
alias Sentry.{Event, Util, Config}
3838

3939
require Logger
4040

4141
@type get_dsn :: {String.t, String.t, Integer.t}
4242
@sentry_version 5
4343
@max_attempts 4
44-
@default_sample_rate 1.0
4544
@hackney_pool_name :sentry_pool
4645

4746
quote do
@@ -60,7 +59,7 @@ defmodule Sentry.Client do
6059
@spec send_event(Event.t) :: {:ok, Task.t | String.t} | :error | :unsampled
6160
def send_event(%Event{} = event, opts \\ []) do
6261
result = Keyword.get(opts, :result, :async)
63-
sample_rate = Keyword.get(opts, :sample_rate) || Application.get_env(:sentry, :sample_rate, @default_sample_rate)
62+
sample_rate = Keyword.get(opts, :sample_rate) || Config.sample_rate()
6463

6564
event = maybe_call_before_send_event(event)
6665

@@ -126,7 +125,7 @@ defmodule Sentry.Client do
126125
Hackney options can be set via the `hackney_opts` configuration option.
127126
"""
128127
def request(method, url, headers, body) do
129-
hackney_opts = Application.get_env(:sentry, :hackney_opts, [])
128+
hackney_opts = Config.hackney_opts()
130129
|> Keyword.put_new(:pool, @hackney_pool_name)
131130
with {:ok, 200, _, client} <- :hackney.request(method, url, headers, body, hackney_opts),
132131
{:ok, body} <- :hackney.body(client),
@@ -176,7 +175,7 @@ defmodule Sentry.Client do
176175
@spec get_dsn! :: get_dsn
177176
def get_dsn! do
178177
# {PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
179-
%URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol} = URI.parse(fetch_dsn())
178+
%URI{userinfo: userinfo, host: host, port: port, path: path, scheme: protocol} = URI.parse(Config.dsn())
180179
[public_key, secret_key] = String.split(userinfo, ":", parts: 2)
181180
[_, binary_project_id] = String.split(path, "/")
182181
project_id = String.to_integer(binary_project_id)
@@ -186,7 +185,7 @@ defmodule Sentry.Client do
186185
end
187186

188187
def maybe_call_after_send_event(result, event) do
189-
case Application.get_env(:sentry, :after_send_event) do
188+
case Config.after_send_event() do
190189
function when is_function(function, 2) ->
191190
function.(event, result)
192191
{module, function} ->
@@ -201,7 +200,7 @@ defmodule Sentry.Client do
201200
end
202201

203202
def maybe_call_before_send_event(event) do
204-
case Application.get_env(:sentry, :before_send_event) do
203+
case Config.before_send_event do
205204
function when is_function(function, 1) ->
206205
function.(event)
207206
{module, function} ->
@@ -217,13 +216,6 @@ defmodule Sentry.Client do
217216
@hackney_pool_name
218217
end
219218

220-
defp fetch_dsn do
221-
case Application.fetch_env!(:sentry, :dsn) do
222-
{:system, env_var} -> System.get_env(env_var)
223-
value -> value
224-
end
225-
end
226-
227219
defp log_api_error(body) do
228220
Logger.warn(fn ->
229221
["Failed to send Sentry event.", ?\n, body]

lib/sentry/config.ex

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
defmodule Sentry.Config do
2+
@moduledoc """
3+
This module provides the functionality for fetching configuration settings and their defaults.
4+
"""
5+
6+
@default_included_environments [:dev, :test, :prod]
7+
@default_environment_name Mix.env
8+
@default_max_hackney_connections 50
9+
@default_hackney_timeout 5000
10+
@default_exclude_patterns [~r"/_build/", ~r"/deps/", ~r"/priv/"]
11+
@default_path_pattern "**/*.ex"
12+
@default_context_lines 3
13+
@default_sample_rate 1.0
14+
15+
def validate_config! do
16+
end
17+
18+
def dsn do
19+
get_config(:dsn, check_dsn: false)
20+
end
21+
22+
def included_environments do
23+
get_config(:included_environments, default: @default_included_environments, check_dsn: false)
24+
end
25+
26+
def environment_name do
27+
get_config(:environment_name, default: @default_environment_name)
28+
end
29+
30+
def max_hackney_connections do
31+
get_config(:hackney_pool_max_connections, default: @default_max_hackney_connections, check_dsn: false)
32+
end
33+
34+
def hackney_timeout do
35+
get_config(:hackney_pool_timeout, default: @default_hackney_timeout, check_dsn: false)
36+
end
37+
38+
def tags do
39+
get_config(:tags, default: %{}, check_dsn: false)
40+
end
41+
42+
def release do
43+
get_config(:release)
44+
end
45+
46+
def server_name do
47+
get_config(:server_name)
48+
end
49+
50+
def filter do
51+
get_config(:filter, default: Sentry.DefaultEventFilter, check_dsn: false)
52+
end
53+
54+
def client do
55+
get_config(:client, default: Sentry.Client, check_dsn: false)
56+
end
57+
58+
def use_error_logger do
59+
get_config(:use_error_logger, default: false, check_dsn: false)
60+
end
61+
62+
def root_source_code_path do
63+
path = get_config(:root_source_code_path)
64+
65+
if path do
66+
path
67+
else
68+
raise ArgumentError.exception(":root_source_code_path must be configured")
69+
end
70+
end
71+
72+
def source_code_path_pattern do
73+
get_config(:source_code_path_pattern, default: @default_path_pattern)
74+
end
75+
76+
def source_code_exclude_patterns do
77+
get_config(:source_code_exclude_patterns, default: @default_exclude_patterns, check_dsn: false)
78+
end
79+
80+
def context_lines do
81+
get_config(:context_lines, default: @default_context_lines, check_dsn: false)
82+
end
83+
84+
def in_app_module_whitelist do
85+
get_config(:in_app_module_whitelist, default: [], check_dsn: false)
86+
end
87+
88+
def sample_rate do
89+
get_config(:sample_rate, default: @default_sample_rate, check_dsn: false)
90+
end
91+
92+
def hackney_opts do
93+
get_config(:hackney_opts, default: [], check_dsn: false)
94+
end
95+
96+
def before_send_event do
97+
get_config(:before_send_event, check_dsn: false)
98+
end
99+
100+
def after_send_event do
101+
get_config(:after_send_event, check_dsn: false)
102+
end
103+
104+
defp get_config(key, opts \\ []) when is_atom(key) do
105+
default = Keyword.get(opts, :default)
106+
check_dsn = Keyword.get(opts, :check_dsn, true)
107+
108+
environment_result = case get_from_application_environment(key) do
109+
{:ok, value} -> {:ok, value}
110+
:not_found -> get_from_system_environment(config_key_to_system_environment_key(key))
111+
end
112+
113+
result = case environment_result do
114+
{:ok, value} -> {:ok, value}
115+
:not_found -> if(check_dsn, do: get_from_dsn_query_string(Atom.to_string(key)), else: :not_found)
116+
end
117+
118+
case result do
119+
{:ok, value} -> value
120+
:not_found -> default
121+
end
122+
end
123+
124+
defp get_from_application_environment(key) when is_atom(key) do
125+
case Application.fetch_env(:sentry, key) do
126+
{:ok, {:system, env_var}} -> get_from_system_environment(env_var)
127+
{:ok, value} -> {:ok, value}
128+
:error -> :not_found
129+
end
130+
end
131+
132+
defp get_from_system_environment(key) when is_binary(key) do
133+
case System.get_env(key) do
134+
nil -> :not_found
135+
value -> {:ok, value}
136+
end
137+
end
138+
139+
defp get_from_dsn_query_string(key) when is_binary(key) do
140+
sentry_dsn = dsn()
141+
142+
if sentry_dsn do
143+
%URI{query: query} = URI.parse(sentry_dsn)
144+
query = query || ""
145+
result = URI.decode_query(query)
146+
|> Map.fetch(key)
147+
148+
case result do
149+
{:ok, value} -> {:ok, value}
150+
:error -> :not_found
151+
end
152+
else
153+
:not_found
154+
end
155+
end
156+
157+
defp config_key_to_system_environment_key(key) when is_atom(key) do
158+
string_key = Atom.to_string(key)
159+
|> String.upcase
160+
161+
"SENTRY_#{string_key}"
162+
end
163+
end

lib/sentry/event.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defmodule Sentry.Event do
3232

3333
@type t :: %__MODULE__{}
3434

35-
alias Sentry.{Event, Util}
35+
alias Sentry.{Event, Util, Config}
3636
@source_code_context_enabled Application.fetch_env!(:sentry, :enable_source_code_context)
3737
@source_files if(@source_code_context_enabled, do: Sentry.Sources.load_files(), else: nil)
3838

@@ -70,7 +70,7 @@ defmodule Sentry.Event do
7070
|> Map.merge(Keyword.get(opts, :extra, %{}))
7171
user = user_context
7272
|> Map.merge(Keyword.get(opts, :user, %{}))
73-
tags = Application.get_env(:sentry, :tags, %{})
73+
tags = Config.tags()
7474
|> Map.merge(tags_context)
7575
|> Map.merge(Keyword.get(opts, :tags, %{}))
7676
request = request_context
@@ -79,11 +79,11 @@ defmodule Sentry.Event do
7979

8080
level = Keyword.get(opts, :level, "error")
8181

82-
release = Application.get_env(:sentry, :release)
82+
release = Config.release()
8383

84-
server_name = Application.get_env(:sentry, :server_name)
84+
server_name = Config.server_name()
8585

86-
env = Application.get_env(:sentry, :environment_name)
86+
env = Config.environment_name()
8787

8888
%Event{
8989
culprit: culprit_from_stacktrace(stacktrace),
@@ -160,7 +160,7 @@ defmodule Sentry.Event do
160160

161161
@spec stacktrace_to_frames(Exception.stacktrace) :: [map]
162162
def stacktrace_to_frames(stacktrace) do
163-
in_app_module_whitelist = Application.get_env(:sentry, :in_app_module_whitelist, [])
163+
in_app_module_whitelist = Config.in_app_module_whitelist()
164164
stacktrace
165165
|> Enum.map(fn(line) ->
166166
{mod, function, arity, location} = line

lib/sentry/sources.ex

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
defmodule Sentry.Sources do
2+
alias Sentry.Config
23
@moduledoc """
34
This module is responsible for providing functionality that stores
45
the text of source files during compilation for displaying the
@@ -49,14 +50,10 @@ defmodule Sentry.Sources do
4950
@type file_map :: %{pos_integer() => String.t}
5051
@type source_map :: %{String.t => file_map}
5152

52-
@default_exclude_patterns [~r"/_build/", ~r"/deps/", ~r"/priv/"]
53-
@default_path_pattern "**/*.ex"
54-
@default_context_lines 3
55-
5653
def load_files do
57-
root_path = Application.fetch_env!(:sentry, :root_source_code_path)
58-
path_pattern = Application.get_env(:sentry, :source_code_path_pattern, @default_path_pattern)
59-
exclude_patterns = Application.get_env(:sentry, :source_code_exclude_patterns, @default_exclude_patterns)
54+
root_path = Config.root_source_code_path()
55+
path_pattern = Config.source_code_path_pattern()
56+
exclude_patterns = Config.source_code_exclude_patterns()
6057

6158
Path.join(root_path, path_pattern)
6259
|> Path.wildcard()
@@ -83,7 +80,7 @@ defmodule Sentry.Sources do
8380
"""
8481
@spec get_source_context(source_map, String.t, pos_integer()) :: {[String.t], String.t | nil, [String.t]}
8582
def get_source_context(files, file_name, line_number) do
86-
context_lines = Application.get_env(:sentry, :context_lines, @default_context_lines)
83+
context_lines = Config.context_lines()
8784
file = Map.get(files, file_name)
8885

8986
do_get_source_context(file, line_number, context_lines)

0 commit comments

Comments
 (0)