Packages
new_relic_agent
1.24.0-rc.9
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/harvest/telemetry_sdk/spans/harvester.ex
defmodule NewRelic.Harvest.TelemetrySdk.Spans.Harvester do
use GenServer
@moduledoc false
alias NewRelic.Harvest
alias NewRelic.Harvest.TelemetrySdk
alias NewRelic.DistributedTrace
def start_link(_) do
GenServer.start_link(__MODULE__, mode: NewRelic.Config.feature(:infinite_tracing))
end
def init(mode: mode) do
{:ok,
%{
trace_mode: mode,
start_time: System.system_time(),
start_time_mono: System.monotonic_time(),
end_time_mono: nil,
sampling: %{
reservoir_size: Application.get_env(:new_relic_agent, :span_reservoir_size, 5_000),
spans_seen: 0
},
spans: []
}}
end
# API
def report_span(
timestamp_ms: timestamp_ms,
duration_s: duration_s,
name: name,
edge: [span: span, parent: parent],
category: category,
attributes: attributes
) do
with %{trace_id: trace_id, guid: guid} <-
DistributedTrace.get_tracing_context() do
report_span(%{
id: generate_guid(span),
timestamp: timestamp_ms,
"trace.id": trace_id,
attributes:
%{
name: name,
category: category,
"parent.id": generate_guid(parent),
transactionId: guid,
"duration.ms": duration_s * 1000
}
|> NewRelic.Span.Event.merge_category_attributes(attributes)
})
end
end
def report_span(
%NewRelic.Span.Event{
guid: guid,
trace_id: trace_id,
timestamp: timestamp
} = span
) do
report_span(%{
id: guid,
timestamp: timestamp,
"trace.id": trace_id,
attributes:
%{
name: span.name,
category: span.category,
transactionId: span.transaction_id,
"nr.entryPoint": span.entry_point,
"duration.ms": span.duration * 1000,
"parent.id": span.parent_id
}
|> NewRelic.Span.Event.merge_category_attributes(span.category_attributes)
})
end
def report_span(span) do
with :infinite <- NewRelic.Config.feature(:infinite_tracing) do
NewRelic.report_metric({:supportability, :infinite_tracing}, spans_seen: 1)
end
TelemetrySdk.Spans.HarvestCycle
|> Harvest.HarvestCycle.current_harvester()
|> GenServer.cast({:report, span})
end
def gather_harvest,
do:
TelemetrySdk.Spans.HarvestCycle
|> Harvest.HarvestCycle.current_harvester()
|> GenServer.call(:gather_harvest)
def handle_cast(_late_msg, :completed), do: {:noreply, :completed}
def handle_cast({:report, span}, state) do
state =
state
|> store_sampling
|> store_span(span)
{:noreply, state}
end
def handle_call(_late_msg, _from, :completed), do: {:reply, :completed, :completed}
def handle_call(:send_harvest, _from, state) do
send_harvest(%{state | end_time_mono: System.monotonic_time()})
{:reply, :ok, :completed}
end
def handle_call(:gather_harvest, _from, state) do
{:reply, build_span_data(state.spans), state}
end
# Helpers
def store_span(
%{trace_mode: :infinite, sampling: %{spans_seen: seen, reservoir_size: size}} = state,
span
)
when seen == size do
Harvest.HarvestCycle.cycle(TelemetrySdk.Spans.HarvestCycle)
%{state | spans: [span | state.spans]}
end
def store_span(%{trace_mode: :infinite} = state, span) do
%{state | spans: [span | state.spans]}
end
def store_span(%{sampling: %{spans_seen: seen, reservoir_size: size}} = state, span)
when seen < size do
%{state | spans: [span | state.spans]}
end
def store_span(state, _span),
do: state
def store_sampling(%{sampling: sampling} = state),
do: %{state | sampling: Map.update!(sampling, :spans_seen, &(&1 + 1))}
def send_harvest(state) do
TelemetrySdk.API.span(build_span_data(state.spans))
log_harvest(
length(state.spans),
state.sampling.spans_seen,
state.sampling.reservoir_size,
state.trace_mode
)
end
def log_harvest(harvest_size, spans_seen, reservoir_size, trace_mode) do
with :infinite <- trace_mode do
NewRelic.report_metric({:supportability, :infinite_tracing}, harvest_size: harvest_size)
end
NewRelic.log(
:debug,
"Completed TelemetrySdk.Span harvest - " <>
"mode: #{trace_mode}, size: #{harvest_size}, seen: #{spans_seen}, max: #{reservoir_size}"
)
end
defp generate_guid(:root), do: DistributedTrace.generate_guid(pid: self())
defp generate_guid({label, ref}),
do: DistributedTrace.generate_guid(pid: self(), label: label, ref: ref)
defp build_span_data(spans) do
[
%{
spans: spans,
common: common()
}
]
end
defp common() do
%{
attributes: NewRelic.Harvest.Collector.AgentRun.entity_metadata()
}
end
end