Each project has a global logger available at Hanami.logger that can be used like this: Hanami.logger.debug "Hello"

It can be configured in config/environment.rb

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :development do
  6. logger level: :info
  7. end
  8. environment :production do
  9. logger level: :info, formatter: :json
  10. # ...
  11. end
  12. end

By default it uses standard output because it’s a best practice that most hosting SaaS companies suggest using.

If you want to use a file, pass stream: 'path/to/file.log' as an option.

Filter sensitive information

Hanami automatically logs the body of non-GET HTTP requests.

When a user submits a form, all the fields and their values will appear in the log:

  1. [bookshelf] [INFO] [2017-08-11 18:17:54 +0200] HTTP/1.1 POST 302 ::1 /signup 5 {"signup"=>{"username"=>"jodosha", "password"=>"secret", "password_confirmation"=>"secret", "bio"=>"lorem"}} 0.00593

To prevent sensitive information from being logged, you can filter it:

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :development do
  6. logger level: :debug, filter: %w[password password_confirmation]
  7. end
  8. end

Now the output will be:

  1. [bookshelf] [INFO] [2017-08-11 18:17:54 +0200] HTTP/1.1 POST 302 ::1 /signup 5 {"signup"=>{"username"=>"jodosha", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "bio"=>"lorem"}} 0.00593

It also supports fine grained patterns to disambiguate params with the same name. For instance, we have a billing form with street number and credit card number, and we want only to filter the credit card:

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :development do
  6. logger level: :debug, filter: %w[credit_card.number]
  7. end
  8. end
  1. [bookshelf] [INFO] [2017-08-11 18:43:04 +0200] HTTP/1.1 PATCH 200 ::1 /billing 2 {"billing"=>{"name"=>"Luca", "address"=>{"street"=>"Centocelle", "number"=>"23", "city"=>"Rome"}, "credit_card"=>{"number"=>"[FILTERED]"}}} 0.009782

Note that billing => address => number wasn’t filtered while billing => credit_card => number was filtered instead.

If you want to disable logging of the body completely, it can be easily achieved with custom formatter:

  1. class NoParamsFormatter < ::Hanami::Logger::Formatter
  2. def _format(hash)
  3. hash.delete :params
  4. super hash
  5. end
  6. end

and then just tell logger to use our new formatter for logging

  1. logger level: :debug, formatter: NoParamsFormatter.new

Arbitrary Arguments

You can specify arbitrary arguments, that are compatible with Ruby’s Logger.

Here’s how to setup daily log rotation:

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :production do
  6. logger 'daily', level: :info, formatter: :json, stream: 'log/production.log'
  7. # ...
  8. end
  9. end

Alternatively, you can decide to put a limit to the number of files (let’s say 10) and the size of each file (eg 1,024,000 bytes, aka 1 megabyte):

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :production do
  6. logger 10, 1_024_000, level: :info, formatter: :json, stream: 'log/production.log'
  7. # ...
  8. end
  9. end

Automatic Logging

All HTTP requests, SQL queries, and database operations are automatically logged.

When a project is used in development mode, the logging format is human readable:

  1. [bookshelf] [INFO] [2017-02-11 15:42:48 +0100] HTTP/1.1 GET 200 127.0.0.1 /books/1 451 0.018576
  2. [bookshelf] [INFO] [2017-02-11 15:42:48 +0100] (0.000381s) SELECT "id", "title", "created_at", "updated_at" FROM "books" WHERE ("book"."id" = '1') ORDER BY "books"."id"

For production environment, the default format is JSON. JSON is parseable and more machine-oriented. It works great with log aggregators or SaaS logging products.

  1. {"app":"bookshelf","severity":"INFO","time":"2017-02-10T22:31:51Z","http":"HTTP/1.1","verb":"GET","status":"200","ip":"127.0.0.1","path":"/books/1","query":"","length":"451","elapsed":0.000391478}

Custom Loggers

You can specify a custom logger in cases where you desire different logging behaviour. For example, the Timber logger:

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :production do
  6. logger Timber::Logger.new(STDOUT)
  7. # ...
  8. end
  9. end

Use this logger as normal via Hanami.logger. It’s important to note that any logger chosen must conform to the default ::Logger interface.

Colorization

Disable colorization

In order to disable the colorization:

  1. # config/environment.rb
  2. # ...
  3. Hanami.configure do
  4. # ...
  5. environment :development do
  6. logger level: :info, colorizer: false
  7. end
  8. end

Custom colorizer

You can build your own colorization strategy

  1. # config/environment.rb
  2. # ...
  3. require_relative "./logger_colorizer"
  4. Hanami.configure do
  5. # ...
  6. environment :development do
  7. logger level: :info, colorizer: LoggerColorizer.new
  8. end
  9. end
  1. # config/logger_colorizer.rb
  2. require "hanami/logger"
  3. require "paint" # gem install paint
  4. class LoggerColorizer < Hanami::Logger::Colorizer
  5. def initialize(colors: { app: [:red, :bright], severity: [:red, :blue], datetime: [:italic, :yellow] })
  6. super
  7. end
  8. private
  9. def colorize(message, color:)
  10. Paint[message, *color]
  11. end
  12. end