README.md

# LoggerBatchedBackend

Service agnostic logger backend for elixir that handles batching and retries.

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `logger_batched_backend` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:logger_batched_backend, "~> 0.1.0"}
  ]
end
```

## Configuration 

```elixir
use Mix.Config

config :logger, backends: [{LoggerBatchedBackend, :example}]

config :logger, :example,
  # [optional] Maximum interval in milliseconds to send batches. Logs are flushed periodically
  # within this interval. Use `nil` to disable this feature.
  # default: 15000
  flush_interval: 1000 * 15,
  # [optional] Maximum batch size. Logs are flushed when the queue reaches this many messages.
  # default: 10
  batch_size: 10,
  # [optional] Minimum log level.
  # default: :debug
  level: :debug
  # [optional] A function that returns the current time, used to override a message's timestamp
  # at the time of logging.
  # default: nil
  timestamp: {Timex, :now},
  # [required] Function that handles logging, see signature below.
  handler: {ExampleHandler, :log},
  # [required] Options passed to the second argument to the `handler` function.
  handler_options: %{
    # ...
  }
```

The configuration must specify `handler_options` and a `handler` method to handle logging as follows:

```elixir
defmodule ExampleHandler do
  @spec log(list(tuple()), any()) :: {:ok, list(tuple())} | {:error, any()}
  def log(batch, config) do
    # ... send messages to some log management service
    {:ok, []}
  end
end
```

The method takes a list of message tuples `{level, message, timestamp, metadata}` and `handler_options`,
and returns an ok tuple with a list of messages that have not been handled to requeue.
This allows for retries without having to handle async tasks:

```elixir
defmodule ExampleHandler do
  @retry 5

  def log(batch, config) do
    with :ok <- do_log(batch) do
      {:ok, []}
    else
      _ -> {:ok, retry(batch)}
    end
  end

  defp retry([]), do: []
  defp retry([{_, _, _, _, @retry} | rest]), do: raise "failed to log message #{@retry} times!"
  defp retry([{lvl, msg, ts, md, tries} | rest]), do: [{lvl, msg, ts, md, tries + 1}] ++ retry(rest)
  defp retry([{lvl, msg, ts, md} | rest]), do: [{lvl, msg, ts, md, 1}] ++ retry(rest)
end