Packages
new_relic_agent
1.19.1
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)
%URI{
host:
Application.get_env(:new_relic_agent, :collector_instance_host) ||
Application.get_env(:new_relic_agent, :collector_host),
path: "/agent_listener/invoke_raw_method",
query: URI.encode_query(params),
scheme: Application.get_env(:new_relic_agent, :scheme, "https"),
port: Application.get_env(:new_relic_agent, :port, 443)
}
|> 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)
{: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.lookup(:request_headers, [])
end
defp default_collector_params,
do: %{
license_key: NewRelic.Config.license_key(),
marshal_format: "json",
protocol_version: @protocol_version
}
end