Packages
new_relic_agent
1.23.0-rc.3
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/protocol.ex
defmodule NewRelic.Harvest.Collector.Protocol do
alias NewRelic.Harvest.Collector
@moduledoc false
@protocol_version 17
def preconnect,
do: call_remote(%{method: "preconnect"}, [])
def connect(payload),
do: call_remote(%{method: "connect"}, payload)
def transaction_event([agent_run_id, _sampling, _events] = payload),
do: call_remote(%{method: "analytic_event_data", run_id: agent_run_id}, payload)
def error_event([agent_run_id, _sampling, _events] = payload),
do: call_remote(%{method: "error_event_data", run_id: agent_run_id}, payload)
def span_event([agent_run_id, _sampling, _spans] = payload),
do: call_remote(%{method: "span_event_data", run_id: agent_run_id}, payload)
def custom_event([agent_run_id, _sampling, _events] = payload),
do: call_remote(%{method: "custom_event_data", run_id: agent_run_id}, payload)
def metric_data([agent_run_id, _ts_start, _ts_end, _data_array] = payload),
do: call_remote(%{method: "metric_data", run_id: agent_run_id}, payload)
def error([agent_run_id, _errors] = payload),
do: call_remote(%{method: "error_data", run_id: agent_run_id}, payload)
def transaction_trace([agent_run_id, _traces] = payload),
do: call_remote(%{method: "transaction_sample_data", run_id: agent_run_id}, payload)
defp call_remote(params, payload) do
call_remote(params, payload, NewRelic.Config.enabled?())
end
defp call_remote(%{run_id: nil}, _payload, _enabled) do
{:error, :not_connected}
end
defp call_remote(_params, _payload, false) do
{:error, :harvest_disabled}
end
defp call_remote(params, payload, true),
do:
params
|> issue_call(payload)
|> retry_call(params, payload)
|> handle_collector_response(params)
defp issue_call(params, payload),
do:
params
|> collector_method_url
|> NewRelic.Util.HTTP.post(payload, collector_headers())
|> parse_http_response(params)
defp retry_call({:ok, response}, _params, _payload), do: {:ok, response}
@retryable [408, 429, 500, 503]
defp retry_call({:error, status}, params, payload) when status in @retryable,
do: issue_call(params, payload)
defp retry_call({:error, error}, _params, _payload), do: {:error, error}
defp collector_method_url(params) do
params = Map.merge(default_collector_params(), params)
host =
Application.get_env(:new_relic_agent, :collector_instance_host) ||
NewRelic.Config.get(:collector_host)
%URI{
host: host,
path: "/agent_listener/invoke_raw_method",
query: URI.encode_query(params),
scheme: NewRelic.Config.get(:scheme),
port: NewRelic.Config.get(:port)
}
|> URI.to_string()
end
defp parse_http_response({:ok, %{status_code: 200, body: body}}, _params) do
case Jason.decode(body) do
{:ok, response} ->
{:ok, response}
{:error, jason_exception} ->
NewRelic.log(:error, "Bad collector JSON: #{Exception.message(jason_exception)}")
{:error, :bad_collector_response}
end
end
defp parse_http_response({:ok, %{status_code: 202}}, _params) do
{:ok, :accepted}
end
@force_restart [401, 409]
defp parse_http_response({:ok, %{status_code: status_code, body: body}}, params)
when status_code in @force_restart do
NewRelic.report_metric({:supportability, :collector}, status: status_code)
log_error(status_code, :force_restart, params, body)
{:error, :force_restart}
end
@force_disconnect [410]
defp parse_http_response({:ok, %{status_code: status_code, body: body}}, params)
when status_code in @force_disconnect do
NewRelic.report_metric({:supportability, :collector}, status: status_code)
log_error(status_code, :force_disconnect, params, body)
{:error, :force_disconnect}
end
defp parse_http_response({:ok, %{status_code: status, body: body}}, params) do
NewRelic.report_metric({:supportability, :collector}, status: status)
log_error(status, :unexpected_response, params, body)
{:error, status}
end
defp parse_http_response({:error, reason}, params) do
log_error(:failed_request, reason, params)
{:error, reason}
end
defp handle_collector_response(
{:ok, %{"return_value" => %{"messages" => messages} = return_value}},
_params
) do
Enum.each(messages, &NewRelic.log(:info, &1["message"]))
{:ok, return_value}
end
defp handle_collector_response({:ok, %{"return_value" => return_value}}, _params) do
{:ok, return_value}
end
defp handle_collector_response({:ok, :accepted}, _params) do
{:ok, :accepted}
end
defp handle_collector_response({:error, :force_disconnect}, _params) do
NewRelic.log(:error, "Disabling agent harvest")
Application.put_env(:new_relic_agent, :harvest_enabled, false)
NewRelic.Init.init_config()
{:error, :force_disconnect}
end
defp handle_collector_response({:error, :force_restart}, _params) do
NewRelic.log(:error, "Reconnecting agent")
Collector.AgentRun.reconnect()
{:error, :force_restart}
end
defp handle_collector_response({:error, reason}, _params) do
{:error, reason}
end
defp log_error(error, reason, params) do
NewRelic.log(:error, "#{params[:method]}: (#{error}) #{inspect(reason)}")
end
defp log_error(error, reason, params, "") do
NewRelic.log(:error, "#{params[:method]}: (#{error}) #{inspect(reason)}")
end
defp log_error(status, error, params, body) do
case Jason.decode(body) do
{:ok, %{"exception" => exception}} ->
NewRelic.log(
:error,
"#{params[:method]}: (#{status}) #{error} - " <>
"#{exception["error_type"]} - #{exception["message"]}"
)
_ ->
NewRelic.log(:error, "#{params[:method]}: (#{status}) #{error} - #{body}")
end
end
defp collector_headers do
["user-agent": "NewRelic-ElixirAgent/#{NewRelic.Config.agent_version()}"] ++
(Collector.AgentRun.request_headers() || [])
end
defp default_collector_params,
do: %{
license_key: NewRelic.Config.license_key(),
marshal_format: "json",
protocol_version: @protocol_version
}
end