View Source Logger.Formatter (Logger v1.14.5)

Conveniences for formatting data for logs.

This module allows developers to specify a {module, function} or a string that serves as template for log messages.

Formatting string

The log messages can be controlled by a formatting string. Here is an example of how to configure the :console backend in a config/config.exs file:

For example:

config :logger, :console,
  format: "$time $metadata[$level] $message\n"

The above will print error messages as:

18:43:12.439 user_id=13 [error] Hello\n

The valid parameters you can use are:

  • $time - the time the log message was sent
  • $date - the date the log message was sent
  • $message - the log message
  • $level - the log level
  • $node - the node that prints the message
  • $metadata - user controlled data presented in "key=val key2=val2 " format

Backends typically allow developers to supply such control strings via configuration files. This module provides compile/1, which compiles the string into a format for fast operations at runtime and format/5 to format the compiled pattern into an actual IO data.

Formatting function

You can also customize the format of your log messages to a {module, function} tuple if you wish to provide your own format function. Here is an example of how to configure the :console backend in a config/config.exs file:

config :logger, :console,
  format: {MyConsoleLogger, :format}

And here is an example of how you can define MyConsoleLogger.format/4 from the above configuration:

defmodule MyConsoleLogger do
  @spec format(atom, term, Logger.Formatter.time(), keyword()) :: IO.chardata()
  def format(level, message, timestamp, metadata) do
    # Custom formatting logic that must return chardata.
    # ...
  end
end

The format/4 function must not fail. If it does, it will bring that particular logger instance down, causing your system to temporarily lose log messages. If necessary, wrap the function in a rescue and log a default message instead:

defmodule MyConsoleLogger do
  def format(level, message, timestamp, metadata) do
    # Custom formatting logic
  rescue
    _ -> "could not format: #{inspect({level, message, metadata})}"
  end
end

The {module, function} will be invoked with four arguments:

  • the log level: an atom (atom/0)
  • the message: this is usually IO.chardata/0, but in some cases it may contain invalid data. Since the formatting function must never fail, you need to prepare for the message being anything
  • the current timestamp: a term of type Logger.Formatter.time/0
  • the metadata: a keyword list (keyword/0)

The {module, function} must return a term of type IO.chardata/0.

Metadata

Metadata to be sent to the logger can be read and written with the Logger.metadata/0 and Logger.metadata/1 functions. For example, you can set Logger.metadata([user_id: 13]) to add user_id metadata to the current process. The user can configure the backend to choose which metadata it wants to print and it will replace the $metadata value.

Link to this section Summary

Functions

Compiles a format string into a data structure that format/5 can handle.

Takes a compiled format and injects the level, timestamp, message, and metadata keyword list and returns a properly formatted string.

Formats date as chardata.

Formats time as chardata.

Prunes invalid Unicode code points from lists and invalid UTF-8 bytes.

Link to this section Types

@type pattern() :: :date | :level | :levelpad | :message | :metadata | :node | :time
@type time() :: {{1970..10000, 1..12, 1..31}, {0..23, 0..59, 0..59, 0..999}}

Link to this section Functions

@spec compile(binary() | nil) :: [pattern() | binary()]
@spec compile(pattern) :: pattern when pattern: {module(), function :: atom()}

Compiles a format string into a data structure that format/5 can handle.

Check the module doc for documentation on the valid parameters that will be interpolated in the pattern. If you pass nil as the pattern, the pattern defaults to:

"\n$time $metadata[$level] $message\n"

If you want to customize formatting through a custom formatter, you can pass a {module, function} tuple as the pattern.

iex> Logger.Formatter.compile("$time $metadata [$level] $message\n")
[:time, " ", :metadata, " [", :level, "] ", :message, "\n"]

iex> Logger.Formatter.compile({MyLoggerFormatter, :format})
{MyLoggerFormatter, :format}
Link to this function

format(pattern_or_function, level, message, timestamp, metadata)

View Source
@spec format(
  mod_and_fun | [pattern() | binary()],
  Logger.level(),
  Logger.message(),
  time(),
  keyword()
) ::
  IO.chardata()
when mod_and_fun: {atom(), atom()}

Takes a compiled format and injects the level, timestamp, message, and metadata keyword list and returns a properly formatted string.

If pattern_or_function is a {module, function_name} tuple, then module.function_name(level, message, timestamp, metadata) is invoked to get the message. See Logger.Backends.Console for more information on this.

Examples

iex> pattern = Logger.Formatter.compile("[$level] $message")
iex> timestamp = {{1977, 01, 28}, {13, 29, 00, 000}}
iex> formatted = Logger.Formatter.format(pattern, :info, "hello", timestamp, [])
iex> IO.chardata_to_string(formatted)
"[info] hello"
@spec format_date({1970..10000, 1..12, 1..31}) :: IO.chardata()

Formats date as chardata.

@spec format_time({0..23, 0..59, 0..59, 0..999}) :: IO.chardata()

Formats time as chardata.

@spec prune(IO.chardata()) :: IO.chardata()

Prunes invalid Unicode code points from lists and invalid UTF-8 bytes.

Typically called after formatting when the data cannot be printed.