Current section

Files

Jump to
og lib og.ex
Raw

lib/og.ex

defmodule Og do
@moduledoc ~S"""
Óg is a small collection of debugging helper functions. Og is a debugging tool for development,
the use of ordinary `Logger` is preferred for production.
## Summary
- `log/2` - logs the data transformed by the inspector function
and returns `:ok`
- `log_r/2` - logs the data transformed by the inspector function
and returns the original data.
- Inspection of the data before logging it can be helpful in a debugging context for
- Avoiding the `Protocol.UndefinedError` when logging tuples for example.
- Not needing to require Logger
- However, the functions `Og.log/2` and `Og.log_r/2` should be reserved for
debugging code only in `:dev` environments and should not
be used in production because:
- Formatting the data carries an overhead.
## Example configuration of the `Logger`
use Mix.Config
config :logger,
backends: [:console],
level: :debug,
compile_time_purge_level: :debug,
compile_time_application: :my_app,
truncate: (4096 * 8),
utc_log: :false
config :logger, :console,
level: :debug,
format: "$time $metadata [$level] $message\n",
metadata: []
config :logger, :og,
kernel_opts: [width: 70],
apex_opts: [numbers: :false, color: :false]
"""
require Logger
defp kernel_opts(), do: Application.get_env(:logger, :og, [])
|> Keyword.get(:kernel_opts, [])
defp apex_opts(), do: Application.get_env(:logger, :og, [])
|> Keyword.get(:apex_opts, [color: :false, numbers: :false])
# Public
@doc """
Formats the data using an inspector function, logs it and returns the
atom `:ok`.
## Notes:
There is an overhead in converting the data to
other formats such as a binary representation. Hence,
`Og.log/2` and `Og.log_r/2` are preferred for
development debugging purposes only.
## opts
***level***: defaults to `:debug`.
***env***: defaults to `:nil`.
***inspector***: defaults to `:default_inspector` in the application config.exs and if
not set, otherwise, defaults to `:kernel`. The inspector function determines how the
data will be transformed. Currently the options are `:kernel` or `:apex` which use
the functions `&Kernel.inspect/2` and `&Apex.Format.format/2` respectively.
## Examples:
Og.log(%{test: "test"})
Og.log(%{test: "test"}, level: :info)
Og.log(%{test: "test"}, env: __ENV__)
Og.log(%{test: "test"}, inspector: :apex)
"""
@spec log(any, Keyword.t) :: :ok
def log(data, opts \\ [])
def log(data, opts) when is_list(opts) do
inspector = Keyword.get(opts, :inspector, :kernel)
inspector_opts =
case inspector do
:kernel -> kernel_opts()
:apex -> apex_opts()
_ -> kernel_opts()
end
inspector =
case inspector do
:kernel -> &Kernel.inspect/2
:apex -> &Apex.Format.format/2
_ -> &Kernel.inspect/2
end
env = Keyword.get(opts, :env, :nil)
level = Keyword.get(opts, :level, :debug)
(base_details(env) <> inspector.(data, inspector_opts))
|> log_data(level)
:ok
end
@spec log(data :: any, env :: Macro.Env.t) :: :ok
def log(data, env_or_level)
@spec log(data :: any, level :: atom) :: :ok
def log(data, :error), do: Og.log(data, level: :error)
def log(data, :warn), do: Og.log(data, level: :error)
def log(data, :info), do: Og.log(data, level: :info)
def log(data, :debug), do: Og.log(data, level: :debug)
@spec log(data :: any, env :: Macro.Env.t, level :: atom) :: :ok
def log(data, %Macro.Env{} = arg, :error), do: Og.log(data, env: arg, level: :error)
def log(data, %Macro.Env{} = arg, :warn), do: Og.log(data, env: arg, level: :warn)
def log(data, %Macro.Env{} = arg, :info), do: Og.log(data, env: arg, level: :info)
def log(data, %Macro.Env{} = arg, :debug), do: Og.log(data, env: arg, level: :debug)
@spec log(data :: any, level :: atom, env :: Macro.Env.t) :: :ok
def log(data, :error, %Macro.Env{} = arg), do: Og.log(data, env: arg, level: :error)
def log(data, :warn, %Macro.Env{} = arg), do: Og.log(data, env: arg, level: :warn)
def log(data, :info, %Macro.Env{} = arg), do: Og.log(data, env: arg, level: :info)
def log(data, :debug, %Macro.Env{} = arg), do: Og.log(data, env: arg, level: :debug)
@doc """
Formats the data using an inspector function, logs it and returns the
original data.
## Notes:
There is an overhead in converting the data to
other formats such as a binary representation. Hence,
`Og.log/2` and `Og.log_r/2` are preferred for
development debugging purposes only.
## opts
***level***: defaults to `:debug`.
***env***: defaults to `:nil`.
***inspector***: defaults to `:default_inspector` in the application config.exs and if
not set, otherwise, defaults to `:kernel`. The inspector function determines how the
data will be transformed. Currently the options are `:kernel` or `:apex` which use
the functions `&Kernel.inspect/2` and `&Apex.Format.format/2` respectively.
## Examples:
Og.log(%{test: "test"})
Og.log(%{test: "test"}, level: :info)
Og.log(%{test: "test"}, env: __ENV__)
Og.log(%{test: "test"}, inspector: :apex)
"""
@spec log_r(any, Keyword.t) :: any
def log_r(data, opts \\ [])
def log_r(data, opts) when is_list(opts) do
log(data, opts)
data
end
@spec log_r(data :: any, env :: Macro.Env.t) :: any
def log_r(data, %Macro.Env{} = env), do: Og.log_r(data, env: env)
@spec log_r(data :: any, level :: atom) :: any
def log_r(data, :error), do: Og.log_r(data, level: :error)
def log_r(data, :warn), do: Og.log_r(data, level: :error)
def log_r(data, :info), do: Og.log_r(data, level: :info)
def log_r(data, :debug), do: Og.log_r(data, level: :debug)
@spec log_r(data :: any, env :: Macro.Env.t, level :: atom) :: any
def log_r(data, %Macro.Env{} = arg, :error), do: Og.log_r(data, env: arg, level: :error)
def log_r(data, %Macro.Env{} = arg, :warn), do: Og.log_r(data, env: arg, level: :warn)
def log_r(data, %Macro.Env{} = arg, :info), do: Og.log_r(data, env: arg, level: :info)
def log_r(data, %Macro.Env{} = arg, :debug), do: Og.log_r(data, env: arg, level: :debug)
@spec log_r(data :: any, level :: atom, env :: Macro.Env.t) :: any
def log_r(data, :error, %Macro.Env{} = arg), do: Og.log_r(data, env: arg, level: :error)
def log_r(data, :warn, %Macro.Env{} = arg), do: Og.log_r(data, env: arg, level: :warn)
def log_r(data, :info, %Macro.Env{} = arg), do: Og.log_r(data, env: arg, level: :info)
def log_r(data, :debug, %Macro.Env{} = arg), do: Og.log_r(data, env: arg, level: :debug)
# Private
defp base_details(:nil), do: ""
defp base_details(env) do
put_into_string = fn(elem, name) ->
case elem do
:nil -> :nil
_ -> "#{name}: #{elem}, "
end
end
function =
case env.function do
:nil -> :nil
{function, arity} -> "#{function}/#{arity}"
end
application = Application.get_env(:logger, :compile_time_application) |> put_into_string.("application")
module = put_into_string.(env.module, "module")
function = put_into_string.(function, "function")
line = put_into_string.(env.line, "line") |> String.trim_trailing(" ") |> String.trim_trailing(",")
"#{application}#{module}#{function}#{line}:\n"
end
defp log_data(formatted_data, level) do
cond do
level == :error -> Logger.error(formatted_data)
level == :warn -> Logger.warn(formatted_data)
level == :info -> Logger.info(formatted_data)
level == :debug -> Logger.debug(formatted_data)
:true -> raise("unexpected log level")
end
end
end