Packages
new_relic_agent
1.28.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/harvest/collector/metric/harvester.ex
defmodule NewRelic.Harvest.Collector.Metric.Harvester do
use GenServer
@moduledoc false
alias NewRelic.Harvest
alias NewRelic.Harvest.Collector
alias NewRelic.Metric.MetricData
def start_link(_) do
GenServer.start_link(__MODULE__, [])
end
def init(_) do
{:ok,
%{
start_time: System.system_time(),
start_time_mono: System.monotonic_time(),
end_time_mono: nil,
metrics: %{}
}}
end
# API
def report_custom_metric(name, value),
do: report_metric({:custom, name}, count: 1, value: value)
def increment_custom_metric(name, count),
do: report_metric({:custom, name}, count: count)
def report_metric(identifier, values),
do:
Collector.Metric.HarvestCycle
|> Harvest.HarvestCycle.current_harvester()
|> GenServer.cast({:report, MetricData.transform(identifier, values)})
def gather_harvest,
do:
Collector.Metric.HarvestCycle
|> Harvest.HarvestCycle.current_harvester()
|> GenServer.call(:gather_harvest)
# Server
def handle_cast(_late_msg, :completed), do: {:noreply, :completed}
def handle_cast({:report, metrics}, state) do
{:noreply, %{state | metrics: merge(metrics, 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_metric_data(state.metrics), state}
end
def merge(metrics, state) do
metrics
|> List.wrap()
|> Enum.reduce(state.metrics, &merge_metric/2)
end
def send_harvest(state) do
metric_data = build_metric_data(state.metrics)
Collector.Protocol.metric_data([
Collector.AgentRun.agent_run_id(),
System.convert_time_unit(state.start_time, :native, :second),
System.convert_time_unit(
state.start_time + (state.end_time_mono - state.start_time_mono),
:native,
:second
),
metric_data
])
log_harvest(length(metric_data))
end
def log_harvest(harvest_size) do
NewRelic.report_metric({:supportability, "MetricData"}, harvest_size: harvest_size)
NewRelic.log(:debug, "Completed Metric harvest - size: #{harvest_size}")
end
defp build_metric_data(metrics) do
Enum.map(metrics, &build/1)
end
@size 6
@call_count 1
@total_call_time 2
@total_exclusive_time 3
@min_call_time 4
@max_call_time 5
@sum_of_squares 6
defp merge_metric(metric, metrics_acc) do
case Map.get(metrics_acc, {metric.name, metric.scope}) do
nil ->
counter = new(@size, [])
add(counter, @call_count, round(metric.call_count))
add(counter, @total_call_time, encode(metric.total_call_time))
add(counter, @total_exclusive_time, encode(metric.total_exclusive_time))
add(counter, @min_call_time, encode(metric.min_call_time))
add(counter, @max_call_time, encode(metric.max_call_time))
add(counter, @sum_of_squares, encode(metric.sum_of_squares))
Map.put(metrics_acc, {metric.name, metric.scope}, counter)
counter ->
add(counter, @call_count, round(metric.call_count))
add(counter, @total_call_time, encode(metric.total_call_time))
add(counter, @total_exclusive_time, encode(metric.total_exclusive_time))
if metric.min_call_time < decode(get(counter, @min_call_time)),
do: put(counter, @min_call_time, encode(metric.max_call_time))
if metric.max_call_time > decode(get(counter, @max_call_time)),
do: put(counter, @max_call_time, encode(metric.max_call_time))
add(counter, @sum_of_squares, encode(metric.sum_of_squares))
metrics_acc
end
end
defp build({{name, scope}, counter}) do
[
%{name: to_string(name), scope: to_string(scope)},
[
get(counter, @call_count),
decode(get(counter, @total_call_time)),
decode(get(counter, @total_exclusive_time)),
decode(get(counter, @min_call_time)),
decode(get(counter, @max_call_time)),
decode(get(counter, @sum_of_squares))
]
]
end
@compile {:inline, new: 2, add: 3, put: 3, get: 2}
defp new(size, opts), do: :counters.new(size, opts)
defp add(counter, index, value), do: :counters.add(counter, index, value)
defp put(counter, index, value), do: :counters.put(counter, index, value)
defp get(counter, index), do: :counters.get(counter, index)
# counters store integers, so we encode values
# into integers keeping 4 decimal places of precision
@precision 10_000
@compile {:inline, encode: 1, decode: 1}
defp encode(val), do: round(val * @precision)
defp decode(val), do: val / @precision
end