Supercharge your Rails logs with tags

Justin Toniazzo

Logs are a great way to keep an eye on what’s happening in a system. They can provide insight into decisions made, logic paths taken, and results of operations.

As your Rails app grows in size and complexity, however, it can be hard to find what you need amidst the sea of logs. You might even be tempted to remove log statements to reduce noise. But that can come at the expense of reducing visibility into other parts of the app, and make your or your coworker’s lives harder in the future.

Rails tagged logs

Rails has a solution for this issue. It allows you to prefix log messages with a tag, making it easier to filter logs to only those you care about right now. Here’s how it works:

Rails.logger.tagged("tag") do
  Rails.logger.info("message")
end

That will write a log like [tag] message. This is useful in your production logs, but very helpful locally as well. If you combine that with grep you can easily filter your development log down to just log statements you care about.

tail -f log/development.log | grep tag

This will only print lines that include tag.

Here are examples of a few situations where I’ve found adding tags to logs helpful.

Understanding new code by adding tagged logs

If you’re just getting started learning about a complex new app or system, you can wrap the entrypoint in Rails.logger.tagged to get a better idea of type types of operations that task performs. For example if you have a complex job, you can wrap its perform method with a tagged log to filter down to just logs written by that job.

class MyJob < ApplicationJob
  def perform
    Rails.logger.tagged("MyJob") do
      # ...
    end
  end
end

Using tagged logs to add context

You can add extra information to logs you’re already writing to help you get a better understanding of the environment. Perhaps you have an endpoint that can be accessed by both regular users and admins, but you notice different behavior for each type. By adding that context to your log statements, you can more easily compare and contrast requests in those two situations.

class UsersController < ApplicationController
  def update
    Rails.logger.tagged(current_user.admin? ? "Admin" : "User") do
      # ...
    end
  end
end

Tagging logs conditionally

If you pass something that’s .blank? (like nil, false, or "") to Rails.logger.tagged it won’t add the tag. You can use this to add tags in certain conditions. For example if you have some way of detecting a suspicious request (e.g. a bot or something), tagging requests made by those users could help keep an eye on them.

# config/initializers/suspicious_request.rb

class SuspiciousRequest
  def initialize(app)
    @app = app
  end

  def call(env)
    Rails.logger.tagged(tag) do
      @app.call(env)
    end
  end

  private

  def tag = suspicious? ? "Suspicious" : nil

  def suspicious? = true # some real implementation, hopefully 😅
end

# also in config/initializers/suspicious_request.rb
Rails.application.config.middleware.use(SuspiciousRequest)

Using multiple levels of tagged logs

Calls to Rails.logger.tagged can be nested. This appends additional tags after the existing ones. So add as many levels of context as you like!

Rails.logger.tagged("first") do
  Rails.logger.tagged("second") do
    Rails.logger.info("wow!")
  end
end

# => [first] [second] wow!

Filter through the noise with tagged logs

Adding tags to logs can be a great way of filtering through noisy logs. The extra context can also make it easier to understand the full picture, or call attention to certain messages. Tags are useful not only as a temporary addition to your development code to help you understand a system or investigate a bug, but also useful as long-term additions to your production logs.

Rails has all sorts of built in little goodies like this. I’m constantly discovering new things. With other languages or frameworks you’d have to add a library to do something like this, or build it yourself. But with Rails it’s all built in and there’s a canonical way of doing things. That’s one of the main reasons I like the framework so much, and what keeps me coming back to it year after year.

Want to chat more about maintaining your Rails app? Let’s talk about how we can work together at thoughtbot.com/hire-us