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