View Source Logger

Section

Elixir provides a single interface for creating logs in your application through the Logger module.

defmodule RunElixir.LogExample do

  # You always have to `require` the Logger first.
  # Elixir will warn you if you forget this.
  require Logger

  def test() do
    # Logger supports all 7 system log levels:
    Logger.debug("This is a debug message")
    Logger.info("This is an info message")
    Logger.notice("This is a notice message")
    Logger.warning("This is a warning message")
    Logger.error("This is an error message")
    Logger.critical("This is a critical message")
    Logger.alert("This is an alert message")
    Logger.emergency("This is an emergency message")
  end
end

RunElixir.LogExample.test()
10:31:41.197 [debug] This is a debug message
10:31:41.198 [info] This is an info message
10:31:41.198 [notice] This is a notice message
10:31:41.198 [warning] This is a warning message
10:31:41.198 [error] This is an error message
10:31:41.198 [critical] This is a critical message
10:31:41.198 [alert] This is an alert message
10:31:41.198 [emergency] This is an emergency message

The debug, info, warning, and error levels are widely used in Elixir applications and you'll encounter them all the time. critical messages are usually logged only by the system or the virtual machine and I've yet to see a notice, alert, or emergency log.

Caveat

You have to watch out when you want to log variables. Not all variables implement the String.Chars protocol, which means Elixir doesn't know how to convert them to a string. If you try to log them anyway, you'll encounter an error.

require Logger

Logger.info("My map: #{%{a: 1}}")
** (Protocol.UndefinedError) protocol String.Chars not implemented for %{a: 1} of type Map
    (elixir 1.17.2) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir 1.17.2) lib/string/chars.ex:22: String.Chars.to_string/1

You can prevent this issue by wrapping your variables with inspect/2 first. The Inspect protocol can "stringify" almost all variable types, so it's a pretty safe option to use.

require Logger

Logger.info("My map: #{inspect(%{a: 1})}")
10:40:10.782 [info] My map: %{a: 1}

Configuration

The Logger offers extensive configuration options, but you can configure them only for an Elixir application, not a script or Livebook as shown above. If you followed the Create a Phoenix Project guide, you can configure the logger in the config/(config|dev|prod|test).exs files.

You can set global options that should apply to all environments in the config/config.exs file and add different options in the test.exs, dev.exs, or prod.exs files. Keep in mind that any configuration you put in the test|dev|prod.exs files will overwrite existing options from the config.exs configuration.

This is an example of how you can overwrite the global configuration for the dev environment.

# e.g. in config/config.exs

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

# e.g. in config/dev.exs

# This configuration will overwrite the `level`-option
# from the `config.exs` file, but keep its `format`-option.
config :logger, :console, level: :debug

Now, Elixir will log all messages, including debug messages, in your local development environment using the format defined in config.exs. You can find all format options here. If you run your application with mix phx.server, you'll see the log messages have now the following format.

2024-09-16 10:57:05.096 [info] log message here
2024-09-16 10:57:06.030 [info] another log message