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/logs_in_context.ex
defmodule NewRelic.LogsInContext do
@moduledoc false
alias NewRelic.Harvest.Collector.AgentRun
alias NewRelic.Harvest.TelemetrySdk
@elixir_version_requirement ">= 1.10.0"
def elixir_version_supported?(mode) do
case Version.match?(System.version(), @elixir_version_requirement) do
true ->
true
false ->
mode in [:direct, :forwarder] &&
NewRelic.log(:error, ":logs_in_context requires Elixir 1.10 or greater")
false
end
end
def configure(:direct) do
:logger.add_primary_filter(:nr_logs_in_context, {&primary_filter/2, %{mode: :direct}})
end
def configure(:forwarder) do
:logger.add_primary_filter(:nr_logs_in_context, {&primary_filter/2, %{mode: :forwarder}})
Logger.configure_backend(:console, format: {NewRelic.LogsInContext, :format})
end
def configure(:disabled) do
:skip
end
def configure(unknown) do
NewRelic.log(:error, "Unknown :logs_in_context mode: #{inspect(unknown)}")
:skip
end
def primary_filter(%{msg: {:string, _msg}} = log, %{mode: :direct}) do
log
|> prepare_log()
|> TelemetrySdk.Logs.Harvester.report_log()
log
end
def primary_filter(%{msg: {:string, _msg}} = log, %{mode: :forwarder}) do
message =
log
|> prepare_log()
|> Map.merge(linking_metadata())
|> Jason.encode!()
%{log | msg: {:string, message}}
end
def primary_filter(_log, _config) do
:ignore
end
defp prepare_log(%{msg: {:string, msg}} = log) do
%{
message: msg,
timestamp: System.convert_time_unit(log.meta.time, :microsecond, :millisecond),
"log.level": log.level
}
|> Map.merge(log_metadata(log))
|> Map.merge(logger_metadata())
|> Map.merge(tracing_metadata())
end
def format(_level, message, _timestamp, _metadata) when is_binary(message) do
message <> "\n"
end
# Fallback to default formatter for future compatibility with Elixir structured logging
def format(level, message, timestamp, metadata) do
config = Logger.Formatter.compile(nil)
Logger.Formatter.format(config, level, message, timestamp, metadata)
end
defp log_metadata(log) do
{module, function, arity} = log.meta.mfa
%{
"line.number": log.meta.line,
"file.name": log.meta.file |> to_string,
"module.name": inspect(module),
"function.name": "#{function}/#{arity}",
"process.pid": inspect(log.meta.pid)
}
end
defp logger_metadata() do
case :logger.get_process_metadata() do
:undefined ->
%{}
metadata ->
[metadata: metadata]
|> NewRelic.Util.deep_flatten()
|> NewRelic.Util.coerce_attributes()
|> Map.new()
|> Map.delete("metadata.size")
end
end
def linking_metadata() do
AgentRun.entity_metadata()
end
def tracing_metadata() do
context = NewRelic.DistributedTrace.get_tracing_context() || %{}
%{
"trace.id": Map.get(context, :trace_id),
"span.id": Map.get(context, :guid)
}
end
end