Current section
Files
Jump to
Current section
Files
lib/pitcher.ex
defmodule Logger.Backend.Pitcher do
use GenEvent
require Logger
defstruct name: nil,
dispatcher: nil,
url: nil,
method: nil,
headers: nil,
opts: nil,
level: nil,
format: nil,
metadata: nil,
context: nil,
metadata_filter: nil
@default_format "[$level] $message\n"
@default_config [
dispatcher: Logger.Backend.Pitcher.Dispatcher.HTTPoison,
url: '',
method: :post,
headers: [],
opts: [],
level: :debug,
metadata: [],
format: Logger.Formatter.compile(@default_format),
metadata_filter: nil
]
def init({__MODULE__, name}) do
{:ok, init_config(name, [])}
end
def handle_call({:configure, opts}, %{name: name} = state) do
{:ok, :ok, init_config(name, opts, state)}
end
def handle_call(:dispatcher, %{dispatcher: dispatcher} = state) do
{:ok, {:ok, dispatcher}, state}
end
def handle_call(:destroy, state) do
{:ok, :ok, %{state | context: nil}}
end
def handle_event(
{level, _gl, {Logger, msg, ts, md}},
%{level: min_level, metadata_filter: metadata_filter} = state
) do
state =
if (is_nil(min_level) or Logger.compare_levels(level, min_level) != :lt) and
has_metadata?(md, metadata_filter) do
init_dispatcher_and_log_msg(state, level, msg, ts, md, true)
else
state
end
{:ok, state}
end
def handle_event(:flush, state) do
{:ok, state}
end
defp log_msg(level, msg, ts, md, state) do
body = format_msg(level, msg, ts, md, state)
state.dispatcher.send(state.context, body)
state
end
defp format_msg(level, msg, ts, md, %{format: format, metadata: keys}) do
Logger.Formatter.format(format, level, msg, ts, filter_keys(md, keys))
end
def init_dispatcher_and_log_msg(%{context: nil} = state, level, msg, ts, md, true) do
init_dispatcher(state) |> init_dispatcher_and_log_msg(level, msg, ts, md, false)
end
def init_dispatcher_and_log_msg(%{context: nil} = state, _level, _msg, _ts, _md, _retry) do
Logger.error("pitcher is not initialised")
state
end
def init_dispatcher_and_log_msg(state, level, msg, ts, md, _retry),
do: log_msg(level, msg, ts, md, state)
def has_metadata?(md, [{key, val} | rest]) do
case Keyword.fetch(md, key) do
{:ok, ^val} ->
has_metadata?(md, rest)
_ ->
false
end
end
def has_metadata?(_md, _), do: true
defp filter_keys(meta, keys) do
meta
|> Enum.into(%{})
|> Map.take(keys)
|> Map.to_list()
end
defp init_config(name, opts) do
init_config(name, opts, %__MODULE__{})
end
defp init_config(name, opts, state) do
data = Keyword.merge(Application.get_env(:logger, name, []), opts)
Application.put_env(:logger, name, data)
data =
@default_config
|> Keyword.merge(data)
|> compile_format()
new_state = Enum.into(data, %{})
Map.merge(state, new_state)
end
def init_dispatcher(
%{dispatcher: dispatcher, method: method, url: url, headers: headers, opts: opts} = state
) do
case dispatcher.init(method, url, headers, opts) do
{:ok, context} ->
%{state | context: context}
{:error, reason} ->
Logger.error("error creating pitcher context: #{inspect(reason)}")
%{state | context: nil}
end
end
def compile_format(opts) do
case Keyword.get(opts, :format, @default_format) do
format when is_binary(format) ->
Keyword.put(opts, :format, Logger.Formatter.compile(format))
_ ->
opts
end
end
end