defmodule Grizzly.Trace do
@moduledoc """
Module that tracks the commands that are sent and received by Grizzly
The trace will hold in memory the last 300 messages. If you want to generate
a log file of the trace records you use `Grizzly.Trace.dump/1`.
The log format is:
```
timestamp source destination sequence_number command_name command_parameters
```
If you want to list the records that are currently being held in memory you
can use `Grizzly.Trace.list/0`.
If you want to start traces from a fresh start you can call
`Grizzly.Trace.clear/0`.
"""
use GenServer
alias Grizzly.Trace.{Record, RecordQueue}
@type src() :: String.t()
@type dest() :: String.t()
@type log_opt() :: {:src, src()} | {:dest, dest()}
@doc """
Start the trace server
"""
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
@doc """
Log the trace information
"""
@spec log(binary(), [log_opt()]) :: :ok
def log(binary, opts \\ []) do
GenServer.cast(__MODULE__, {:log, binary, opts})
end
@doc """
Dump the trace records into a file
"""
@spec dump(Path.t()) :: :ok
def dump(file) do
GenServer.call(__MODULE__, {:dump, file})
end
@doc """
Force clear the records from the trace
"""
@spec clear() :: :ok
def clear() do
GenServer.call(__MODULE__, :clear)
end
@doc """
List all the records currently being traced
"""
@spec list() :: [Record.t()]
def list() do
GenServer.call(__MODULE__, :list)
end
@impl GenServer
def init(_args) do
{:ok, RecordQueue.new()}
end
@impl GenServer
def handle_cast({:log, binary, opts}, records) do
record = Record.new(binary, opts)
{:noreply, RecordQueue.add_record(records, record)}
end
@impl GenServer
def handle_call({:dump, file}, _from, records) do
records_list = RecordQueue.to_list(records)
file_contents = records_to_contents(records_list)
case File.write(file, file_contents) do
:ok ->
{:reply, :ok, records}
{:error, _reason} = error ->
{:reply, error, records}
end
end
def handle_call(:clear, _from, _records) do
{:reply, :ok, RecordQueue.new()}
end
def handle_call(:list, _from, records) do
{:reply, RecordQueue.to_list(records), records}
end
defp records_to_contents(records) do
Enum.reduce(records, "", fn record, str ->
str <> Record.to_string(record) <> "\n"
end)
end
end