Packages
new_relic_agent
1.12.0
1.40.3
1.40.2
1.40.1
1.40.0
1.39.0
1.38.0
1.37.0
1.36.0
1.35.0
1.34.0
1.33.0
1.32.0
1.31.1
1.31.0
1.30.0
1.29.0
1.28.0
1.27.8
1.27.7
1.27.6
1.27.5
1.27.4
1.27.3
1.27.2
1.27.1
1.27.0
1.26.0
1.25.3
1.25.2
1.25.1
1.25.0
1.24.5
1.24.4
1.24.3
1.24.2
1.24.1
1.24.0
1.24.0-rc.10
1.24.0-rc.9
1.24.0-rc.8
1.24.0-rc.7
1.23.6
1.23.5
1.23.4
1.23.3
1.23.2
1.23.1
1.23.0
1.23.0-rc.6
1.23.0-rc.5
1.23.0-rc.4
1.23.0-rc.3
1.23.0-rc.2
1.23.0-rc.1
1.22.6
1.22.5
1.22.4
1.22.3
1.22.2
1.22.1
1.22.0
1.21.2
1.21.1
1.21.0
1.21.0-rc.3
1.21.0-rc.1
1.20.0
1.20.0-rc.1
1.19.7
1.19.6
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.18.0-rc.5
1.18.0-rc.4
1.18.0-rc.2
1.18.0-rc.1
1.17.1
1.17.0
1.17.0-rc.4
1.17.0-rc.3
1.17.0-rc.2
1.17.0-rc.1
1.17.0-rc.0
1.16.7
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.16.0-rc.2
1.16.0-rc.1
1.16.0-rc.0
1.15.0
1.15.0-rc.3
1.15.0-rc.2
1.15.0-rc.1
1.14.0
1.13.1
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.12
1.9.11
1.9.10
1.9.9
1.9.8
1.9.7
1.9.6
1.9.5
1.9.4
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.0
1.6.2
1.6.1
1.6.0
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.0
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
New Relic's Open-Source Elixir Agent
Current section
Files
Jump to
Current section
Files
lib/new_relic/distributed_trace.ex
defmodule NewRelic.DistributedTrace do
@dt_header "newrelic"
@moduledoc false
alias NewRelic.DistributedTrace.{Context, Tracker}
alias NewRelic.Harvest.Collector.AgentRun
alias NewRelic.Transaction
def accept_distributed_trace_payload(:http, conn) do
case Plug.Conn.get_req_header(conn, @dt_header) do
[trace_payload | _] ->
trace_payload
|> Context.decode()
[] ->
:no_payload
end
end
def create_distributed_trace_payload(:http) do
case get_tracing_context() do
nil -> []
context -> [{@dt_header, Context.encode(context, get_current_span_guid())}]
end
end
def generate_new_context() do
{priority, sampled} = generate_sampling()
%Context{
account_id: AgentRun.account_id(),
app_id: AgentRun.primary_application_id(),
trust_key: AgentRun.trusted_account_key(),
priority: priority,
sampled: sampled
}
end
def track_transaction(context, transport_type: type) do
context
|> assign_transaction_guid()
|> report_attributes(transport_type: type)
|> convert_to_outbound()
|> set_tracing_context()
end
def report_attributes(
%Context{parent_id: nil} = context,
transport_type: _type
) do
[
guid: context.guid,
traceId: context.guid,
priority: context.priority,
sampled: context.sampled
]
|> NewRelic.add_attributes()
context
end
def report_attributes(context, transport_type: type) do
[
"parent.type": context.type,
"parent.app": context.app_id,
"parent.account": context.account_id,
"parent.transportType": type,
"parent.transportDuration": transport_duration(context.timestamp),
parentId: context.parent_id,
parentSpanId: context.span_guid,
guid: context.guid,
traceId: context.trace_id,
priority: context.priority,
sampled: context.sampled
]
|> NewRelic.add_attributes()
context
end
def convert_to_outbound(%Context{parent_id: nil} = context) do
%Context{
account_id: AgentRun.account_id(),
app_id: AgentRun.primary_application_id(),
parent_id: nil,
trust_key: context.trust_key,
guid: context.guid,
trace_id: context.guid,
priority: context.priority,
sampled: context.sampled
}
end
def convert_to_outbound(%Context{} = context) do
%Context{
account_id: AgentRun.account_id(),
app_id: AgentRun.primary_application_id(),
parent_id: context.guid,
trust_key: context.trust_key,
guid: context.guid,
trace_id: context.trace_id,
priority: context.priority,
sampled: context.sampled
}
end
def set_tracing_context(context) do
Tracker.store(self(), context: context)
end
def cleanup_context() do
Tracker.cleanup(self())
end
def get_tracing_context() do
if Transaction.Reporter.tracking?(self()) do
self()
|> Transaction.Reporter.root()
|> Tracker.fetch()
end
end
def set_span(:generic, attrs) do
Process.put(:nr_current_span_attrs, Enum.into(attrs, %{}))
end
def set_span(:http, url: url, method: method, component: component) do
Process.put(:nr_current_span_attrs, %{url: url, method: method, component: component})
end
def set_span(
:datastore,
statement: statement,
instance: instance,
address: address,
hostname: hostname,
component: component
) do
Process.put(:nr_current_span_attrs, %{
statement: statement,
instance: instance,
address: address,
hostname: hostname,
component: component
})
end
def get_span_attrs() do
Process.get(:nr_current_span_attrs) || %{}
end
def set_current_span(label: label, ref: ref) do
current = {label, ref}
previous_span = Process.get(:nr_current_span)
previous_span_attrs = Process.get(:nr_current_span_attrs)
Process.put(:nr_current_span, current)
{current, previous_span, previous_span_attrs}
end
def get_current_span_guid() do
case Process.get(:nr_current_span) do
nil -> generate_guid(pid: self())
{label, ref} -> generate_guid(pid: self(), label: label, ref: ref)
end
end
def reset_span(previous_span: previous_span, previous_span_attrs: previous_span_attrs) do
Process.put(:nr_current_span, previous_span)
Process.put(:nr_current_span_attrs, previous_span_attrs)
end
defp generate_sampling() do
case {generate_sample?(), generate_priority()} do
{true, priority} -> {priority + 1, true}
{false, priority} -> {priority, false}
end
end
defp generate_sample?() do
NewRelic.DistributedTrace.BackoffSampler.sample?()
end
defp generate_priority, do: :rand.uniform() |> Float.round(6)
def assign_transaction_guid(context) do
Map.put(context, :guid, generate_guid())
end
def generate_guid(), do: :crypto.strong_rand_bytes(8) |> Base.encode16() |> String.downcase()
def generate_guid(pid: pid), do: encode_guid([pid, node()])
def generate_guid(pid: pid, label: label, ref: ref), do: encode_guid([label, ref, pid, node()])
def encode_guid(segments) when is_list(segments) do
segments
|> Enum.map(&encode_guid/1)
|> Enum.join("")
|> String.pad_trailing(16, "0")
end
def encode_guid(term) do
term
|> :erlang.phash2()
|> Integer.to_charlist(16)
|> to_string()
|> String.slice(0..4)
|> String.downcase()
end
defp transport_duration(context_start_time) do
(System.system_time(:millisecond) - context_start_time) / 1_000
end
end