Packages
new_relic_agent
1.24.2
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