prod.exs
config :logger,
backends: [
:console,
{LoggerExtension.Backend.Matrix, :matrix}
]
config :logger, :matrix, level: :info
releases.exs
config :logger, :matrix, server_url: "https://matrix.example.com"
config :logger, :matrix, room: System.fetch_env!("MATRIX_ROOM")
config :logger, :matrix, access_token: System.fetch_env!("MATRIX_ACCESS_TOKEN")
matrix.ex
defmodule LoggerExtension.Backend.Matrix do
@moduledoc """
Deliver log messages to Matrix server.
"""
@behaviour :gen_event
@derive {Inspect, except: [:access_token]}
defstruct level: nil, server_url: nil, room: nil, access_token: nil
# :gen_event API
def init({__MODULE__, _options}) do
config = Application.get_env(:logger, :matrix)
device = Keyword.get(config, :device, :user)
if Process.whereis(device) do
{:ok, configure(config, %__MODULE__{})}
else
{:error, :ignore}
end
end
def handle_call({:configure, options}, state) do
{:ok, :ok, configure(options, state)}
end
def handle_event({_level, group_leader, _event}, state) when node(group_leader) != node() do
{:ok, state}
end
def handle_event({level, _group_leader, {Logger, message, _timestamp, _metadata}}, state) do
%{level: log_level} = state
cond do
not meet_level?(level, log_level) ->
{:ok, state}
true ->
{:ok, send_message(message, state)}
end
end
def handle_event(_, state) do
{:ok, state}
end
def handle_info(_, state) do
{:ok, state}
end
def code_change(_old_version, state, _extra) do
{:ok, state}
end
def terminate(_reason, _state) do
:ok
end
# Internal API
defp meet_level?(_level, nil), do: true
defp meet_level?(level, min) do
Logger.compare_levels(level, min) != :lt
end
defp configure(config, state) do
level = Keyword.get(config, :level)
server_url = Keyword.get(config, :server_url)
room = Keyword.get(config, :room)
access_token = Keyword.get(config, :access_token)
%{
state
| level: level,
server_url: server_url,
room: room,
access_token: access_token
}
end
# Sends the message payload to the server
def send_message(message, state) do
%{server_url: server_url, room: room, access_token: access_token} = state
api_url =
"#{server_url}/_matrix/client/r0/rooms/#{room}/send/m.room.message?access_token=#{
access_token
}"
payload = "{\"msgtype\":\"m.text\", \"body\":\"#{message}\"}"
response =
:httpc.request(
:post,
{to_charlist(api_url), [], 'application/json', payload},
[],
[]
)
with {:error, reason} <- response,
do:
IO.warn(
"LoggerExtension.Backend.Matrix.send_message/3 failed! Error (httpc): '#{
inspect(reason)
}'."
)
state
end
end