changed
CHANGELOG.md
|
@@ -1,5 1,14 @@
|
1
1
|
## CHANGELOG
|
2
2
|
|
3
|
### `v1.16.0`
|
4
|
|
5
|
Features
|
6
|
|
7
|
* Support the W3C Trace Context spec for Distributed Tracing. [#169](https://github.com/newrelic/elixir_agent/pull/169)
|
8
|
* Upgrade to New Relic "Protocol 17" which includes support for faster event harvest (sent every 5 seconds!). [#168](https://github.com/newrelic/elixir_agent/pull/168)
|
9
|
|
10
|
------
|
11
|
|
3
12
|
### `v1.15.0`
|
4
13
|
|
5
14
|
Features
|
changed
README.md
|
@@ -99,7 99,7 @@ defmodule MyExternalService do
|
99
99
|
@trace {:request, category: :external}
|
100
100
|
def request(method, url, headers) do
|
101
101
|
NewRelic.set_span(:http, url: url, method: method, component: "HttpClient")
|
102
|
- headers NewRelic.create_distributed_trace_payload(:http)
|
102
|
headers NewRelic.distributed_trace_headers(:http)
|
103
103
|
HttpClient.request(method, url, headers)
|
104
104
|
end
|
105
105
|
end
|
changed
VERSION
|
@@ -1 1 @@
|
1
|
- 1.15.0
|
1
|
1.16.0-rc.0
|
changed
hex_metadata.config
|
@@ -20,12 20,17 @@
|
20
20
|
<<"lib/new_relic/distributed_trace">>,
|
21
21
|
<<"lib/new_relic/distributed_trace/supervisor.ex">>,
|
22
22
|
<<"lib/new_relic/distributed_trace/backoff_sampler.ex">>,
|
23
|
<<"lib/new_relic/distributed_trace/w3c_trace_context.ex">>,
|
24
|
<<"lib/new_relic/distributed_trace/w3c_trace_context">>,
|
25
|
<<"lib/new_relic/distributed_trace/w3c_trace_context/trace_parent.ex">>,
|
26
|
<<"lib/new_relic/distributed_trace/w3c_trace_context/trace_state.ex">>,
|
23
27
|
<<"lib/new_relic/distributed_trace/context.ex">>,
|
24
28
|
<<"lib/new_relic/distributed_trace/plug.ex">>,
|
25
|
- <<"lib/new_relic/distributed_trace/tracker.ex">>,<<"lib/new_relic/util">>,
|
26
|
- <<"lib/new_relic/util/event.ex">>,<<"lib/new_relic/util/error.ex">>,
|
27
|
- <<"lib/new_relic/util/vendor.ex">>,<<"lib/new_relic/util/apdex.ex">>,
|
28
|
- <<"lib/new_relic/util/request_start.ex">>,
|
29
|
<<"lib/new_relic/distributed_trace/tracker.ex">>,
|
30
|
<<"lib/new_relic/distributed_trace/new_relic_context.ex">>,
|
31
|
<<"lib/new_relic/util">>,<<"lib/new_relic/util/event.ex">>,
|
32
|
<<"lib/new_relic/util/error.ex">>,<<"lib/new_relic/util/vendor.ex">>,
|
33
|
<<"lib/new_relic/util/apdex.ex">>,<<"lib/new_relic/util/request_start.ex">>,
|
29
34
|
<<"lib/new_relic/util/priority_queue.ex">>,<<"lib/new_relic/util/http.ex">>,
|
30
35
|
<<"lib/new_relic/util/attr_store.ex">>,<<"lib/new_relic/logger.ex">>,
|
31
36
|
<<"lib/new_relic/graceful_shutdown.ex">>,<<"lib/new_relic/aggregate">>,
|
|
@@ -110,4 115,4 @@
|
110
115
|
{<<"optional">>,true},
|
111
116
|
{<<"repository">>,<<"hexpm">>},
|
112
117
|
{<<"requirement">>,<<"~> 3.3">>}]]}.
|
113
|
- {<<"version">>,<<"1.15.0">>}.
|
118
|
{<<"version">>,<<"1.16.0-rc.0">>}.
|
changed
lib/new_relic.ex
|
@@ -108,16 108,22 @@ defmodule NewRelic do
|
108
108
|
of a Distributed Trace, but outgoing requests need an extra header:
|
109
109
|
|
110
110
|
```elixir
|
111
|
- dt_header_payload = NewRelic.create_distributed_trace_payload(:http)
|
112
|
- HTTPoison.get(url, ["x-api-key": "secret"] dt_header_payload)
|
111
|
HTTPoison.get(url, ["x-api-key": "secret"] NewRelic.distributed_trace_headers(:http))
|
113
112
|
```
|
114
113
|
|
115
114
|
**Notes:**
|
116
115
|
|
117
|
- * Call `NewRelic.create_distributed_trace_payload` immediately before making the
|
116
|
* Call `NewRelic.distributed_trace_headers` immediately before making the
|
118
117
|
request since calling the function marks the "start" time of the request.
|
119
118
|
"""
|
120
|
- defdelegate create_distributed_trace_payload(type), to: NewRelic.DistributedTrace
|
119
|
defdelegate distributed_trace_headers(type), to: NewRelic.DistributedTrace
|
120
|
|
121
|
@doc """
|
122
|
Deprecated, please use `distributed_trace_headers`
|
123
|
"""
|
124
|
defdelegate create_distributed_trace_payload(type),
|
125
|
to: NewRelic.DistributedTrace,
|
126
|
as: :distributed_trace_headers
|
121
127
|
|
122
128
|
@doc """
|
123
129
|
To get detailed information about a particular process, you can install a Process sampler.
|
changed
lib/new_relic/config.ex
|
@@ -121,9 121,21 @@ defmodule NewRelic.Config do
|
121
121
|
@doc false
|
122
122
|
def enabled?, do: (harvest_enabled?() && app_name() && license_key() && true) || false
|
123
123
|
|
124
|
def event_harvest_config() do
|
125
|
%{
|
126
|
harvest_limits: %{
|
127
|
analytic_event_data:
|
128
|
Application.get_env(:new_relic_agent, :analytic_event_per_minute, 1000),
|
129
|
custom_event_data: Application.get_env(:new_relic_agent, :custom_event_per_minute, 1000),
|
130
|
error_event_data: Application.get_env(:new_relic_agent, :error_event_per_minute, 100),
|
131
|
span_event_data: Application.get_env(:new_relic_agent, :span_event_per_minute, 1000)
|
132
|
}
|
133
|
}
|
134
|
end
|
135
|
|
124
136
|
defp harvest_enabled?,
|
125
137
|
do:
|
126
|
- System.get_env("NEW_RELIC_HARVEST_ENABLED") ||
|
138
|
System.get_env("NEW_RELIC_HARVEST_ENABLED") == "true" ||
|
127
139
|
Application.get_env(:new_relic_agent, :harvest_enabled, true)
|
128
140
|
|
129
141
|
defp parse_app_names(nil), do: nil
|
changed
lib/new_relic/distributed_trace.ex
|
@@ -1,5 1,7 @@
|
1
1
|
defmodule NewRelic.DistributedTrace do
|
2
|
- @dt_header "newrelic"
|
2
|
@nr_header "newrelic"
|
3
|
@w3c_traceparent "traceparent"
|
4
|
@w3c_tracestate "tracestate"
|
3
5
|
|
4
6
|
@moduledoc false
|
5
7
|
|
|
@@ -7,21 9,44 @@ defmodule NewRelic.DistributedTrace do
|
7
9
|
alias NewRelic.Harvest.Collector.AgentRun
|
8
10
|
alias NewRelic.Transaction
|
9
11
|
|
10
|
- def accept_distributed_trace_payload(:http, conn) do
|
11
|
- case Plug.Conn.get_req_header(conn, @dt_header) do
|
12
|
- [trace_payload | _] ->
|
13
|
- trace_payload
|
14
|
- |> Context.decode()
|
12
|
def accept_distributed_trace_headers(:http, conn) do
|
13
|
w3c_headers(conn) || newrelic_header(conn) || :no_payload
|
14
|
end
|
15
15
|
|
16
|
- [] ->
|
17
|
- :no_payload
|
16
|
defp w3c_headers(conn) do
|
17
|
case Plug.Conn.get_req_header(conn, @w3c_traceparent) do
|
18
|
[_traceparent | _] -> NewRelic.DistributedTrace.W3CTraceContext.extract(conn)
|
19
|
_ -> false
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
- def create_distributed_trace_payload(:http) do
|
23
|
defp newrelic_header(conn) do
|
24
|
case Plug.Conn.get_req_header(conn, @nr_header) do
|
25
|
[trace_payload | _] -> NewRelic.DistributedTrace.NewRelicContext.extract(trace_payload)
|
26
|
_ -> false
|
27
|
end
|
28
|
end
|
29
|
|
30
|
def distributed_trace_headers(:http) do
|
22
31
|
case get_tracing_context() do
|
23
|
- nil -> []
|
24
|
- context -> [{@dt_header, Context.encode(context, get_current_span_guid())}]
|
32
|
nil ->
|
33
|
[]
|
34
|
|
35
|
context ->
|
36
|
context = %{
|
37
|
context
|
38
|
| span_guid: get_current_span_guid(),
|
39
|
timestamp: System.system_time(:millisecond)
|
40
|
}
|
41
|
|
42
|
nr_header = NewRelic.DistributedTrace.NewRelicContext.generate(context)
|
43
|
{traceparent, tracestate} = NewRelic.DistributedTrace.W3CTraceContext.generate(context)
|
44
|
|
45
|
[
|
46
|
{@nr_header, nr_header},
|
47
|
{@w3c_traceparent, traceparent},
|
48
|
{@w3c_tracestate, tracestate}
|
49
|
]
|
25
50
|
end
|
26
51
|
end
|
27
52
|
|
|
@@ -29,6 54,7 @@ defmodule NewRelic.DistributedTrace do
|
29
54
|
{priority, sampled} = generate_sampling()
|
30
55
|
|
31
56
|
%Context{
|
57
|
source: :new,
|
32
58
|
account_id: AgentRun.account_id(),
|
33
59
|
app_id: AgentRun.primary_application_id(),
|
34
60
|
trust_key: AgentRun.trusted_account_key(),
|
|
@@ -40,18 66,54 @@ defmodule NewRelic.DistributedTrace do
|
40
66
|
def track_transaction(context, transport_type: type) do
|
41
67
|
context
|
42
68
|
|> assign_transaction_guid()
|
69
|
|> maybe_generate_sampling()
|
70
|
|> maybe_generate_trace_id()
|
43
71
|
|> report_attributes(transport_type: type)
|
72
|
|> report_attributes(:w3c)
|
44
73
|
|> convert_to_outbound()
|
45
74
|
|> set_tracing_context()
|
46
75
|
end
|
47
76
|
|
77
|
def maybe_generate_sampling(%Context{sampled: nil, priority: nil} = context) do
|
78
|
{priority, sampled} = generate_sampling()
|
79
|
%{context | sampled: sampled, priority: priority}
|
80
|
end
|
81
|
|
82
|
def maybe_generate_sampling(context), do: context
|
83
|
|
84
|
def maybe_generate_trace_id(%Context{parent_id: nil} = context) do
|
85
|
%{context | trace_id: generate_guid(16)}
|
86
|
end
|
87
|
|
88
|
def maybe_generate_trace_id(context) do
|
89
|
context
|
90
|
end
|
91
|
|
92
|
def report_attributes(
|
93
|
%{source: {:w3c, %{tracestate: :non_new_relic}}} = context,
|
94
|
transport_type: type
|
95
|
) do
|
96
|
[
|
97
|
"parent.transportType": type,
|
98
|
guid: context.trace_id,
|
99
|
traceId: context.trace_id,
|
100
|
priority: context.priority,
|
101
|
sampled: context.sampled,
|
102
|
parentId: context.parent_id,
|
103
|
parentSpanId: context.span_guid
|
104
|
]
|
105
|
|> NewRelic.add_attributes()
|
106
|
|
107
|
context
|
108
|
end
|
109
|
|
48
110
|
def report_attributes(
|
49
111
|
%Context{parent_id: nil} = context,
|
50
112
|
transport_type: _type
|
51
113
|
) do
|
52
114
|
[
|
53
115
|
guid: context.guid,
|
54
|
- traceId: context.guid,
|
116
|
traceId: context.trace_id,
|
55
117
|
priority: context.priority,
|
56
118
|
sampled: context.sampled
|
57
119
|
]
|
|
@@ -79,14 141,28 @@ defmodule NewRelic.DistributedTrace do
|
79
141
|
context
|
80
142
|
end
|
81
143
|
|
144
|
def report_attributes(%{source: {:w3c, w3c}} = context, :w3c) do
|
145
|
NewRelic.add_attributes(tracingVendors: Enum.join(w3c.tracing_vendors, ","))
|
146
|
|
147
|
w3c.tracestate == :new_relic &&
|
148
|
NewRelic.add_attributes(trustedParentId: w3c.span_id)
|
149
|
|
150
|
context
|
151
|
end
|
152
|
|
153
|
def report_attributes(context, :w3c) do
|
154
|
context
|
155
|
end
|
156
|
|
82
157
|
def convert_to_outbound(%Context{parent_id: nil} = context) do
|
83
158
|
%Context{
|
159
|
source: context.source,
|
84
160
|
account_id: AgentRun.account_id(),
|
85
161
|
app_id: AgentRun.primary_application_id(),
|
86
162
|
parent_id: nil,
|
87
163
|
trust_key: context.trust_key,
|
88
164
|
guid: context.guid,
|
89
|
- trace_id: context.guid,
|
165
|
trace_id: context.trace_id,
|
90
166
|
priority: context.priority,
|
91
167
|
sampled: context.sampled
|
92
168
|
}
|
|
@@ -94,6 170,7 @@ defmodule NewRelic.DistributedTrace do
|
94
170
|
|
95
171
|
def convert_to_outbound(%Context{} = context) do
|
96
172
|
%Context{
|
173
|
source: context.source,
|
97
174
|
account_id: AgentRun.account_id(),
|
98
175
|
app_id: AgentRun.primary_application_id(),
|
99
176
|
parent_id: context.guid,
|
|
@@ -181,13 258,16 @@ defmodule NewRelic.DistributedTrace do
|
181
258
|
NewRelic.DistributedTrace.BackoffSampler.sample?()
|
182
259
|
end
|
183
260
|
|
184
|
- defp generate_priority, do: :rand.uniform() |> Float.round(6)
|
261
|
defp generate_priority do
|
262
|
:rand.uniform() |> Float.round(6)
|
263
|
end
|
185
264
|
|
186
265
|
def assign_transaction_guid(context) do
|
187
266
|
Map.put(context, :guid, generate_guid())
|
188
267
|
end
|
189
268
|
|
190
269
|
def generate_guid(), do: :crypto.strong_rand_bytes(8) |> Base.encode16() |> String.downcase()
|
270
|
def generate_guid(16), do: :crypto.strong_rand_bytes(16) |> Base.encode16() |> String.downcase()
|
191
271
|
def generate_guid(pid: pid), do: encode_guid([pid, node()])
|
192
272
|
def generate_guid(pid: pid, label: label, ref: ref), do: encode_guid([label, ref, pid, node()])
|
193
273
|
|
|
@@ -203,7 283,7 @@ defmodule NewRelic.DistributedTrace do
|
203
283
|
|> :erlang.phash2()
|
204
284
|
|> Integer.to_charlist(16)
|
205
285
|
|> to_string()
|
206
|
- |> String.slice(0..4)
|
286
|
|> String.slice(0..3)
|
207
287
|
|> String.downcase()
|
208
288
|
end
|
changed
lib/new_relic/distributed_trace/context.ex
|
@@ -1,12 1,9 @@
|
1
1
|
defmodule NewRelic.DistributedTrace.Context do
|
2
|
- # Struct containing the DT payload context
|
3
|
-
|
4
2
|
@moduledoc false
|
5
3
|
|
6
|
- @payload_version [0, 1]
|
7
|
-
|
8
4
|
defstruct type: "App",
|
9
|
- version: @payload_version,
|
5
|
source: nil,
|
6
|
version: nil,
|
10
7
|
account_id: nil,
|
11
8
|
app_id: nil,
|
12
9
|
parent_id: nil,
|
|
@@ -15,79 12,6 @@ defmodule NewRelic.DistributedTrace.Context do
|
15
12
|
trace_id: nil,
|
16
13
|
trust_key: nil,
|
17
14
|
priority: nil,
|
18
|
- sampled: false,
|
15
|
sampled: nil,
|
19
16
|
timestamp: nil
|
20
|
-
|
21
|
- def decode(raw_payload) when is_binary(raw_payload) do
|
22
|
- with {:ok, json} <- Base.decode64(raw_payload),
|
23
|
- {:ok, map} <- Jason.decode(json),
|
24
|
- context <- validate(map) do
|
25
|
- NewRelic.report_metric(:supportability, [:dt, :accept, :success])
|
26
|
- context
|
27
|
- else
|
28
|
- error ->
|
29
|
- NewRelic.report_metric(:supportability, [:dt, :accept, :parse_error])
|
30
|
- NewRelic.log(:debug, "Bad DT Payload: #{inspect(error)} #{inspect(raw_payload)}")
|
31
|
- nil
|
32
|
- end
|
33
|
- end
|
34
|
-
|
35
|
- def validate(%{
|
36
|
- "v" => @payload_version,
|
37
|
- "d" =>
|
38
|
- %{
|
39
|
- "ty" => type,
|
40
|
- "ac" => account_id,
|
41
|
- "ap" => app_id,
|
42
|
- "tr" => trace_id,
|
43
|
- "ti" => timestamp
|
44
|
- } = data
|
45
|
- }) do
|
46
|
- %__MODULE__{
|
47
|
- version: @payload_version,
|
48
|
- type: type,
|
49
|
- account_id: account_id,
|
50
|
- app_id: app_id,
|
51
|
- parent_id: data["tx"],
|
52
|
- span_guid: data["id"],
|
53
|
- trace_id: trace_id,
|
54
|
- trust_key: data["tk"],
|
55
|
- priority: data["pr"],
|
56
|
- sampled: data["sa"] || false,
|
57
|
- timestamp: timestamp
|
58
|
- }
|
59
|
- end
|
60
|
-
|
61
|
- def validate(_invalid), do: :invalid
|
62
|
-
|
63
|
- def encode(context, current_span_guid) do
|
64
|
- %{
|
65
|
- "v" => @payload_version,
|
66
|
- "d" =>
|
67
|
- %{
|
68
|
- "ty" => context.type,
|
69
|
- "ac" => context.account_id,
|
70
|
- "ap" => context.app_id,
|
71
|
- "tx" => context.guid,
|
72
|
- "tr" => context.trace_id,
|
73
|
- "pr" => context.priority,
|
74
|
- "sa" => context.sampled,
|
75
|
- "ti" => System.system_time(:millisecond)
|
76
|
- }
|
77
|
- |> maybe_put(:span_guid, "id", context.sampled, current_span_guid)
|
78
|
- |> maybe_put(:trust_key, "tk", context.account_id, context.trust_key)
|
79
|
- }
|
80
|
- |> Jason.encode!()
|
81
|
- |> Base.encode64()
|
82
|
- end
|
83
|
-
|
84
|
- def maybe_put(data, :span_guid, key, true = _sampled, guid), do: Map.put(data, key, guid)
|
85
|
- def maybe_put(data, :span_guid, _key, false = _sampled, _guid), do: data
|
86
|
-
|
87
|
- def maybe_put(data, :trust_key, _key, account_id, account_id), do: data
|
88
|
- def maybe_put(data, :trust_key, _key, _account_id, nil), do: data
|
89
|
- def maybe_put(data, :trust_key, key, _account_id, trust_key), do: Map.put(data, key, trust_key)
|
90
|
-
|
91
|
- def parse_int(nil), do: :error
|
92
|
- def parse_int(field), do: Integer.parse(field)
|
93
17
|
end
|
added
lib/new_relic/distributed_trace/new_relic_context.ex
|
@@ -0,0 1,96 @@
|
1
|
defmodule NewRelic.DistributedTrace.NewRelicContext do
|
2
|
alias NewRelic.DistributedTrace.Context
|
3
|
alias NewRelic.Harvest.Collector.AgentRun
|
4
|
|
5
|
def extract(trace_payload) do
|
6
|
decode(trace_payload)
|
7
|
|> restrict_access
|
8
|
end
|
9
|
|
10
|
def generate(context) do
|
11
|
encode(context)
|
12
|
end
|
13
|
|
14
|
def decode(raw_payload) when is_binary(raw_payload) do
|
15
|
with {:ok, json} <- Base.decode64(raw_payload),
|
16
|
{:ok, map} <- Jason.decode(json),
|
17
|
context <- validate(map) do
|
18
|
NewRelic.report_metric(:supportability, [:dt, :accept, :success])
|
19
|
context
|
20
|
else
|
21
|
error ->
|
22
|
NewRelic.report_metric(:supportability, [:dt, :accept, :parse_error])
|
23
|
NewRelic.log(:debug, "Bad DT Payload: #{inspect(error)} #{inspect(raw_payload)}")
|
24
|
nil
|
25
|
end
|
26
|
end
|
27
|
|
28
|
def restrict_access(context) do
|
29
|
if (context.trust_key || context.account_id) == AgentRun.trusted_account_key() do
|
30
|
context
|
31
|
else
|
32
|
:restricted
|
33
|
end
|
34
|
end
|
35
|
|
36
|
@payload_version [0, 1]
|
37
|
def validate(%{
|
38
|
"v" => @payload_version,
|
39
|
"d" =>
|
40
|
%{
|
41
|
"ty" => type,
|
42
|
"ac" => account_id,
|
43
|
"ap" => app_id,
|
44
|
"tr" => trace_id,
|
45
|
"ti" => timestamp
|
46
|
} = data
|
47
|
}) do
|
48
|
%Context{
|
49
|
source: :new_relic,
|
50
|
version: @payload_version,
|
51
|
type: type,
|
52
|
account_id: account_id,
|
53
|
app_id: app_id,
|
54
|
parent_id: data["tx"],
|
55
|
span_guid: data["id"],
|
56
|
trace_id: trace_id,
|
57
|
trust_key: data["tk"],
|
58
|
priority: data["pr"],
|
59
|
sampled: data["sa"] || false,
|
60
|
timestamp: timestamp
|
61
|
}
|
62
|
end
|
63
|
|
64
|
def validate(_invalid), do: :invalid
|
65
|
|
66
|
def encode(context) do
|
67
|
%{
|
68
|
"v" => @payload_version,
|
69
|
"d" =>
|
70
|
%{
|
71
|
"ty" => context.type,
|
72
|
"ac" => context.account_id,
|
73
|
"ap" => context.app_id,
|
74
|
"tx" => context.guid,
|
75
|
"tr" => context.trace_id,
|
76
|
"pr" => context.priority,
|
77
|
"sa" => context.sampled,
|
78
|
"ti" => context.timestamp
|
79
|
}
|
80
|
|> maybe_put(:span_guid, "id", context.sampled, context.span_guid)
|
81
|
|> maybe_put(:trust_key, "tk", context.account_id, context.trust_key)
|
82
|
}
|
83
|
|> Jason.encode!()
|
84
|
|> Base.encode64()
|
85
|
end
|
86
|
|
87
|
def maybe_put(data, :span_guid, key, true = _sampled, guid), do: Map.put(data, key, guid)
|
88
|
def maybe_put(data, :span_guid, _key, false = _sampled, _guid), do: data
|
89
|
|
90
|
def maybe_put(data, :trust_key, _key, account_id, account_id), do: data
|
91
|
def maybe_put(data, :trust_key, _key, _account_id, nil), do: data
|
92
|
def maybe_put(data, :trust_key, key, _account_id, trust_key), do: Map.put(data, key, trust_key)
|
93
|
|
94
|
def parse_int(nil), do: :error
|
95
|
def parse_int(field), do: Integer.parse(field)
|
96
|
end
|
changed
lib/new_relic/distributed_trace/plug.ex
|
@@ -9,7 9,6 @@ defmodule NewRelic.DistributedTrace.Plug do
|
9
9
|
|
10
10
|
alias NewRelic.DistributedTrace
|
11
11
|
alias NewRelic.DistributedTrace.Context
|
12
|
- alias NewRelic.Harvest.Collector.AgentRun
|
13
12
|
|
14
13
|
@impl Plug
|
15
14
|
def init(opts), do: opts
|
|
@@ -42,19 41,9 @@ defmodule NewRelic.DistributedTrace.Plug do
|
42
41
|
end
|
43
42
|
|
44
43
|
defp determine_context(conn) do
|
45
|
- with %Context{} = context <- DistributedTrace.accept_distributed_trace_payload(:http, conn),
|
46
|
- %Context{} = context <- restrict_access(context) do
|
47
|
- context
|
48
|
- else
|
44
|
case DistributedTrace.accept_distributed_trace_headers(:http, conn) do
|
45
|
%Context{} = context -> context
|
49
46
|
_ -> DistributedTrace.generate_new_context()
|
50
47
|
end
|
51
48
|
end
|
52
|
-
|
53
|
- def restrict_access(context) do
|
54
|
- if (context.trust_key || context.account_id) == AgentRun.trusted_account_key() do
|
55
|
- context
|
56
|
- else
|
57
|
- :restricted
|
58
|
- end
|
59
|
- end
|
60
49
|
end
|
added
lib/new_relic/distributed_trace/w3c_trace_context.ex
|
@@ -0,0 1,105 @@
|
1
|
defmodule NewRelic.DistributedTrace.W3CTraceContext do
|
2
|
@moduledoc false
|
3
|
|
4
|
alias NewRelic.Harvest.Collector.AgentRun
|
5
|
alias NewRelic.DistributedTrace.Context
|
6
|
alias __MODULE__.{TraceParent, TraceState}
|
7
|
|
8
|
@w3c_traceparent "traceparent"
|
9
|
@w3c_tracestate "tracestate"
|
10
|
|
11
|
def extract(conn) do
|
12
|
with traceparent_header <- Plug.Conn.get_req_header(conn, @w3c_traceparent),
|
13
|
tracestate_header <- Plug.Conn.get_req_header(conn, @w3c_tracestate),
|
14
|
%TraceParent{} = traceparent <- TraceParent.decode(traceparent_header),
|
15
|
%TraceState{} = tracestate <- TraceState.decode(tracestate_header) do
|
16
|
case TraceState.restrict_access(tracestate) do
|
17
|
{newrelic, others} ->
|
18
|
NewRelic.report_metric(:supportability, [:trace_context, :accept, :success])
|
19
|
|
20
|
%Context{
|
21
|
source:
|
22
|
{:w3c,
|
23
|
%{
|
24
|
tracestate: :new_relic,
|
25
|
sampled: traceparent.flags.sampled,
|
26
|
others: others,
|
27
|
span_id: newrelic.span_id,
|
28
|
tracing_vendors: Enum.map(others, & &1.key)
|
29
|
}},
|
30
|
type: newrelic.parent_type,
|
31
|
account_id: newrelic.account_id,
|
32
|
app_id: newrelic.app_id,
|
33
|
parent_id: newrelic.transaction_id,
|
34
|
span_guid: traceparent.parent_id,
|
35
|
trace_id: traceparent.trace_id,
|
36
|
trust_key: newrelic.trusted_account_key,
|
37
|
priority: newrelic.priority,
|
38
|
sampled: newrelic.sampled,
|
39
|
timestamp: newrelic.timestamp
|
40
|
}
|
41
|
|
42
|
others ->
|
43
|
NewRelic.report_metric(:supportability, [:trace_context, :accept, :success])
|
44
|
NewRelic.report_metric(:supportability, [:trace_context, :tracestate, :non_new_relic])
|
45
|
|
46
|
%Context{
|
47
|
source:
|
48
|
{:w3c,
|
49
|
%{
|
50
|
tracestate: :non_new_relic,
|
51
|
sampled: traceparent.flags.sampled,
|
52
|
others: others,
|
53
|
tracing_vendors: Enum.map(others, & &1.key)
|
54
|
}},
|
55
|
parent_id: traceparent.parent_id,
|
56
|
span_guid: traceparent.parent_id,
|
57
|
trace_id: traceparent.trace_id,
|
58
|
trust_key: AgentRun.trusted_account_key()
|
59
|
}
|
60
|
end
|
61
|
else
|
62
|
_ ->
|
63
|
NewRelic.report_metric(:supportability, [:trace_context, :accept, :exception])
|
64
|
false
|
65
|
end
|
66
|
end
|
67
|
|
68
|
def generate(%{source: source} = context) do
|
69
|
{others, traceparent_sampled} =
|
70
|
case source do
|
71
|
{:w3c, %{others: others, sampled: sampled}} -> {others, sampled}
|
72
|
_ -> {[], context.sampled}
|
73
|
end
|
74
|
|
75
|
traceparent =
|
76
|
TraceParent.encode(%TraceParent{
|
77
|
trace_id: context.trace_id,
|
78
|
parent_id: context.span_guid,
|
79
|
flags: %{sampled: traceparent_sampled}
|
80
|
})
|
81
|
|
82
|
tracestate =
|
83
|
TraceState.encode(%TraceState{
|
84
|
members: [
|
85
|
%{
|
86
|
key: :new_relic,
|
87
|
value: %TraceState.NewRelicState{
|
88
|
trusted_account_key: context.trust_key,
|
89
|
parent_type: context.type,
|
90
|
account_id: context.account_id,
|
91
|
app_id: context.app_id,
|
92
|
span_id: context.span_guid,
|
93
|
transaction_id: context.parent_id,
|
94
|
sampled: context.sampled,
|
95
|
priority: context.priority,
|
96
|
timestamp: context.timestamp
|
97
|
}
|
98
|
}
|
99
|
| others
|
100
|
]
|
101
|
})
|
102
|
|
103
|
{traceparent, tracestate}
|
104
|
end
|
105
|
end
|
added
lib/new_relic/distributed_trace/w3c_trace_context/trace_parent.ex
|
@@ -0,0 1,96 @@
|
1
|
defmodule NewRelic.DistributedTrace.W3CTraceContext.TraceParent do
|
2
|
@moduledoc false
|
3
|
|
4
|
# https://w3c.github.io/trace-context/#traceparent-header
|
5
|
|
6
|
defstruct version: "00",
|
7
|
trace_id: nil,
|
8
|
parent_id: nil,
|
9
|
flags: nil
|
10
|
|
11
|
@version 2
|
12
|
@trace_id 32
|
13
|
@parent_id 16
|
14
|
@flags 2
|
15
|
|
16
|
def decode([header]), do: decode(header)
|
17
|
|
18
|
def decode(<<"ff", "-", _::binary>>),
|
19
|
do: invalid()
|
20
|
|
21
|
def decode(<<_::binary-size(@version), "-", "00000000000000000000000000000000", _::binary>>),
|
22
|
do: invalid()
|
23
|
|
24
|
def decode(
|
25
|
<<_::binary-size(@version), "-", _::binary-size(@trace_id), "-", "0000000000000000",
|
26
|
_::binary>>
|
27
|
),
|
28
|
do: invalid()
|
29
|
|
30
|
def decode(
|
31
|
<<version::binary-size(@version), "-", trace_id::binary-size(@trace_id), "-",
|
32
|
parent_id::binary-size(@parent_id), "-", flags::binary-size(@flags)>>
|
33
|
) do
|
34
|
validate(
|
35
|
[version, trace_id, parent_id, flags],
|
36
|
%__MODULE__{
|
37
|
version: version,
|
38
|
trace_id: trace_id,
|
39
|
parent_id: parent_id,
|
40
|
flags: %{sampled: flags == "01"}
|
41
|
}
|
42
|
)
|
43
|
end
|
44
|
|
45
|
# Future versions can be longer
|
46
|
def decode(
|
47
|
<<version::binary-size(@version), "-", trace_id::binary-size(@trace_id), "-",
|
48
|
parent_id::binary-size(@parent_id), "-", flags::binary-size(@flags), "-", _::binary>>
|
49
|
)
|
50
|
when version != "00" do
|
51
|
validate(
|
52
|
[version, trace_id, parent_id, flags],
|
53
|
%__MODULE__{
|
54
|
version: version,
|
55
|
trace_id: trace_id,
|
56
|
parent_id: parent_id,
|
57
|
flags: %{sampled: flags == "01"}
|
58
|
}
|
59
|
)
|
60
|
end
|
61
|
|
62
|
def decode(_),
|
63
|
do: invalid()
|
64
|
|
65
|
def encode(%__MODULE__{
|
66
|
version: _version,
|
67
|
trace_id: trace_id,
|
68
|
parent_id: parent_id,
|
69
|
flags: %{
|
70
|
sampled: sampled
|
71
|
}
|
72
|
}) do
|
73
|
[
|
74
|
"00",
|
75
|
String.pad_leading(trace_id, @trace_id, "0") |> String.downcase(),
|
76
|
String.pad_leading(parent_id, @parent_id, "0") |> String.downcase(),
|
77
|
(sampled && "01") || "00"
|
78
|
]
|
79
|
|> Enum.join("-")
|
80
|
end
|
81
|
|
82
|
defp invalid() do
|
83
|
NewRelic.report_metric(:supportability, [:trace_context, :traceparent, :invalid])
|
84
|
:invalid
|
85
|
end
|
86
|
|
87
|
defp validate(values, context) do
|
88
|
case Enum.all?(values, &valid?/1) do
|
89
|
true -> context
|
90
|
false -> :invalid
|
91
|
end
|
92
|
end
|
93
|
|
94
|
defp valid?(value),
|
95
|
do: Base.decode16(value, case: :mixed) != :error
|
96
|
end
|
added
lib/new_relic/distributed_trace/w3c_trace_context/trace_state.ex
|
@@ -0,0 1,178 @@
|
1
|
defmodule NewRelic.DistributedTrace.W3CTraceContext.TraceState do
|
2
|
@moduledoc false
|
3
|
|
4
|
# https://w3c.github.io/trace-context/#tracestate-header
|
5
|
|
6
|
alias NewRelic.Harvest.Collector.AgentRun
|
7
|
|
8
|
defstruct [:members]
|
9
|
|
10
|
defmodule NewRelicState do
|
11
|
defstruct version: "0",
|
12
|
trusted_account_key: nil,
|
13
|
parent_type: nil,
|
14
|
account_id: nil,
|
15
|
app_id: nil,
|
16
|
span_id: nil,
|
17
|
transaction_id: nil,
|
18
|
sampled: nil,
|
19
|
priority: nil,
|
20
|
timestamp: nil
|
21
|
end
|
22
|
|
23
|
def encode(%__MODULE__{members: members}) do
|
24
|
members
|
25
|
|> Enum.take(32)
|
26
|
|> Enum.map(&encode/1)
|
27
|
|> Enum.join(",")
|
28
|
end
|
29
|
|
30
|
def encode(%{key: :new_relic, value: value}) do
|
31
|
encoded_value =
|
32
|
[
|
33
|
value.version,
|
34
|
value.parent_type |> encode_type(),
|
35
|
value.account_id,
|
36
|
value.app_id,
|
37
|
value.span_id,
|
38
|
value.transaction_id,
|
39
|
value.sampled |> encode_sampled(),
|
40
|
value.priority,
|
41
|
value.timestamp
|
42
|
]
|
43
|
|> Enum.join("-")
|
44
|
|
45
|
"#{value.trusted_account_key}@nr=#{encoded_value}"
|
46
|
end
|
47
|
|
48
|
def encode(%{key: key, value: value}) do
|
49
|
"#{key}=#{value}"
|
50
|
end
|
51
|
|
52
|
def decode([]), do: %__MODULE__{members: []}
|
53
|
|
54
|
def decode([header]) when is_binary(header) do
|
55
|
members =
|
56
|
header
|
57
|
|> String.split(",")
|
58
|
|> Enum.map(&String.trim/1)
|
59
|
|> Enum.map(&String.split(&1, "="))
|
60
|
|> Enum.reject(&(&1 == [""]))
|
61
|
|
62
|
%__MODULE__{members: validate(members)}
|
63
|
end
|
64
|
|
65
|
def restrict_access(%__MODULE__{members: members}) do
|
66
|
members
|
67
|
|> Enum.split_with(&matching_new_relic_state?/1)
|
68
|
|> case do
|
69
|
{[%{value: new_relic}], others} -> {new_relic, others}
|
70
|
{_, others} -> others
|
71
|
end
|
72
|
end
|
73
|
|
74
|
def matching_new_relic_state?(context) do
|
75
|
context.key == :new_relic &&
|
76
|
context.value.trusted_account_key == AgentRun.trusted_account_key()
|
77
|
end
|
78
|
|
79
|
defp validate(members) do
|
80
|
case valid_members?(members) do
|
81
|
true -> Enum.flat_map(members, &decode_member/1)
|
82
|
false -> []
|
83
|
end
|
84
|
end
|
85
|
|
86
|
defp valid_members?(members) do
|
87
|
Enum.all?(members, &valid_member?/1) &&
|
88
|
no_duplicate_keys(members) &&
|
89
|
length(members) <= 32
|
90
|
end
|
91
|
|
92
|
@key_wo_vendor ~r/^[0-9a-z][_0-9a-z\-\*\/]{0,255}$/
|
93
|
@key_with_vendor ~r/^[0-9a-z][_0-9a-z\-\*\/]{0,240}@[0-9a-z][_0-9a-z\-\*\/]{0,13}$/
|
94
|
@value ~r/^([\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e])$/
|
95
|
defp valid_member?([key, value]) do
|
96
|
valid_key? = Regex.match?(@key_wo_vendor, key) || Regex.match?(@key_with_vendor, key)
|
97
|
valid_value? = Regex.match?(@value, value)
|
98
|
|
99
|
valid_key? && valid_value?
|
100
|
end
|
101
|
|
102
|
defp valid_member?(_) do
|
103
|
false
|
104
|
end
|
105
|
|
106
|
defp no_duplicate_keys(members) do
|
107
|
keys = Enum.map(members, &List.first/1)
|
108
|
duplicates? = length(keys) != length(Enum.uniq(keys))
|
109
|
|
110
|
!duplicates?
|
111
|
end
|
112
|
|
113
|
defp decode_member([key, value]) do
|
114
|
decode_member(vendor_type(key), key, value)
|
115
|
end
|
116
|
|
117
|
defp decode_member(:new_relic, key, value) do
|
118
|
[
|
119
|
version,
|
120
|
parent_type,
|
121
|
account_id,
|
122
|
app_id,
|
123
|
span_id,
|
124
|
transaction_id,
|
125
|
sampled,
|
126
|
priority,
|
127
|
timestamp
|
128
|
] = String.split(value, "-")
|
129
|
|
130
|
[trusted_account_key, _] = String.split(key, "@")
|
131
|
|
132
|
[
|
133
|
%{
|
134
|
key: :new_relic,
|
135
|
value: %__MODULE__.NewRelicState{
|
136
|
trusted_account_key: trusted_account_key,
|
137
|
version: version |> String.to_integer(),
|
138
|
parent_type: parent_type |> decode_type(),
|
139
|
account_id: account_id,
|
140
|
app_id: app_id,
|
141
|
span_id: span_id,
|
142
|
transaction_id: transaction_id,
|
143
|
sampled: sampled |> decode_sampled(),
|
144
|
priority: priority |> decode_priority(),
|
145
|
timestamp: timestamp |> String.to_integer()
|
146
|
}
|
147
|
}
|
148
|
]
|
149
|
end
|
150
|
|
151
|
defp decode_member(:other, key, value) do
|
152
|
[
|
153
|
%{key: key, value: value}
|
154
|
]
|
155
|
end
|
156
|
|
157
|
defp vendor_type(key) do
|
158
|
if String.contains?(key, "@nr"), do: :new_relic, else: :other
|
159
|
end
|
160
|
|
161
|
defp decode_type("0"), do: "App"
|
162
|
defp decode_type("1"), do: "Browser"
|
163
|
defp decode_type("2"), do: "Mobile"
|
164
|
|
165
|
defp encode_type("App"), do: "0"
|
166
|
defp encode_type("Browser"), do: "1"
|
167
|
defp encode_type("Mobile"), do: "2"
|
168
|
|
169
|
defp decode_priority(""), do: nil
|
170
|
defp decode_priority(priority), do: String.to_float(priority)
|
171
|
|
172
|
defp decode_sampled("1"), do: true
|
173
|
defp decode_sampled("0"), do: false
|
174
|
defp decode_sampled(""), do: nil
|
175
|
|
176
|
defp encode_sampled(true), do: "1"
|
177
|
defp encode_sampled(false), do: "0"
|
178
|
end
|
changed
lib/new_relic/harvest/collector/agent_run.ex
|
@@ -69,24 69,50 @@ defmodule NewRelic.Harvest.Collector.AgentRun do
|
69
69
|
store(:account_id, connect_response["account_id"])
|
70
70
|
store(:primary_application_id, connect_response["primary_application_id"])
|
71
71
|
|
72
|
store(:request_headers, connect_response["request_headers_map"] |> Map.to_list())
|
73
|
|
72
74
|
store(:sampling_target, connect_response["sampling_target"])
|
73
75
|
store(:sampling_target_period, connect_response["sampling_target_period_in_seconds"] * 1000)
|
74
76
|
|
75
|
- transaction_event = connect_response["data_methods"]["analytic_event_data"]
|
76
|
- store(:transaction_event_reservoir_size, transaction_event["max_samples_stored"])
|
77
|
- store(:transaction_event_harvest_cycle, transaction_event["report_period_in_seconds"] * 1000)
|
77
|
connect_response["data_methods"]
|
78
|
event_harvest = connect_response["event_harvest_config"]
|
79
|
harvest_limits = event_harvest["harvest_limits"]
|
78
80
|
|
79
|
- custom_event = connect_response["data_methods"]["custom_event_data"]
|
80
|
- store(:custom_event_reservoir_size, custom_event["max_samples_stored"])
|
81
|
- store(:custom_event_harvest_cycle, custom_event["report_period_in_seconds"] * 1000)
|
81
|
if harvest_limits["analytic_event_data"] do
|
82
|
store(:transaction_event_reservoir_size, harvest_limits["analytic_event_data"])
|
83
|
store(:transaction_event_harvest_cycle, event_harvest["report_period_ms"])
|
84
|
else
|
85
|
analytic_event = connect_response["data_methods"]["analytic_event_data"]
|
86
|
store(:transaction_event_reservoir_size, analytic_event["max_samples_stored"])
|
87
|
store(:transaction_event_harvest_cycle, analytic_event["report_period_in_seconds"] * 1000)
|
88
|
end
|
82
89
|
|
83
|
- error_event = connect_response["data_methods"]["error_event_data"]
|
84
|
- store(:error_event_reservoir_size, error_event["max_samples_stored"])
|
85
|
- store(:error_event_harvest_cycle, error_event["report_period_in_seconds"] * 1000)
|
90
|
if harvest_limits["custom_event_data"] do
|
91
|
store(:custom_event_reservoir_size, harvest_limits["custom_event_data"])
|
92
|
store(:custom_event_harvest_cycle, event_harvest["report_period_ms"])
|
93
|
else
|
94
|
custom_event = connect_response["data_methods"]["custom_event_data"]
|
95
|
store(:custom_event_reservoir_size, custom_event["max_samples_stored"])
|
96
|
store(:custom_event_harvest_cycle, custom_event["report_period_in_seconds"] * 1000)
|
97
|
end
|
86
98
|
|
87
|
- span_event = connect_response["data_methods"]["span_event_data"]
|
88
|
- store(:span_event_reservoir_size, span_event["max_samples_stored"])
|
89
|
- store(:span_event_harvest_cycle, span_event["report_period_in_seconds"] * 1000)
|
99
|
if harvest_limits["error_event_data"] do
|
100
|
store(:error_event_reservoir_size, harvest_limits["error_event_data"])
|
101
|
store(:error_event_harvest_cycle, event_harvest["report_period_ms"])
|
102
|
else
|
103
|
error_event = connect_response["data_methods"]["error_event_data"]
|
104
|
store(:error_event_reservoir_size, error_event["max_samples_stored"])
|
105
|
store(:error_event_harvest_cycle, error_event["report_period_in_seconds"] * 1000)
|
106
|
end
|
107
|
|
108
|
if harvest_limits["span_event_data"] do
|
109
|
store(:span_event_reservoir_size, harvest_limits["span_event_data"])
|
110
|
store(:span_event_harvest_cycle, event_harvest["report_period_ms"])
|
111
|
else
|
112
|
span_event = connect_response["data_methods"]["span_event_data"]
|
113
|
store(:span_event_reservoir_size, span_event["max_samples_stored"])
|
114
|
store(:span_event_harvest_cycle, span_event["report_period_in_seconds"] * 1000)
|
115
|
end
|
90
116
|
|
91
117
|
store(:data_report_period, connect_response["data_report_period"] * 1000)
|
92
118
|
|
|
@@ -103,11 129,11 @@ defmodule NewRelic.Harvest.Collector.AgentRun do
|
103
129
|
:ets.insert(__MODULE__, {key, value})
|
104
130
|
end
|
105
131
|
|
106
|
- def lookup(key) do
|
132
|
def lookup(key, default \\ nil) do
|
107
133
|
Application.get_env(:new_relic_agent, key) ||
|
108
134
|
case :ets.lookup(__MODULE__, key) do
|
109
135
|
[{^key, value}] -> value
|
110
|
- [] -> nil
|
136
|
[] -> default
|
111
137
|
end
|
112
138
|
end
|
113
139
|
end
|
changed
lib/new_relic/harvest/collector/connect.ex
|
@@ -13,6 13,7 @@ defmodule NewRelic.Harvest.Collector.Connect do
|
13
13
|
%{label_type: key, label_value: value}
|
14
14
|
end),
|
15
15
|
utilization: NewRelic.Util.utilization(),
|
16
|
event_harvest_config: NewRelic.Config.event_harvest_config(),
|
16
17
|
metadata: NewRelic.Util.metadata(),
|
17
18
|
environment: NewRelic.Util.elixir_environment(),
|
18
19
|
agent_version: NewRelic.Config.agent_version()
|
changed
lib/new_relic/harvest/collector/custom_event/harvester.ex
|
@@ -17,7 17,7 @@ defmodule NewRelic.Harvest.Collector.CustomEvent.Harvester do
|
17
17
|
start_time_mono: System.monotonic_time(),
|
18
18
|
end_time_mono: nil,
|
19
19
|
sampling: %{
|
20
|
- reservoir_size: Collector.AgentRun.lookup(:custom_event_reservoir_size),
|
20
|
reservoir_size: Collector.AgentRun.lookup(:custom_event_reservoir_size, 100),
|
21
21
|
events_seen: 0
|
22
22
|
},
|
23
23
|
custom_events: []
|
|
@@ -90,11 90,12 @@ defmodule NewRelic.Harvest.Collector.CustomEvent.Harvester do
|
90
90
|
def send_harvest(state) do
|
91
91
|
events = build_payload(state)
|
92
92
|
Collector.Protocol.custom_event([Collector.AgentRun.agent_run_id(), state.sampling, events])
|
93
|
- log_harvest(length(events))
|
93
|
log_harvest(length(events), state.sampling.reservoir_size)
|
94
94
|
end
|
95
95
|
|
96
|
- def log_harvest(harvest_size) do
|
97
|
- NewRelic.report_metric({:supportability, CustomEvent}, harvest_size: harvest_size)
|
96
|
def log_harvest(harvest_size, reservoir_size) do
|
97
|
NewRelic.report_metric({:supportability, "CustomEventData"}, harvest_size: harvest_size)
|
98
|
NewRelic.report_metric({:supportability, "CustomEventData"}, reservoir_size: reservoir_size)
|
98
99
|
NewRelic.log(:debug, "Completed Custom Event harvest - size: #{harvest_size}")
|
99
100
|
end
|
changed
lib/new_relic/harvest/collector/error_trace/harvester.ex
|
@@ -76,7 76,7 @@ defmodule NewRelic.Harvest.Collector.ErrorTrace.Harvester do
|
76
76
|
end
|
77
77
|
|
78
78
|
def log_harvest(harvest_size) do
|
79
|
- NewRelic.report_metric({:supportability, ErrorTrace}, harvest_size: harvest_size)
|
79
|
NewRelic.report_metric({:supportability, "ErrorData"}, harvest_size: harvest_size)
|
80
80
|
NewRelic.log(:debug, "Completed Error Trace harvest - size: #{harvest_size}")
|
81
81
|
end
|
changed
lib/new_relic/harvest/collector/metric/harvester.ex
|
@@ -74,7 74,7 @@ defmodule NewRelic.Harvest.Collector.Metric.Harvester do
|
74
74
|
end
|
75
75
|
|
76
76
|
def log_harvest(harvest_size) do
|
77
|
- NewRelic.report_metric({:supportability, Metric}, harvest_size: harvest_size)
|
77
|
NewRelic.report_metric({:supportability, "MetricData"}, harvest_size: harvest_size)
|
78
78
|
NewRelic.log(:debug, "Completed Metric harvest - size: #{harvest_size}")
|
79
79
|
end
|
changed
lib/new_relic/harvest/collector/metric_data.ex
|
@@ -245,11 245,21 @@ defmodule NewRelic.Harvest.Collector.MetricData do
|
245
245
|
}
|
246
246
|
]
|
247
247
|
|
248
|
def transform({:supportability, harvester}, reservoir_size: reservoir_size),
|
249
|
do: [
|
250
|
%Metric{
|
251
|
name: join(["Supportability/EventHarvest", harvester, "HarvestLimit"]),
|
252
|
call_count: 1,
|
253
|
total_call_time: reservoir_size
|
254
|
}
|
255
|
]
|
256
|
|
248
257
|
def transform({:supportability, harvester}, harvest_size: harvest_size),
|
249
258
|
do: [
|
250
259
|
%Metric{
|
251
|
- name: join(["Supportability/Elixir/Collector/HarvestSize", inspect(harvester)]),
|
252
|
- call_count: harvest_size
|
260
|
name: join(["Supportability/Elixir/Collector/HarvestSize", harvester]),
|
261
|
call_count: 1,
|
262
|
total_call_time: harvest_size
|
253
263
|
},
|
254
264
|
%Metric{
|
255
265
|
name: :"Supportability/Elixir/Harvest",
|
|
@@ -261,12 271,29 @@ defmodule NewRelic.Harvest.Collector.MetricData do
|
261
271
|
}
|
262
272
|
]
|
263
273
|
|
274
|
def transform(:supportability, [:trace_context, :accept, :success]),
|
275
|
do: [
|
276
|
%Metric{name: :"Supportability/TraceContext/Accept/Success", call_count: 1}
|
277
|
]
|
278
|
|
279
|
def transform(:supportability, [:trace_context, :accept, :exception]),
|
280
|
do: [
|
281
|
%Metric{name: :"Supportability/TraceContext/Accept/Exception", call_count: 1}
|
282
|
]
|
283
|
|
284
|
def transform(:supportability, [:trace_context, :tracestate, :non_new_relic]),
|
285
|
do: [
|
286
|
%Metric{name: :"Supportability/TraceContext/TraceState/NoNrEntry", call_count: 1}
|
287
|
]
|
288
|
|
289
|
def transform(:supportability, [:trace_context, :traceparent, :invalid]),
|
290
|
do: [
|
291
|
%Metric{name: :"Supportability/TraceContext/TraceParent/Parse/Exception", call_count: 1}
|
292
|
]
|
293
|
|
264
294
|
def transform(:supportability, [:dt, :accept, :success]),
|
265
295
|
do: [
|
266
|
- %Metric{
|
267
|
- name: :"Supportability/DistributedTrace/AcceptPayload/Success",
|
268
|
- call_count: 1
|
269
|
- }
|
296
|
%Metric{name: :"Supportability/DistributedTrace/AcceptPayload/Success", call_count: 1}
|
270
297
|
]
|
271
298
|
|
272
299
|
def transform(:supportability, [:dt, :accept, :parse_error]),
|
changed
lib/new_relic/harvest/collector/protocol.ex
|
@@ -3,7 3,7 @@ defmodule NewRelic.Harvest.Collector.Protocol do
|
3
3
|
|
4
4
|
@moduledoc false
|
5
5
|
|
6
|
- @protocol_version 16
|
6
|
@protocol_version 17
|
7
7
|
|
8
8
|
def preconnect,
|
9
9
|
do: call_remote(%{method: "preconnect"}, [])
|
|
@@ -76,7 76,8 @@ defmodule NewRelic.Harvest.Collector.Protocol do
|
76
76
|
|> URI.to_string()
|
77
77
|
end
|
78
78
|
|
79
|
- defp parse_http_response({:ok, %{status_code: 200, body: body}}, _params) do
|
79
|
defp parse_http_response({:ok, %{status_code: status_code, body: body}}, _params)
|
80
|
when status_code in [200, 410] do
|
80
81
|
case Jason.decode(body) do
|
81
82
|
{:ok, response} ->
|
82
83
|
{:ok, response}
|
|
@@ -87,6 88,10 @@ defmodule NewRelic.Harvest.Collector.Protocol do
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
91
|
defp parse_http_response({:ok, %{status_code: 202}}, _params) do
|
92
|
{:ok, :accepted}
|
93
|
end
|
94
|
|
90
95
|
defp parse_http_response({:ok, %{status_code: status, body: body}}, params) do
|
91
96
|
NewRelic.log(:error, "#{params[:method]}: (#{status}) #{body}")
|
92
97
|
{:error, status}
|
|
@@ -114,6 119,10 @@ defmodule NewRelic.Harvest.Collector.Protocol do
|
114
119
|
{:error, exception_type}
|
115
120
|
end
|
116
121
|
|
122
|
defp parse_collector_response({:ok, :accepted}, _params) do
|
123
|
{:ok, :accepted}
|
124
|
end
|
125
|
|
117
126
|
defp parse_collector_response({:error, reason}, _params) do
|
118
127
|
{:error, reason}
|
119
128
|
end
|
|
@@ -160,8 169,10 @@ defmodule NewRelic.Harvest.Collector.Protocol do
|
160
169
|
:unexpected_exception
|
161
170
|
end
|
162
171
|
|
163
|
- defp collector_headers,
|
164
|
- do: ["user-agent": "NewRelic-ElixirAgent/#{NewRelic.Config.agent_version()}"]
|
172
|
defp collector_headers do
|
173
|
["user-agent": "NewRelic-ElixirAgent/#{NewRelic.Config.agent_version()}"]
|
174
|
Collector.AgentRun.lookup(:request_headers, [])
|
175
|
end
|
165
176
|
|
166
177
|
defp default_collector_params,
|
167
178
|
do: %{
|
changed
lib/new_relic/harvest/collector/span_event/harvester.ex
|
@@ -19,7 19,7 @@ defmodule NewRelic.Harvest.Collector.SpanEvent.Harvester do
|
19
19
|
start_time_mono: System.monotonic_time(),
|
20
20
|
end_time_mono: nil,
|
21
21
|
sampling: %{
|
22
|
- reservoir_size: Collector.AgentRun.lookup(:span_event_reservoir_size),
|
22
|
reservoir_size: Collector.AgentRun.lookup(:span_event_reservoir_size, 100),
|
23
23
|
events_seen: 0
|
24
24
|
},
|
25
25
|
events: PriorityQueue.new()
|
|
@@ -131,7 131,7 @@ defmodule NewRelic.Harvest.Collector.SpanEvent.Harvester do
|
131
131
|
spans
|
132
132
|
])
|
133
133
|
|
134
|
- log_harvest(length(spans))
|
134
|
log_harvest(length(spans), state.sampling.reservoir_size)
|
135
135
|
end
|
136
136
|
|
137
137
|
def generate_guid(:root), do: DistributedTrace.generate_guid(pid: self())
|
|
@@ -139,8 139,9 @@ defmodule NewRelic.Harvest.Collector.SpanEvent.Harvester do
|
139
139
|
def generate_guid({label, ref}),
|
140
140
|
do: DistributedTrace.generate_guid(pid: self(), label: label, ref: ref)
|
141
141
|
|
142
|
- def log_harvest(harvest_size) do
|
143
|
- NewRelic.report_metric({:supportability, SpanEvent}, harvest_size: harvest_size)
|
142
|
def log_harvest(harvest_size, reservoir_size) do
|
143
|
NewRelic.report_metric({:supportability, "SpanEventData"}, harvest_size: harvest_size)
|
144
|
NewRelic.report_metric({:supportability, "SpanEventData"}, reservoir_size: reservoir_size)
|
144
145
|
NewRelic.log(:debug, "Completed Span Event harvest - size: #{harvest_size}")
|
145
146
|
end
|
changed
lib/new_relic/harvest/collector/transaction_error_event/harvester.ex
|
@@ -17,7 17,7 @@ defmodule NewRelic.Harvest.Collector.TransactionErrorEvent.Harvester do
|
17
17
|
start_time_mono: System.monotonic_time(),
|
18
18
|
end_time_mono: nil,
|
19
19
|
sampling: %{
|
20
|
- reservoir_size: Collector.AgentRun.lookup(:error_event_reservoir_size),
|
20
|
reservoir_size: Collector.AgentRun.lookup(:error_event_reservoir_size, 10),
|
21
21
|
events_seen: 0
|
22
22
|
},
|
23
23
|
error_events: []
|
|
@@ -80,7 80,7 @@ defmodule NewRelic.Harvest.Collector.TransactionErrorEvent.Harvester do
|
80
80
|
end
|
81
81
|
|
82
82
|
def log_harvest(harvest_size) do
|
83
|
- NewRelic.report_metric({:supportability, TransactionErrorEvent}, harvest_size: harvest_size)
|
83
|
NewRelic.report_metric({:supportability, "ErrorEventData"}, harvest_size: harvest_size)
|
84
84
|
NewRelic.log(:debug, "Completed Error Event harvest - size: #{harvest_size}")
|
85
85
|
end
|
changed
lib/new_relic/harvest/collector/transaction_event/harvester.ex
|
@@ -18,7 18,7 @@ defmodule NewRelic.Harvest.Collector.TransactionEvent.Harvester do
|
18
18
|
start_time_mono: System.monotonic_time(),
|
19
19
|
end_time_mono: nil,
|
20
20
|
sampling: %{
|
21
|
- reservoir_size: Collector.AgentRun.lookup(:transaction_event_reservoir_size),
|
21
|
reservoir_size: Collector.AgentRun.lookup(:transaction_event_reservoir_size, 100),
|
22
22
|
events_seen: 0
|
23
23
|
},
|
24
24
|
events: PriorityQueue.new()
|
|
@@ -82,11 82,12 @@ defmodule NewRelic.Harvest.Collector.TransactionEvent.Harvester do
|
82
82
|
events
|
83
83
|
])
|
84
84
|
|
85
|
- log_harvest(length(events))
|
85
|
log_harvest(length(events), state.sampling.reservoir_size)
|
86
86
|
end
|
87
87
|
|
88
|
- def log_harvest(harvest_size) do
|
89
|
- NewRelic.report_metric({:supportability, TransactionEvent}, harvest_size: harvest_size)
|
88
|
def log_harvest(harvest_size, reservoir_size) do
|
89
|
NewRelic.report_metric({:supportability, "AnalyticEventData"}, harvest_size: harvest_size)
|
90
|
NewRelic.report_metric({:supportability, "AnalyticEventData"}, reservoir_size: reservoir_size)
|
90
91
|
NewRelic.log(:debug, "Completed Transaction Event harvest - size: #{harvest_size}")
|
91
92
|
end
|
changed
lib/new_relic/harvest/collector/transaction_trace/harvester.ex
|
@@ -104,7 104,7 @@ defmodule NewRelic.Harvest.Collector.TransactionTrace.Harvester do
|
104
104
|
end
|
105
105
|
|
106
106
|
def log_harvest(harvest_size) do
|
107
|
- NewRelic.report_metric({:supportability, TransactionTrace}, harvest_size: harvest_size)
|
107
|
NewRelic.report_metric({:supportability, "TransactionTraceData"}, harvest_size: harvest_size)
|
108
108
|
NewRelic.log(:debug, "Completed Transaction Trace harvest - size: #{harvest_size}")
|
109
109
|
end
|
changed
lib/new_relic/instrumented/httpoison.ex
|
@@ -30,7 30,7 @@ if Code.ensure_loaded?(HTTPoison) do
|
30
30
|
|
31
31
|
defp instrument(method, url, headers) do
|
32
32
|
NewRelic.set_span(:http, url: url, method: method, component: "HTTPoison")
|
33
|
- headers NewRelic.create_distributed_trace_payload(:http)
|
33
|
headers NewRelic.distributed_trace_headers(:http)
|
34
34
|
end
|
35
35
|
|
36
36
|
@trace {:get, category: :external}
|
changed
lib/new_relic/span/event.ex
|
@@ -92,5 92,11 @@ defmodule NewRelic.Span.Event do
|
92
92
|
end
|
93
93
|
|
94
94
|
def merge_category_attributes(span, category_attributes),
|
95
|
- do: Map.merge(span, category_attributes)
|
95
|
do:
|
96
|
Map.merge(
|
97
|
span,
|
98
|
category_attributes,
|
99
|
# Don't overwrite existing span keys with custom values
|
100
|
fn _k, v1, _v2 -> v1 end
|
101
|
)
|
96
102
|
end
|
changed
lib/new_relic/tracer.ex
|
@@ -40,7 40,7 @@ defmodule NewRelic.Tracer do
|
40
40
|
@trace {:request, category: :external}
|
41
41
|
def request(method, url, headers) do
|
42
42
|
NewRelic.set_span(:http, url: url, method: method, component: "HttpClient")
|
43
|
- headers NewRelic.create_distributed_trace_payload(:http)
|
43
|
headers NewRelic.distributed_trace_headers(:http)
|
44
44
|
HttpClient.request(method, url, headers)
|
45
45
|
end
|
46
46
|
end
|
changed
lib/new_relic/tracer/report.ex
|
@@ -129,6 129,4 @@ defmodule NewRelic.Tracer.Report do
|
129
129
|
defp function_name({m, f}, i), do: "#{inspect(m)}.#{f}:#{i}"
|
130
130
|
defp function_name({m, f, a}, f), do: "#{inspect(m)}.#{f}/#{a}"
|
131
131
|
defp function_name({m, f, a}, i), do: "#{inspect(m)}.#{f}:#{i}/#{a}"
|
132
|
- defp function_name(f, f), do: "#{f}"
|
133
|
- defp function_name(_f, i), do: "#{i}"
|
134
132
|
end
|
changed
lib/new_relic/transaction/complete.ex
|
@@ -164,17 164,27 @@ defmodule NewRelic.Transaction.Complete do
|
164
164
|
sampled: tx_attrs[:sampled],
|
165
165
|
priority: tx_attrs[:priority],
|
166
166
|
category: "generic",
|
167
|
- name: "Transaction Root Process #{inspect(pid)}",
|
167
|
name: "Transaction Root Process",
|
168
168
|
guid: DistributedTrace.generate_guid(pid: pid),
|
169
169
|
parent_id: tx_attrs[:parentSpanId],
|
170
170
|
timestamp: tx_attrs[:start_time],
|
171
171
|
duration: tx_attrs[:duration_s],
|
172
|
- entry_point: true
|
172
|
entry_point: true,
|
173
|
category_attributes:
|
174
|
%{
|
175
|
pid: inspect(pid)
|
176
|
}
|
177
|
|> maybe_add(:tracingVendors, tx_attrs[:tracingVendors])
|
178
|
|> maybe_add(:trustedParentId, tx_attrs[:trustedParentId])
|
173
179
|
}
|
174
180
|
| spans
|
175
181
|
]
|
176
182
|
end
|
177
183
|
|
184
|
def maybe_add(attrs, _key, nil), do: attrs
|
185
|
def maybe_add(attrs, _key, ""), do: attrs
|
186
|
def maybe_add(attrs, key, value), do: Map.put(attrs, key, value)
|
187
|
|
178
188
|
defp spawned_process_span_events(tx_attrs, process_spawns, process_names, process_exits) do
|
179
189
|
process_spawns
|
180
190
|
|> collect_process_segments(process_names, process_exits)
|
|
@@ -186,11 196,14 @@ defmodule NewRelic.Transaction.Complete do
|
186
196
|
sampled: tx_attrs[:sampled],
|
187
197
|
priority: tx_attrs[:priority],
|
188
198
|
category: "generic",
|
189
|
- name: "Process #{proc.name || proc.pid}",
|
199
|
name: proc.name || "Process",
|
190
200
|
guid: DistributedTrace.generate_guid(pid: proc.id),
|
191
201
|
parent_id: DistributedTrace.generate_guid(pid: proc.parent_id),
|
192
202
|
timestamp: proc[:start_time],
|
193
|
- duration: (proc[:end_time] - proc[:start_time]) / 1000
|
203
|
duration: (proc[:end_time] - proc[:start_time]) / 1000,
|
204
|
category_attributes: %{
|
205
|
pid: proc.pid
|
206
|
}
|
194
207
|
}
|
195
208
|
end)
|
196
209
|
end
|