Packages
timber
3.1.1
3.1.2
3.1.1
3.1.0
3.0.0
3.0.0-alpha.3
3.0.0-alpha.2
3.0.0-alpha.1
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.1
2.3.0
2.2.1
2.2.0
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc7
2.0.0-rc6
2.0.0-rc5
2.0.0-rc4
2.0.0-rc3
2.0.0-rc2
2.0.0-rc1
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
🌲 Great Elixir Logging Made Easy. Official Timber.io Integration.
Current section
Files
Jump to
Current section
Files
lib/timber.ex
defmodule Timber do
@moduledoc """
This is the root module for interacting with the `:timber` library.
It defines the primary public interface. Users should favor the methods
defined in this module over their lower level counterparts. For example,
instead of `Timber.LocalContact.add/1` use `Timber.add_context/1`.
"""
alias Timber.Context
alias Timber.LocalContext
alias Timber.GlobalContext
#
# Typespecs
#
@typedoc """
The target context to perform the operation.
- `:global` - This stores the context at a global level, meaning
it will be present on every log line, regardless of which process
generates the log line.
- `:local` - This stores the context in the Logger Metadata which
is local to the process
"""
@type context_location :: :local | :global
#
# API
#
@doc """
Adds Timber context to the current process
See `add_context/2`
"""
@spec add_context(map()) :: :ok
def add_context(data, location \\ :local)
@doc """
Adds context which will be included on log entries
The second parameter indicates where you want the context to be
stored. See `context_location` for more details.
"""
@spec add_context(map(), context_location) :: :ok
def add_context(data, :local) do
LocalContext.add(data)
end
def add_context(data, :global) do
GlobalContext.add(data)
end
@doc """
Deletes a key from Timber context on the current process.
See `delete_context/2`
"""
@spec delete_context(atom) :: :ok
def delete_context(key, location \\ :local)
@doc """
Deletes a context key.
The second parameter indicates which context you want the key to be removed from.
"""
@spec delete_context(atom, context_location) :: :ok
def delete_context(key, :local) do
LocalContext.delete(key)
end
def delete_context(key, :global) do
GlobalContext.delete(key)
end
@doc """
Captures the duration in fractional milliseconds since the timer was started. See
`start_timer/0`.
"""
defdelegate duration_ms(timer),
to: Timber.Timer
@doc """
Gets the current context
This is a merged representation of the `Timber.LocalContext` and `Timber.GlobalContext`.
If you would like local or global context specifically you can pass `:global` or `:local`
as the argument to this function.
"""
@spec get_context() :: Context.t()
def get_context(type \\ :all)
def get_context(:all) do
Map.merge(GlobalContext.get(), LocalContext.get())
end
def get_context(:local) do
LocalContext.get()
end
def get_context(:global) do
GlobalContext.get()
end
@doc false
@deprecated "Please use delete_context/1 or delete_context/2"
@spec remove_context_key(atom, context_location) :: :ok
def remove_context_key(key, location \\ :local) do
delete_context(key, location)
end
@doc ~S"""
Used to time runtime execution.
We highly recommend using this method as it uses the system monotonic time for
accuracy.
## Example
timer = Timber.start_timer()
# .... do something
duration_ms = Timber.duration_ms(timer)
event = %{job_completed: %{duration_ms: duration_ms}}
message = "Job completed in #{duration_ms}ms"
Logger.info(message, event: event)
"""
defdelegate start_timer,
to: Timber.Timer,
as: :start
#
# Utilility methods
#
# The following methods are used for internally throughout Timber and it's
# dependent integration libraries.
#
# This method should be used for logging internal events.
#
# Because Timber is a logger it cannot log like a traditional library. This will create
# a loop of debug messages. Instead we write to a configurable IO device that the user can
# inspect, such as `STDOUT`, `STDERR`, a file.
@doc false
def log(level, message_fun) do
Timber.Config.debug_io_device()
|> log(level, message_fun)
end
@doc false
def log(nil, _level, _message_fun) do
false
end
def log(io_device, level, message_fun) when is_function(message_fun) do
level = level |> Atom.to_string() |> String.upcase()
message = message_fun.()
IO.puts(io_device, [level, ": ", message])
end
# Formats a duration, in milliseonds, to a human friendly representation
@doc false
def format_duration_ms(duration_ms) when is_integer(duration_ms),
do: [Integer.to_string(duration_ms), "ms"]
def format_duration_ms(duration_ms) when is_float(duration_ms) and duration_ms >= 1,
do: [:erlang.float_to_binary(duration_ms, decimals: 2), "ms"]
def format_duration_ms(duration_ms) when is_float(duration_ms) and duration_ms < 1,
do: [:erlang.float_to_binary(duration_ms * 1000, decimals: 0), "µs"]
# Convenience function for formatting durations into a human readable string.
@doc false
def format_time_ms(time_ms) when is_integer(time_ms),
do: [Integer.to_string(time_ms), "ms"]
def format_time_ms(time_ms) when is_float(time_ms) and time_ms >= 1,
do: [:erlang.float_to_binary(time_ms, decimals: 2), "ms"]
def format_time_ms(time_ms) when is_float(time_ms) and time_ms < 1,
do: [:erlang.float_to_binary(time_ms * 1000, decimals: 0), "µs"]
end