Packages
new_relic_agent
1.25.2
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: IO.iodata_to_binary(msg),
timestamp: System.convert_time_unit(log.meta.time, :microsecond, :millisecond),
"log.level": log.level
}
|> Map.merge(log_metadata(log))
|> Map.merge(custom_metadata(log))
|> 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
@ignored [:domain, :file, :gl, :line, :mfa, :pid, :time]
defp custom_metadata(log) do
[metadata: Map.drop(log.meta, @ignored)]
|> NewRelic.Util.deep_flatten()
|> NewRelic.Util.coerce_attributes()
|> Map.new()
|> Map.delete("metadata.size")
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