Skip to main content

lib/kameleoon/logger.ex

defmodule Kameleoon.Logger do
  @moduledoc """
  Logger configuration for messages emitted by the native Kameleoon Core.

  The logger is global because the underlying Rust Core logger is global.
  """

  alias Kameleoon.Native.Events
  alias Kameleoon.Native.Nif

  @type level :: :none | :error | :warning | :info | :debug
  @type logger :: module()

  @callback log(level(), String.t()) :: any()

  @levels %{
    none: 0,
    error: 1,
    warning: 2,
    info: 3,
    debug: 4
  }

  @doc """
  Sets the minimum log level emitted by the native Core.

  Defaults to `:warning`.
  """
  @spec set_log_level(level()) :: :ok
  def set_log_level(level) do
    Nif.logger_set_log_level(native_level!(level))
  end

  @doc """
  Routes native Core logs to a custom logger.

  The logger must be a module implementing `c:log/2`. Pass `nil` to reset
  logging back to the Core default logger.
  """
  @spec set_logger(logger() | nil) :: :ok | {:error, Kameleoon.Error.t()}
  def set_logger(nil) do
    with {:ok, _pid} <- Events.set_logger(nil) do
      Nif.logger_set_handler(nil)
    end
  end

  def set_logger(logger) when is_atom(logger) do
    unless function_exported?(logger, :log, 2) do
      raise ArgumentError, "logger module must export log/2"
    end

    with {:ok, pid} <- Events.set_logger(logger) do
      Nif.logger_set_handler(pid)
    end
  end

  def set_logger(_logger) do
    raise ArgumentError, "logger must be nil or a module with log/2"
  end

  defp native_level!(level) do
    Map.fetch!(@levels, level)
  rescue
    KeyError ->
      raise ArgumentError,
            "invalid log level #{inspect(level)}, expected one of #{inspect(Map.keys(@levels))}"
  end
end