Packages
new_relic_agent
1.14.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/protocol.ex
defmodule NewRelic.Harvest.Collector.Protocol do
alias NewRelic.Harvest.Collector
@moduledoc false
@protocol_version 16
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)
|> parse_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}
defp retry_call({:error, _response}, params, payload), do: issue_call(params, payload)
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: status, body: body}}, params) do
NewRelic.log(:error, "#{params[:method]}: (#{status}) #{body}")
{:error, status}
end
defp parse_http_response({:error, reason}, params) do
NewRelic.log(:error, "#{params[:method]}: #{inspect(reason)}")
{:error, reason}
end
defp parse_collector_response(
{:ok, %{"return_value" => %{"messages" => messages} = return_value}},
_params
) do
Enum.each(messages, &NewRelic.log(:info, &1["message"]))
{:ok, return_value}
end
defp parse_collector_response({:ok, %{"return_value" => return_value}}, _params) do
{:ok, return_value}
end
defp parse_collector_response({:ok, %{"exception" => exception}}, %{method: method}) do
exception_type = respond_to_exception(exception, method)
{:error, exception_type}
end
defp parse_collector_response({:error, reason}, _params) do
{:error, reason}
end
defp parse_collector_response(response, method) do
NewRelic.log(:error, "#{method}: (Unexpected collector response) #{inspect(response)}")
{:error, :unexpected_collector_response}
end
defp respond_to_exception(%{"error_type" => error_type} = exception, method)
when error_type in [
"NewRelic::Agent::ForceDisconnectException",
"NewRelic::Agent::LicenseException"
] do
NewRelic.log(:error, "#{method}: (#{error_type}) #{exception["message"]}")
NewRelic.log(:error, "Disabling agent harvest")
Application.put_env(:new_relic_agent, :harvest_enabled, false)
:license_exception
end
defp respond_to_exception(%{"error_type" => error_type} = exception, method)
when error_type in [
"NewRelic::Agent::ForceRestartException"
] do
NewRelic.log(:error, "#{method}: (#{error_type}) #{exception["message"]}")
NewRelic.log(:error, "Reconnecting agent")
Collector.AgentRun.reconnect()
:force_restart_exception
end
defp respond_to_exception(%{"error_type" => error_type} = exception, method) do
NewRelic.log(:error, "#{method}: (#{error_type}) #{exception["message"]}")
:collector_exception
end
defp respond_to_exception(exception, method) do
NewRelic.log(:error, "#{method}: Unexpected collector exception: #{inspect(exception)}")
:unexpected_exception
end
defp collector_headers,
do: ["user-agent": "NewRelic-ElixirAgent/#{NewRelic.Config.agent_version()}"]
defp default_collector_params,
do: %{
license_key: NewRelic.Config.license_key(),
marshal_format: "json",
protocol_version: @protocol_version
}
end