In non-production environments, structured logs are turned into easy read text log entries, but the underlying log entries are represented as struct-like objects, even if you pass a text message to your logger.

To log a text entry, simply use a logger method with a name corresponding to the log level that you want to use. Let’s say you want to log an entry with INFO level:

  1. bookshelf[development]> app["logger"].info "Something's wrong"
  2. # [bookshelf] [ERROR] [2022-11-20 13:48:05 +0100] Something's wrong

The following logging methods are available:

  • debug
  • warn
  • fatal

In addition to plain text logging, you can log arbitrary data by passing a log entry payload to a log method:

  1. bookshelf[development]> app["logger"].info text: "Hello World", component: "admin"
  2. # [bookshelf] [INFO] [2022-11-20 13:51:40 +0100] text="Hello World" component="admin"

Notice that the default development log formatting turns our payload into a key=value representation. It’s easy to read or even parse programatically, but please remember that in any environment where logs are parsed and post-processing using JSON format is the recommended way, and this is how Hanami configures its logger in production environment by default.

Hanami logger supports logging exceptions out of the box without the need to write custom formatters. Simply rescue from an exception and pass it to the error log method:

  1. bookshelf[development]> raise "OH NOEZ!"
  2. bookshelf[development]> rescue => e
  3. bookshelf[development]> app["logger"].error(e, component: "admin")
  4. bookshelf[development]> end
  5. # [bookshelf] [ERROR] [2022-11-20 13:56:36 +0100] component="console"