62 lines
1.5 KiB
Elixir
62 lines
1.5 KiB
Elixir
defmodule Forum.LogStore do
|
|
@moduledoc """
|
|
Persists request and hosting logs to a JSON file.
|
|
"""
|
|
use GenServer
|
|
|
|
@max_entries 1_000
|
|
|
|
def start_link(opts) do
|
|
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
|
end
|
|
|
|
def add(entry) when is_map(entry) do
|
|
GenServer.cast(__MODULE__, {:add, entry})
|
|
end
|
|
|
|
def list(limit \\ 200) do
|
|
GenServer.call(__MODULE__, {:list, limit})
|
|
end
|
|
|
|
@impl true
|
|
def init(_opts) do
|
|
path = Path.expand("priv/logs/requests.json", File.cwd!())
|
|
File.mkdir_p!(Path.dirname(path))
|
|
|
|
{:ok, %{path: path, entries: read_entries(path)}}
|
|
end
|
|
|
|
@impl true
|
|
def handle_cast({:add, entry}, state) do
|
|
entry =
|
|
entry
|
|
|> Map.put_new("id", System.unique_integer([:positive, :monotonic]))
|
|
|> Map.put_new("time", DateTime.utc_now() |> DateTime.to_iso8601())
|
|
|
|
entries = [entry | state.entries] |> Enum.take(@max_entries)
|
|
write_entries!(state.path, entries)
|
|
|
|
{:noreply, %{state | entries: entries}}
|
|
end
|
|
|
|
@impl true
|
|
def handle_call({:list, limit}, _from, state) do
|
|
{:reply, Enum.take(state.entries, limit), state}
|
|
end
|
|
|
|
defp read_entries(path) do
|
|
with {:ok, raw} <- File.read(path),
|
|
{:ok, entries} when is_list(entries) <- Jason.decode(raw) do
|
|
entries
|
|
else
|
|
_ -> []
|
|
end
|
|
end
|
|
|
|
defp write_entries!(path, entries) do
|
|
tmp_path = path <> ".tmp"
|
|
File.write!(tmp_path, Jason.encode!(entries, pretty: true))
|
|
File.rename!(tmp_path, path)
|
|
end
|
|
end
|