Skip to content

class Log
inherits Reference

The Log class provides a logging utility that you can use to output messages.

The messages, or Log::Entry have associated levels, such as Info or Error that indicate their importance. See Log::Severity.

To log a message trace, debug, info, notice, warn, error, and fatal methods can be used. They expect a block that will evaluate to the message of the entry.

require "log"

Log.info { "Program started" }

Data can be associated with a log entry via the Log::Emitter yielded in the logging methods.

Log.info &.emit("User logged in", user_id: 42)

If you want to log an exception, you can indicate it in the exception: named argument.

Log.warn(exception: e) { "Oh no!" }
Log.warn exception: e, &.emit("Oh no!", user_id: 42)

The block is only evaluated if the current message is to be emitted to some Log::Backend.

To add structured information to the message you can use the Log::Context.

When creating log messages they belong to a source. If the top-level Log is used as in the above examples its source is the empty string.

The source can be used to identify the module or part of the application that is logging. You can configure for each source a different level to filter the messages.

A recommended pattern is to declare a Log constant in the namespace of your shard or module as follows:

module DB
  Log = ::Log.for("db") # => Log for db source

  def do_something
    Log.info { "this is logged in db source" }
  end
end

DB::Log.info { "this is also logged in db source" }
Log.for("db").info { "this is also logged in db source" }
Log.info { "this is logged in top-level source" }

That way, any Log.info call within the DB module will use the db source. And not the top-level ::Log.info.

Sources can be nested. Continuing the last example, to declare a Log constant db.pool source you can do as follows:

class DB::Pool
  Log = DB::Log.for("pool") # => Log for db.pool source
end

A Log will emit the messages to the Log::Backends attached to it as long as the configured severity filter level permits it.

Logs can also be created from a type directly. For the type DB::Pool the source db.pool will be used. For generic types as Foo::Bar(Baz) the source foo.bar will be used (i.e. without generic arguments).

module DB
  Log = ::Log.for(self) # => Log for db source
end

Default logging configuration

By default entries from all sources with Info and above severity will be logged to STDOUT using the Log::IOBackend.

If you need to change the default level, backend or sources call Log.setup upon startup.

Note

Calling setup will override previous setup calls.

Log.setup(:debug)                     # Log debug and above for all sources to STDOUT
Log.setup("myapp.*, http.*", :notice) # Log notice and above for myapp.* and http.* sources only, and log nothing for any other source.
backend_with_formatter = Log::IOBackend.new(formatter: custom_formatter)
Log.setup(:debug, backend_with_formatter) # Log debug and above for all sources to using a custom backend

Configure logging explicitly in the code

Use Log.setup methods to indicate which sources should go to which backends.

You can indicate actual sources or patterns.

  • the empty string matches only the top-level source
  • * matches all the sources
  • foo.bar.* matches foo.bar and every nested source
  • foo.bar matches foo.bar, but not its nested sources
  • Any comma separated combination of the above

The following configuration will setup for all sources to emit warnings (or higher) to STDOUT, allow any of the db.* and nested source to emit debug (or higher), and to also emit for all sources errors (or higher) to an elasticsearch backend.

Log.setup do |c|
  backend = Log::IOBackend.new

  c.bind "*", :warn, backend
  c.bind "db.*", :debug, backend
  c.bind "*", :error, ElasticSearchBackend.new("http://localhost:9200")
end

Configure logging from environment variables

Include the following line to allow configuration from environment variables.

Log.setup_from_env

The environment variable LOG_LEVEL is used to indicate which severity level to emit. By default entries from all sources with Info and above severity will be logged to STDOUT using the Log::IOBackend.

To change the level and sources change the environment variable value:

$ LOG_LEVEL=DEBUG ./bin/app

You can tweak the default values (used when LOG_LEVEL variable is not defined):

Log.setup_from_env(default_level: :error)

Class methods

.builder

Returns the default Log::Builder used for Log.for calls.

View source

