Elixir

Below is some sample code for a fixed size memory logger in Elixir using ETS as the back end. The idea was to create a a temporary log that could exist across requests and handle multiple channels.

With very little effort we came up with the following solution:

defmodule EtsLog do
  @moduledoc """
  GenServer to manage an in-memory log table. Start with EtsLog.start_link/0
  """
  use GenServer

  @size 3

  # Public API
  @doc """
  Logs a specific message to a channel
  """
  def log(channel, msg) do
    GenServer.cast(__MODULE__, {channel, msg})
  end

  @doc """
  Returns a list of messages in a channel
  """
  def get(channel) do
    GenServer.call(__MODULE__, channel)
  end

  # Private API
  def start_link() do
    GenServer.start_link(__MODULE__, %{table: nil}, [name: __MODULE__])
  end

  def init(state) do
    table = :ets.new(__MODULE__, [:set, :protected])
    {:ok, %{ state | table: table}}
  end

  def handle_call(channel, _from, state) do
    {:reply, :ets.lookup(state[:table], channel), state}
  end

  def handle_cast({channel, msg}, state) do
    case :ets.lookup(state[:table], channel) do
      [] ->
        :ets.insert(state[:table], {channel, [msg]})
      [{_channel, list}] when length(list) >= @size ->
        [_h | t] = Enum.reverse(list)
        :ets.insert(state[:table], {channel, [msg | Enum.reverse(t)]})
      [{_channel, list}] ->
        :ets.insert(state[:table], {channel, [msg | list]})
    end
    {:noreply, state}
  end
end

Here is an example of the logger running in IEx

Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> EtsLog.start_link
{:ok, #PID<0.354.0>}
iex(2)> EtsLog.log(:a, 1)
:ok
iex(3)> EtsLog.log(:a, 2)
:ok
iex(4)> EtsLog.log(:a, 3)
:ok
iex(5)> EtsLog.log(:a, 4)
:ok
iex(6)> EtsLog.get(:a)
[a: [4, 3, 2]]