Lucky Logo

# Configuration

Lucky uses Dexter to handle all logging. The configuration is in config/ where you will find the setup for the Lucky logger, some options for the LogHandler, and logging for Avram.

Lucky sets up a different configuration based on the environment your app is running in.

  • In the test environment, the logger uses the DEBUG level, and writes to a temp file (tmp/test.log) so we don’t muddle your spec output.
  • In production, the logger uses the INFO level, and writes to STDOUT using a json formatter for the output.
  • Finally, in development, the logger uses the DEBUG level, and writes to STDOUT using a custom pretty formatter

All of these settings can be changed in config/

When writing to STDOUT, the colors are turned on for development. If you’d like to disable color, open config/, and set Colorize.enabled = false.

Logger Severity

There are several different severity levels you can choose. These are all defined in the Crystal Logger.

  • Logger::Severity::DEBUG - Show information like which page was rendered, what actions were called, and other debug info.
  • Logger::Severity::ERROR - Only log exception errors.
  • Logger::Severity::FATAL - Only log errors that cause the app to crash.
  • Logger::Severity::INFO - This includes HTTP method, path, and time.
  • Logger::Severity::UNKNOWN - It is unknown
  • Logger::Severity::WARN - Information like deprecation notices, and potential application oddities like retrying a process.

# Logging Data

You can access the logger from anywhere in your app using the Lucky.logger method. This gives you methods based on severity to log to. (e.g. Lucky.logger.debug,

class Dashboard::Index < BrowserAction
  get "/dashboard" do
    # Pass a String to log that message"User has been logged in successsfully")
    html IndexPage

For more key/value logging, you can pass a NamedTuple and Lucky will format the data for you.

class LegacyRedirectHandler
  def call(context)

    # ▸ Message Calling LegacyRedirectHandler. Last thing i had for lunch 🌮
      message: "Calling LegacyRedirectHandler",
      last_thing_i_had_for_lunch: "🌮"

# Custom Log Format

Lucky gives you 2 different formatters by default; the Lucky::PrettyLogFormatter, and the Dexter::Formatters::JsonLogFormatter. These are the formats used in development, and production, respectively.

For custom formatting, you would create a new struct that inherits from Dexter::Formatters::BaseLogFormatter, then define a format method.

# src/log_formatters/
struct MyCustomLogFormatter < Dexter::Formatters::BaseLogFormatter
  # You have access to these methods
  #   severity : Logger::Severity
  #   timestamp : Time
  #   progname : String
  #   io : IO
  def format(data : NamedTuple) : Nil
    io << "The time is #{timestamp}\n"
    data.each do |key, value|
      io << "#{Wordsmith::Inflector.humanize(key)} => #{value}"

Now you can update your default logger to use this format in config/ Change log_formatter: Lucky::PrettyLogFormatter to log_formatter: MyCustomLogFormatter.

The recommended location for this would be in src/log_formatters/. Be sure to require this file in your src/

# LogHandler

Lucky includes a LogHandler middleware in src/ that logs basic request information like the request method, request path, response status, and the duration of the request.


The skip_if option is configured in config/ to allow you to skip logging certain requests that come in to your application. By default, this will skip all GET requests to files in any path starting with /css, /js, or /assets.

This may be a good option to also skip logging if a token is passed in the query params, or any POST request that could contain sensitive data.