.capture(source : String = "*", level : Severity = Log::Severity::Trace, *, builder = Log

Returns and yields an EntriesChecker that allows checking specific log entries were emitted.

This capture will even work if there are currently no backends configured, effectively adding a temporary backend.

require "spec"
require "log"
require "log/spec"

Log.setup(:none)

def greet(name)
  Log.info { "Greeting #{name}" }
end

it "greets" do
  Log.capture do |logs|
    greet("Harry")
    greet("Hermione")
    greet("Ron")

    logs.check(:info, /greeting harry/i)
    logs.next(:info, /greeting hermione/i)
  end
end

By default logs of all sources and severities will be captured.

Use level to only capture of the given severity or above.

Use source to narrow which source are captured. Values that represent single pattern like http.* are allowed.

The EntriesChecker will hold a list of emitted entries.

EntriesChecker#check will find the next entry which matches the level and message. EntriesChecker#next will validate that the following entry in the list matches the given level and message. EntriesChecker#clear will clear the emitted and captured entries.

With these methods it is possible to express expected traces in either a strict or loose way, while checking ordering.

EntriesChecker#entry returns the last matched Entry. Useful to check additional entry properties other than the message.

EntriesChecker#empty validates there are no pending entries to match.

Using the yielded EntriesChecker allows clearing the entries between statements.

Invocations can be nested in order to capture each source in their own EntriesChecker.

View source

.capture(level : Log::Severity = Log::Severity::Trace, *, builder : Log::Builder = Log

Returns and yields an EntriesChecker that allows checking specific log entries were emitted.

This capture will even work if there are currently no backends configured, effectively adding a temporary backend.

require "spec"
require "log"
require "log/spec"

Log.setup(:none)

def greet(name)
  Log.info { "Greeting #{name}" }
end

it "greets" do
  Log.capture do |logs|
    greet("Harry")
    greet("Hermione")
    greet("Ron")

    logs.check(:info, /greeting harry/i)
    logs.next(:info, /greeting hermione/i)
  end
end

By default logs of all sources and severities will be captured.

Use level to only capture of the given severity or above.

Use source to narrow which source are captured. Values that represent single pattern like http.* are allowed.

The EntriesChecker will hold a list of emitted entries.

EntriesChecker#check will find the next entry which matches the level and message. EntriesChecker#next will validate that the following entry in the list matches the given level and message. EntriesChecker#clear will clear the emitted and captured entries.

With these methods it is possible to express expected traces in either a strict or loose way, while checking ordering.

EntriesChecker#entry returns the last matched Entry. Useful to check additional entry properties other than the message.

EntriesChecker#empty validates there are no pending entries to match.

Using the yielded EntriesChecker allows clearing the entries between statements.

Invocations can be nested in order to capture each source in their own EntriesChecker.

View source

.context : Log::Context

Returns the current fiber logging context.

View source

.context=(value : Log::Context)

Sets the current fiber logging context.

View source

.context=(value : Log::Metadata)

Sets the current fiber logging context.

View source

.debug(*, exception : Exception? = nil

See Log#debug.

View source

.error(*, exception : Exception? = nil

See Log#error.

View source

.fatal(*, exception : Exception? = nil

See Log#fatal.

View source

.info(*, exception : Exception? = nil

See Log#info.

View source

.notice(*, exception : Exception? = nil

See Log#notice.

View source

.progname

The program name used for log entries

Defaults to the executable name

View source

.progname=(progname)

The program name used for log entries

Defaults to the executable name

View source

.setup(*, builder : Log::Builder = Log

Setups logging bindings discarding all previous configurations.

View source

.setup(sources : String = "*", level : Log::Severity = Log::Severity::Info, backend : Log::Backend = IOBackend.new, *, builder : Log::Builder = Log.builder)

Setups logging for sources using the specified level, backend.

View source

.setup(level : Log::Severity = Log::Severity::Info, backend : Log::Backend = IOBackend.new, *, builder : Log::Builder = Log.builder)

Setups logging for all sources using the specified level, backend.

View source

.setup_from_env(*, builder : Log::Builder = Log.builder, default_level : Log::Severity = Log::Severity::Info, default_sources = "*", log_level_env = "LOG_LEVEL", backend = Log::IOBackend.new)

Setups logging based on LOG_LEVEL environment variable.

View source

.trace(*, exception : Exception? = nil

See Log#trace.

View source

.warn(*, exception : Exception? = nil

See Log#warn.

View source

.with_context

Method to save and restore the current logging context.

Log.context.set a: 1
Log.info { %(message with {"a" => 1} context) }
Log.with_context do
  Log.context.set b: 2
  Log.info { %(message with {"a" => 1, "b" => 2} context) }
end
Log.info { %(message with {"a" => 1} context) }
View source

.for(type : Class, level : Severity? = nil) : Log

Creates a Log for the given type. A type Foo::Bar(Baz) corresponds to the source foo.bar. If level is given, it will override the configuration.

View source

.for(source : String, level : Severity? = nil) : Log

Creates a Log for the given source. If level is given, it will override the configuration.

View source

Methods

#backend : Backend?

View source

#context : Log::Context

Returns the current fiber logging context.

View source

#context=(value : Log::Metadata | Log::Context)

Sets the current fiber logging context.

View source

#debug(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 1.

View source

#error(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 5.

View source

#fatal(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 6.

View source

#for(child_source : String, level : Severity? = nil) : Log

Creates a Log for the given nested source. If level is given, it will override the configuration.

View source

#for(type : Class, level : Severity? = nil) : Log

Creates a Log for the given type. A type Foo::Bar(Baz) corresponds to the source foo.bar. If level is given, it will override the configuration.

View source

#info(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 2.

View source

#level=(value : Severity)

Change this log severity level filter.

View source

#notice(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 3.

View source

#source : String

View source

#trace(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 0.

View source

#warn(*, exception : Exception? = nil

Logs a message if the logger's current severity is lower or equal to 4.

View source

#with_context

Method to save and restore the current logging context.

Log.context.set a: 1
Log.info { %(message with {"a" => 1} context) }
Log.with_context do
  Log.context.set b: 2
  Log.info { %(message with {"a" => 1, "b" => 2} context) }
end
Log.info { %(message with {"a" => 1} context) }
View source

Macros

define_formatter(name, pattern)

Generate subclasses of Log::StaticFormatter from a string with interpolations

Example:

Log.define_formatter MyFormat, "- #{severity}: #{message}"
See Log::StaticFormatter for the available methods that can be called within the interpolations.

View source