Current section
Files
Jump to
Current section
Files
lib/og.ex
defmodule Og do
@moduledoc ~S"""
Some convenience utility functions on top of the elixir `Logger` module. Mostly useful for debugging purposes.
## Summary
- `log/1`, `log/2`, `alog/1`, `alog/2`, `klog/1`, `klog/2`
- Inspects the data before logging it. For example, this can be helpful for avoiding the `Protocol.UndefinedError`
when logging tuples for example. There are two choices for formatting the data:
1. `Kernel.inspect/2` (from erlang core)
2. `Apex.Format.format/2` from [Apex](https://hex.pm/packages/apex) library for pretty printing
- Configure the inspection function in the `config.exs` file by passing:
config :logger, :og, default_inspector: Kernel.inspect/2
or
config :logger, :og, default_inspector: Apex.Format.format/2
- *Optionally* pass the log_level `:debug`, `:info`, `:warn` or `:error` which will respect the
logger `:compile_time_purge_level` configuration setting. Defaults to `:debug`.
- `log_r/1`, `log_r/2`, `alog_r/1`, `alog_r/2`, `klog_r/1`, `klog_r/2`
- Performs operations identical to `Og.log/4` but returns the data in it's original form.
- Can be useful when one wants to log some intermediate data in the middle of a series of
data transformations using the `|>` operator. see the example in `log_return/4`.
## Example configuration
- Short example:
```
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,
default_inspector: &Kernel.inspect/2
```
- Long example:
```
use Mix.Config
limiter1 = "=================== [$level] ========================"
datetime = "|---$date $time"
metadata = "|---$metadata"
node = "|---$node"
msg = "|---message:"
limiter2 = "================== [end $level] ======================="
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: "#{limiter1}\n#{datetime}\n#{metadata}\n#{node}\n#{msg}\n\n$message\n\n#{limiter2}\n",
metadata: [:module, :function, :line]
config :logger, :og,
default_inspector: &Kernel.inspect/2,
kernel: [
inspect_opts: [width: 70]
],
apex: [
format_opts: [numbers: :false, color: :false]
]
```
"""
require Logger
@default_inspector (Application.get_env(:logger, :og) || []) |> Keyword.get(:default_inspector, &Kernel.inspect/2)
@kernel_opts (Application.get_env(:logger, :og) || []) |> Keyword.get(:kernel, [])
@kernel_inspect_opts Keyword.get(@kernel_opts, :inspect_opts, [])
@apex_opts (Application.get_env(:logger, :og) || []) |> Keyword.get(:apex, [])
@apex_format_opts Keyword.get(@apex_opts, :format_opts, [color: :false, numbers: :false])
@default_opts if @default_inspector == &Kernel.inspect/2, do: @kernel_inspect_opts, else: @apex_format_opts
# Public
@doc "Logs the data formatted with the `default_inspector` function and log_level `:debug`"
@spec log(any) :: :ok
def log(data), do: log(data, :debug)
@doc "Logs the data formatted with the `default_inspector` function and log_level passed as the second argument"
@spec log(any, atom) :: :ok
def log(data, log_level) when is_atom(log_level) do
data = @default_inspector.(data, @default_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
def log(data, %Macro.Env{} = env) do
log(data, env, :debug)
end
@doc :false
def log(data, %Macro.Env{} = env, log_level) do
data = base_details(env) <> @default_inspector.(data, @default_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
@doc "Logs the data formatted with the `Kernel.inspect/2` function and log_level `:debug`"
@spec klog(any) :: :ok
def klog(data), do: klog(data, :debug)
@doc "Logs the data formatted with the `Kernel.inspect/2` function and log_level passed as the second argument"
@spec klog(any, atom) :: any
def klog(data, log_level) when is_atom(log_level) do
data = Kernel.inspect(data, @kernel_inspect_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
def klog(data, %Macro.Env{} = env) do
klog(data, env, :debug)
end
@doc :false
def klog(data, %Macro.Env{} = env, log_level) do
data = base_details(env) <> Kernel.inspect(data, @kernel_inspect_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
@doc "Logs the data formatted with the `Apex.Format.format/2` function and log_level `:debug`"
@spec alog(any) :: :ok
def alog(data), do: alog(data, :debug)
@doc "Logs the data formatted with the `Apex.Format.format/2` function and log_level passed as the second argument"
@spec alog(any, atom) :: any
def alog(data, log_level) when is_atom(log_level) do
data = Apex.Format.format(data, @apex_format_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
def alog(data, %Macro.Env{} = env) do
alog(data, env, :debug)
end
@doc :false
def alog(data, %Macro.Env{} = env, log_level) do
data = base_details(env) <> Apex.Format.format(data, @apex_format_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: data], requires: [Logger])
:ok
end
@doc "Logs the data formatted with the `default_inspector` function and log_level `:debug`.
Returns the original data"
@spec log_r(any) :: any
def log_r(data), do: log(data, :debug)
@doc "Logs the data formatted with the `default_inspector` function and log_level passed as the second argument.
Returns the original data"
@spec log_r(any, atom) :: any
def log_r(data, log_level) when is_atom(log_level) do
log_data = @default_inspector.(data, @default_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
def log_r(data, %Macro.Env{} = env) do
log_r(data, env, :debug)
end
@doc :false
def log_r(data, %Macro.Env{} = env, log_level) do
log_data = base_details(env) <> @default_inspector.(data, @default_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
@doc "Logs the data formatted with the `Kernel.inspect/2` function and log_level `:debug`"
@spec klog_r(any) :: any
def klog_r(data), do: klog(data, :debug)
@doc "Logs the data formatted with the `Kernel.inspect/2` function and log_level passed as the second argument"
@spec klog_r(any, atom) :: any
def klog_r(data, log_level) when is_atom(log_level) do
log_data = Kernel.inspect(data, @kernel_inspect_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
def klog_r(data, %Macro.Env{} = env) do
klog_r(data, env, :debug)
end
@doc :false
def klog_r(data, %Macro.Env{} = env, log_level) do
log_data = base_details(env) <> Kernel.inspect(data, @kernel_inspect_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
@doc "Logs the data formatted with the `Apex.Format.format/2` function and log_level `:debug`"
@spec alog_r(any) :: any
def alog_r(data), do: alog(data, :debug)
@doc "Logs the data formatted with the `Apex.Format.format/2` function and log_level passed as the second argument"
@spec alog_r(any, atom) :: any
def alog_r(data, log_level) when is_atom(log_level) do
log_data = Apex.Format.format(data, @apex_format_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
def alog_r(data, %Macro.Env{} = env) do
alog_r(data, env, :debug)
end
@doc :false
def alog_r(data, %Macro.Env{} = env, log_level) do
log_data = base_details(env) <> Apex.Format.format(data, @apex_format_opts)
Code.eval_string("Logger.#{Atom.to_string(log_level)}(args)", [args: log_data], requires: [Logger])
data
end
## To be deprecated
@doc :false
def log_return(data), do: log_r(data, :debug)
## To be deprecated
@doc :false
def log_return(data, log_level) when is_atom(log_level), do: log_r(data, log_level)
def log_return(data, env), do: log_r(data, env, :debug)
## To be deprecated
@doc :false
def log_return(data, env, log_level) when is_map(env), do: log_r(data, env, log_level)
def log_return(data, log_level, _inspect_opts) do
IO.puts("Please note that passing the inspect_opts to log_return has now been deprecated, use another function")
log_r(data, log_level)
end
## To be deprecated
@doc :false
def log_return(data, env, log_level, _inspect_opts) do
IO.puts("Please note that passing the inspect_opts to log_return has now been deprecated, use another function")
log_r(data, env, log_level)
end
# Private
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
end