defmodule File.Only.Logger do @moduledoc """ A simple logger which writes messages to log files only (not to the console). """ @fun_limit 66 # Logging levels ordered by importance or severity... @levels [ :emergency, :alert, :critical, :error, :warning, :warn, :notice, :info, :debug ] @doc """ Either aliases `File.Only.Logger` (this module) and requires the alias or imports `File.Only.Logger`. In the latter case, you could instead simply `import File.Only.Logger`. ## Examples use File.Only.Logger, alias: FileLogger use File.Only.Logger import File.Only.Logger """ defmacro __using__(options) do alias = options[:alias] if alias do quote do alias unquote(__MODULE__), as: unquote(alias) require unquote(alias) end else quote do import unquote(__MODULE__) end end end @doc ~S''' Injects function `info` into the caller's module. The function will write the `message` returned by the `do_block` to the configured info log file. ## Examples use File.Only.Logger info :game_state, {player, game} do """ \nNote that #{player.name}... • Has joined game #{inspect(game.name)} • Game state: #{inspect(game.state)} """ end ''' defmacro info(message_id, variables, do_block) defmacro info(message_id, variables, do: message) do quote do def info(unquote(message_id), unquote(variables)) do File.Only.Logger.Proxy.log(:info, unquote(message)) end end end @doc ~S''' Injects function `error` into the caller's module. The function will write the `message` returned by the `do_block` to the configured error log file. ## Examples use File.Only.Logger error :exit, {reason} do """ \n'exit' caught... • Reason: #{inspect(reason)} """ end ''' defmacro error(message_id, variables, do_block) defmacro error(message_id, variables, do: message) do quote do def error(unquote(message_id), unquote(variables)) do File.Only.Logger.Proxy.log(:error, unquote(message)) end end end for level <- @levels -- [:info, :error] do defmacro unquote(level)(message_id, variables, do_block) defmacro unquote(level)(message_id, variables, do: message) do level = unquote(level) quote do def unquote(level)(unquote(message_id), unquote(variables)) do File.Only.Logger.Proxy.log(unquote(level), unquote(message)) end end end end @doc ~S''' Returns string "./" e.g. "My.Math.sqrt/1" for the given [environment](`Macro.Env`). A string longer than `limit` will be prefixed with "\n\s\s". ## Examples use File.Only.Logger error :exit, {reason, env} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Function: #{fun(env)} """ end ''' defmacro fun(env, limit \\ @fun_limit) do quote do File.Only.Logger.Proxy.fun(unquote(env), unquote(limit)) end end @doc ~S''' Returns the application for the current process or module. Returns `:undefined` if the current process does not belong to any application or the current module is not listed in any application spec. ## Examples use File.Only.Logger error :exit, {reason} do """ \n'exit' caught... • Reason: #{inspect(reason)} • App: #{app()} """ end ''' defmacro app do quote do case :application.get_application() do {:ok, app} -> app :undefined -> Application.get_application(__MODULE__) || :undefined end end end @doc ~S''' Returns the current library name. ## Examples use File.Only.Logger error :exit, {reason} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Library: #{lib()} """ end ''' defmacro lib do quote do unquote(Mix.Project.config()[:app]) end end @doc ~S''' Returns the current module name. ## Examples use File.Only.Logger error :exit, {reason} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Module: #{mod()} """ end ''' defmacro mod do quote do "#{inspect(__MODULE__)}" end end @doc ~S''' Returns a heredoc to trace the logged message back to its source using the given [environment](`Macro.Env`). ## Examples use File.Only.Logger error :exit, {reason, env} do """ \n'exit' caught... • Reason: #{inspect(reason)} #{from(env)} """ end ''' defmacro from(env) do quote do """ • App: #{app()} • Library: #{lib()} • Module: #{mod()} • Function: #{fun(unquote(env))} """ |> String.trim_trailing() end end end