defmodule File.Only.Logger do @moduledoc """ A simple logger that writes messages to log files only (not to the console). """ use PersistConfig @levels get_env(:levels) @line_length get_env(:line_length, 80) @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/2` into the caller's module. The function will execute the `do_block` and write its result 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/2` into the caller's module. The function will execute the `do_block` and write its result 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" from the given `env` (`Macro.Env`). ## Examples use File.Only.Logger error :exit, {reason, env} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Function: #{fun(env)} """ end ''' defmacro fun(env) do quote do File.Only.Logger.Proxy.fun(unquote(env)) end end @doc ~S''' Will prefix `string` with "\n\s\s" if longer than `line_length` - `offset`. You may use file `config/config.exs` or friends to configure `line_length`: ```elixir import Config config :file_only_logger, line_length: 80 ``` ```elixir import Config line_length = try do {keyword, _binding} = Code.eval_file(".formatter.exs") keyword[:line_length] || 98 rescue _error -> 80 end config :file_only_logger, line_length: line_length ``` ## Examples use File.Only.Logger error :exit, {reason, env} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Function: #{fun(env) |> maybe_break(12)} """ end ''' defmacro maybe_break(string, offset, line_length \\ @line_length) do string = Macro.expand(string, __CALLER__) quote bind_quoted: [string: string, offset: offset, limit: line_length] do File.Only.Logger.Proxy.maybe_break(string, offset, 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 File.Only.Logger.Proxy.app() 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 File.Only.Logger.Proxy.lib() end end @doc ~S''' Returns the current module as a string. ## Examples use File.Only.Logger error :exit, {reason} do """ \n'exit' caught... • Reason: #{inspect(reason)} • Module: #{mod()} """ end ''' defmacro mod do quote do File.Only.Logger.Proxy.mod(__MODULE__) end end @doc ~S''' Returns a formatted heredoc to trace a message from the given `env` (`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 File.Only.Logger.Proxy.from(unquote(env)) end end @doc ~S''' Returns a formatted heredoc to trace a message from the given `env` (`Macro.Env`) and `module`. ## Examples use File.Only.Logger error :exit, {reason, env} do """ \n'exit' caught... • Reason: #{inspect(reason)} #{from(env, __MODULE__)} """ end ''' defmacro from(env, module) do quote do File.Only.Logger.Proxy.from(unquote(env), unquote(module)) end end end