changed
CHANGELOG.md
|
@@ -13,6 13,8 @@ Tweaks
|
13
13
|
* Make `plug_cowboy` an optional dep. [#201](https://github.com/newrelic/elixir_agent/pull/201)
|
14
14
|
* Use Erlang/OTP 21 built-in SSL hostname verification. [#197](https://github.com/newrelic/elixir_agent/pull/197)
|
15
15
|
* Reduce contention on transaction storage. [#209](https://github.com/newrelic/elixir_agent/pull/209)
|
16
|
* Detect Ecto repo via telemetry event instead of an Erlang tracer. [#214](https://github.com/newrelic/elixir_agent/pull/214)
|
17
|
* Fix for Distributed Traces that start from a Browser Agent. [#215](https://github.com/newrelic/elixir_agent/pull/215)
|
16
18
|
|
17
19
|
------
|
changed
VERSION
|
@@ -1 1 @@
|
1
|
- 1.18.0-rc.2
|
1
|
1.18.0-rc.4
|
changed
hex_metadata.config
|
@@ -115,5 115,10 @@
|
115
115
|
{<<"name">>,<<"ecto_sql">>},
|
116
116
|
{<<"optional">>,true},
|
117
117
|
{<<"repository">>,<<"hexpm">>},
|
118
|
- {<<"requirement">>,<<"~> 3.3">>}]]}.
|
119
|
- {<<"version">>,<<"1.18.0-rc.2">>}.
|
118
|
{<<"requirement">>,<<">= 3.4.0">>}],
|
119
|
[{<<"app">>,<<"ecto">>},
|
120
|
{<<"name">>,<<"ecto">>},
|
121
|
{<<"optional">>,true},
|
122
|
{<<"repository">>,<<"hexpm">>},
|
123
|
{<<"requirement">>,<<">= 3.4.1">>}]]}.
|
124
|
{<<"version">>,<<"1.18.0-rc.4">>}.
|
changed
lib/new_relic/distributed_trace.ex
|
@@ -85,7 85,7 @@ defmodule NewRelic.DistributedTrace do
|
85
85
|
|
86
86
|
def maybe_generate_sampling(context), do: context
|
87
87
|
|
88
|
- def maybe_generate_trace_id(%Context{parent_id: nil} = context) do
|
88
|
def maybe_generate_trace_id(%Context{trace_id: nil} = context) do
|
89
89
|
%{context | trace_id: generate_guid(16)}
|
90
90
|
end
|
91
91
|
|
|
@@ -112,7 112,10 @@ defmodule NewRelic.DistributedTrace do
|
112
112
|
end
|
113
113
|
|
114
114
|
def report_attributes(
|
115
|
- %Context{parent_id: nil} = context,
|
115
|
%Context{
|
116
|
parent_id: nil,
|
117
|
span_guid: nil
|
118
|
} = context,
|
116
119
|
transport_type: _type
|
117
120
|
) do
|
118
121
|
[
|
|
@@ -158,20 161,6 @@ defmodule NewRelic.DistributedTrace do
|
158
161
|
context
|
159
162
|
end
|
160
163
|
|
161
|
- def convert_to_outbound(%Context{parent_id: nil} = context) do
|
162
|
- %Context{
|
163
|
- source: context.source,
|
164
|
- account_id: AgentRun.account_id(),
|
165
|
- app_id: AgentRun.primary_application_id(),
|
166
|
- parent_id: nil,
|
167
|
- trust_key: context.trust_key,
|
168
|
- guid: context.guid,
|
169
|
- trace_id: context.trace_id,
|
170
|
- priority: context.priority,
|
171
|
- sampled: context.sampled
|
172
|
- }
|
173
|
- end
|
174
|
-
|
175
164
|
def convert_to_outbound(%Context{} = context) do
|
176
165
|
%Context{
|
177
166
|
source: context.source,
|
changed
lib/new_relic/distributed_trace/new_relic_context.ex
|
@@ -73,24 73,21 @@ defmodule NewRelic.DistributedTrace.NewRelicContext do
|
73
73
|
"d" =>
|
74
74
|
%{
|
75
75
|
"ty" => context.type,
|
76
|
- "ac" => context.account_id,
|
77
|
- "ap" => context.app_id,
|
76
|
"ac" => context.account_id |> to_string,
|
77
|
"ap" => context.app_id |> to_string,
|
78
78
|
"tx" => context.guid,
|
79
79
|
"tr" => context.trace_id,
|
80
|
"id" => context.span_guid,
|
80
81
|
"pr" => context.priority,
|
81
82
|
"sa" => context.sampled,
|
82
83
|
"ti" => context.timestamp
|
83
84
|
}
|
84
|
- |> maybe_put(:span_guid, "id", context.sampled, context.span_guid)
|
85
85
|
|> maybe_put(:trust_key, "tk", context.account_id, context.trust_key)
|
86
86
|
}
|
87
87
|
|> Jason.encode!()
|
88
88
|
|> Base.encode64()
|
89
89
|
end
|
90
90
|
|
91
|
- def maybe_put(data, :span_guid, key, true = _sampled, guid), do: Map.put(data, key, guid)
|
92
|
- def maybe_put(data, :span_guid, _key, false = _sampled, _guid), do: data
|
93
|
-
|
94
91
|
def maybe_put(data, :trust_key, _key, account_id, account_id), do: data
|
95
92
|
def maybe_put(data, :trust_key, _key, _account_id, nil), do: data
|
96
93
|
def maybe_put(data, :trust_key, key, _account_id, trust_key), do: Map.put(data, key, trust_key)
|
changed
lib/new_relic/logger.ex
|
@@ -44,6 44,10 @@ defmodule NewRelic.Logger do
|
44
44
|
{:reply, StringIO.flush(io_device), state}
|
45
45
|
end
|
46
46
|
|
47
|
def handle_call(:flush, _from, state) do
|
48
|
{:reply, "", state}
|
49
|
end
|
50
|
|
47
51
|
def handle_call({:logger, logger}, _from, old_state) do
|
48
52
|
{:ok, io_device} = device(logger)
|
49
53
|
{:reply, old_state, %{io_device: io_device, logger: logger}}
|
changed
lib/new_relic/telemetry/ecto.ex
|
@@ -18,22 18,24 @@ defmodule NewRelic.Telemetry.Ecto do
|
18
18
|
SQL query collection via configuration. See `NewRelic.Config` for details.
|
19
19
|
"""
|
20
20
|
|
21
|
- def start_link(otp_app) do
|
22
|
- enabled = NewRelic.Config.feature?(:ecto_instrumentation)
|
23
|
- ecto_repos = Application.get_env(otp_app, :ecto_repos)
|
24
|
- config = extract_config(otp_app, ecto_repos)
|
21
|
def start_link(repo: repo, opts: opts) do
|
22
|
config = %{
|
23
|
enabled?: NewRelic.Config.feature?(:ecto_instrumentation),
|
24
|
collect_sql?: NewRelic.Config.feature?(:sql_collection),
|
25
|
handler_id: {:new_relic_ecto, repo},
|
26
|
event: opts[:telemetry_prefix] [:query],
|
27
|
opts: opts
|
28
|
}
|
25
29
|
|
26
|
- GenServer.start_link(__MODULE__, config: config, enabled: enabled)
|
30
|
GenServer.start_link(__MODULE__, config)
|
27
31
|
end
|
28
32
|
|
29
|
- def init(config: _, enabled: false), do: :ignore
|
33
|
def init(%{enabled?: false}), do: :ignore
|
30
34
|
|
31
|
- def init(config: config, enabled: true) do
|
32
|
- log(config)
|
33
|
-
|
34
|
- :telemetry.attach_many(
|
35
|
def init(%{enabled?: true} = config) do
|
36
|
:telemetry.attach(
|
35
37
|
config.handler_id,
|
36
|
- config.events,
|
38
|
config.event,
|
37
39
|
&NewRelic.Telemetry.Ecto.Handler.handle_event/4,
|
38
40
|
config
|
39
41
|
)
|
|
@@ -45,59 47,4 @@ defmodule NewRelic.Telemetry.Ecto do
|
45
47
|
def terminate(_reason, %{handler_id: handler_id}) do
|
46
48
|
:telemetry.detach(handler_id)
|
47
49
|
end
|
48
|
-
|
49
|
- defp extract_config(otp_app, ecto_repos) do
|
50
|
- %{
|
51
|
- otp_app: otp_app,
|
52
|
- events: extract_events(otp_app, ecto_repos),
|
53
|
- repo_configs: extract_repo_configs(otp_app, ecto_repos),
|
54
|
- collect_sql?: NewRelic.Config.feature?(:sql_collection),
|
55
|
- handler_id: {:new_relic_ecto, otp_app}
|
56
|
- }
|
57
|
- end
|
58
|
-
|
59
|
- defp extract_events(otp_app, ecto_repos) do
|
60
|
- Enum.map(ecto_repos, fn repo ->
|
61
|
- ecto_telemetry_prefix(otp_app, repo) [:query]
|
62
|
- end)
|
63
|
- end
|
64
|
-
|
65
|
- defp extract_repo_configs(otp_app, ecto_repos) do
|
66
|
- Enum.into(ecto_repos, %{}, fn repo ->
|
67
|
- {repo, extract_repo_config(otp_app, repo)}
|
68
|
- end)
|
69
|
- end
|
70
|
-
|
71
|
- defp extract_repo_config(otp_app, repo) do
|
72
|
- Application.get_env(otp_app, repo)
|
73
|
- |> Map.new()
|
74
|
- |> case do
|
75
|
- %{url: url} ->
|
76
|
- uri = URI.parse(url)
|
77
|
-
|
78
|
- %{
|
79
|
- hostname: uri.host,
|
80
|
- port: uri.port,
|
81
|
- database: uri.path |> String.trim_leading("/")
|
82
|
- }
|
83
|
-
|
84
|
- config ->
|
85
|
- config
|
86
|
- end
|
87
|
- end
|
88
|
-
|
89
|
- defp ecto_telemetry_prefix(otp_app, repo) do
|
90
|
- Application.get_env(otp_app, repo)
|
91
|
- |> Keyword.get_lazy(:telemetry_prefix, fn ->
|
92
|
- repo
|
93
|
- |> Module.split()
|
94
|
- |> Enum.map(&(&1 |> Macro.underscore() |> String.to_atom()))
|
95
|
- end)
|
96
|
- end
|
97
|
-
|
98
|
- defp log(%{repo_configs: repo_configs}) do
|
99
|
- for {repo, _config} <- repo_configs do
|
100
|
- NewRelic.log(:info, "Detected Ecto Repo `#{inspect(repo)}`")
|
101
|
- end
|
102
|
- end
|
103
50
|
end
|
changed
lib/new_relic/telemetry/ecto/handler.ex
|
@@ -17,9 17,9 @@ defmodule NewRelic.Telemetry.Ecto.Handler do
|
17
17
|
queue_time_ms = measurements[:queue_time] |> to_ms
|
18
18
|
decode_time_ms = measurements[:decode_time] |> to_ms
|
19
19
|
|
20
|
- database = config.repo_configs[repo][:database] || "unknown"
|
21
|
- hostname = config.repo_configs[repo][:hostname] || "unknown"
|
22
|
- port = config.repo_configs[repo][:port] || "unknown"
|
20
|
database = config.opts[:database] || "unknown"
|
21
|
hostname = config.opts[:hostname] || "unknown"
|
22
|
port = config.opts[:port] || "unknown"
|
23
23
|
|
24
24
|
query = (config.collect_sql? && metadata.query) || ""
|
changed
lib/new_relic/telemetry/ecto/supervisor.ex
|
@@ -3,15 3,29 @@ defmodule NewRelic.Telemetry.Ecto.Supervisor do
|
3
3
|
|
4
4
|
use DynamicSupervisor
|
5
5
|
|
6
|
@ecto_repo_init [:ecto, :repo, :init]
|
7
|
|
6
8
|
def start_link() do
|
7
9
|
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
|
8
10
|
end
|
9
11
|
|
10
|
- def start_child(otp_app) do
|
11
|
- DynamicSupervisor.start_child(__MODULE__, {NewRelic.Telemetry.Ecto, otp_app})
|
12
|
- end
|
13
|
-
|
14
12
|
def init(:ok) do
|
13
|
:telemetry.attach(
|
14
|
{:new_relic_ecto, :supervisor},
|
15
|
@ecto_repo_init,
|
16
|
&__MODULE__.handle_event/4,
|
17
|
%{}
|
18
|
)
|
19
|
|
15
20
|
DynamicSupervisor.init(strategy: :one_for_one)
|
16
21
|
end
|
22
|
|
23
|
def handle_event(@ecto_repo_init, _, %{repo: repo, opts: opts}, _) do
|
24
|
NewRelic.log(:info, "Detected Ecto Repo `#{inspect(repo)}`")
|
25
|
|
26
|
DynamicSupervisor.start_child(
|
27
|
__MODULE__,
|
28
|
{NewRelic.Telemetry.Ecto, [repo: repo, opts: opts]}
|
29
|
)
|
30
|
end
|
17
31
|
end
|
changed
lib/new_relic/transaction/monitor.ex
|
@@ -90,15 90,6 @@ defmodule NewRelic.Transaction.Monitor do
|
90
90
|
{:noreply, state}
|
91
91
|
end
|
92
92
|
|
93
|
- def handle_info(
|
94
|
- {:trace_ts, _pid, :call,
|
95
|
- {Ecto.Repo.Supervisor, :start_link, [_repo, otp_app, _adapter, _opts]}, _timestamp},
|
96
|
- state
|
97
|
- ) do
|
98
|
- NewRelic.Telemetry.Ecto.Supervisor.start_child(otp_app)
|
99
|
- {:noreply, state}
|
100
|
- end
|
101
|
-
|
102
93
|
def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
|
103
94
|
Transaction.Reporter.ensure_purge(pid)
|
104
95
|
Transaction.Reporter.complete(pid, :async)
|
|
@@ -140,7 131,6 @@ defmodule NewRelic.Transaction.Monitor do
|
140
131
|
# http://erlang.org/doc/apps/erts/match_spec.html
|
141
132
|
trace_task_async_nolink()
|
142
133
|
trace_poolboy_checkout()
|
143
|
- trace_ecto_repo_discovery()
|
144
134
|
end
|
145
135
|
|
146
136
|
defp trace_task_async_nolink do
|
|
@@ -150,9 140,4 @@ defmodule NewRelic.Transaction.Monitor do
|
150
140
|
defp trace_poolboy_checkout do
|
151
141
|
:erlang.trace_pattern({:poolboy, :checkout, :_}, [{:_, [], [{:return_trace}]}], [])
|
152
142
|
end
|
153
|
-
|
154
|
- defp trace_ecto_repo_discovery() do
|
155
|
- Code.ensure_loaded?(Ecto.Repo.Supervisor) &&
|
156
|
- :erlang.trace_pattern({Ecto.Repo.Supervisor, :start_link, :_}, true, [:meta])
|
157
|
- end
|
158
143
|
end
|
changed
lib/new_relic/util.ex
|
@@ -57,27 57,30 @@ defmodule NewRelic.Util do
|
57
57
|
end
|
58
58
|
|
59
59
|
def coerce_attributes(attrs) do
|
60
|
- Enum.map(attrs, fn
|
60
|
Enum.flat_map(attrs, fn
|
61
|
{_key, nil} ->
|
62
|
[]
|
63
|
|
61
64
|
{key, value} when is_number(value) when is_boolean(value) ->
|
62
|
- {key, value}
|
65
|
[{key, value}]
|
63
66
|
|
64
67
|
{key, value} when is_bitstring(value) ->
|
65
68
|
case String.valid?(value) do
|
66
|
- true -> {key, value}
|
67
|
- false -> bad_value(key, value)
|
69
|
true -> [{key, value}]
|
70
|
false -> [bad_value(key, value)]
|
68
71
|
end
|
69
72
|
|
70
73
|
{key, value} when is_reference(value) when is_pid(value) when is_port(value) ->
|
71
|
- {key, inspect(value)}
|
74
|
[{key, inspect(value)}]
|
72
75
|
|
73
76
|
{key, value} when is_atom(value) ->
|
74
|
- {key, to_string(value)}
|
77
|
[{key, to_string(value)}]
|
75
78
|
|
76
79
|
{key, %struct{} = value} when struct in [Date, DateTime, Time, NaiveDateTime] ->
|
77
|
- {key, struct.to_iso8601(value)}
|
80
|
[{key, struct.to_iso8601(value)}]
|
78
81
|
|
79
82
|
{key, value} ->
|
80
|
- bad_value(key, value)
|
83
|
[bad_value(key, value)]
|
81
84
|
end)
|
82
85
|
end
|
changed
mix.exs
|
@@ -43,7 43,8 @@ defmodule NewRelic.Mixfile do
|
43
43
|
{:plug, "~> 1.0"},
|
44
44
|
{:plug_cowboy, "~> 2.0", optional: true},
|
45
45
|
# Optional Instrumentation:
|
46
|
- {:ecto_sql, "~> 3.3", optional: true}
|
46
|
{:ecto_sql, ">= 3.4.0", optional: true},
|
47
|
{:ecto, ">= 3.4.1", optional: true}
|
47
48
|
]
|
48
49
|
end
|