Technical Resources
Educational Resources
APM Integrated Experience
Connect with Us
Logging is vital for any nontrivial application. Without logging, you’d be completely hopeless when your application crashes in production, and you wouldn’t have the faintest idea of what went wrong or how to fix it. The importance of logging is the same across all technology stacks and types of software, and Ruby and Ruby on Rails are no exception to this rule. So today, we’ll share a list of Ruby on Rails logging best practices for beginners.
Some of the tips will be specific to Rails, and others will be completely platform agnostic. By leveraging this short list of easy-to-follow, readily applicable tips, you can enhance your logging approach on Rails and other platforms, as most of these tips are applicable to other tech stacks.
When it comes to logging, the metadata associated with each entry is almost as important as the message itself. Logging levels are important pieces of information you should include in every log.
Levels are labels you can use to categorize each log entry according to their severity. The Rails logger supports five levels, with values from 0 to 5:
– Debug is used during debugging.
– Info indicates a relevant yet expected event.
– Warn indicates a relevant, abnormal event. It’s not an error yet, but you should pay attention to it.
– Error indicates a mostly recoverable error in the application.
– Fatal indicates a catastrophic problem causing the application to crash.
– Unknown captures every event without an associated known log level, so you can cover all bases and avoid letting anything slip through.
What’s the benefit of applying logging levels to your log entries?
First, using log levels helps you avoid a “needle in a haystack” kind of situation by facilitating the searching and filtering of log messages.
Additionally, you can use levels to further control your log output by writing logs of different levels to different destinations.
Finally, logging levels can help control the granularity of your logging. One best practice is to define different logging levels for each environment. In the development environment, for instance, you might want to log everything to help with debugging. But in production, this would be considered noisy and excessive, so you might want to set info as the minimum level.
Rails used to default to debug in the development environment and to info in production, but this is no longer the case. From Rails 4.2 onward, debug is the default on all environments.
It’s not much use knowing an important event happened without knowing when it occurred. This is why a timestamp is an incredibly valuable piece of information, and you should include one in every log entry.
Things are more complicated than just “adding a timestamp,” though. For starters, we often can’t make up our minds on how to represent a point in time. There are many different formats to represent a timestamp, and this can cause ambiguities. When you factor in time zones, you have a recipe for confusion.
The advice here is twofold. First, use the ISO-8601 format for displaying timestamps. Second, to avoid problems caused by differences in time zones, use UTC for your timestamps. Alternatively, use local time plus the offset from UTC.
Be considerate of your reader when logging. Make your log messages meaningful by adding as much context as possible.
For instance, don’t just log this:
Operation failed.
Instead, log this because it provides more context:
Upload of file 'q1-sales-report.csv' failed.
Logging too little is a problem, but logging too much is also undesirable. Logging excessively in production can make it harder to find the information you need when troubleshooting a faulty application. However, there’s more to it than just noise.
When logging, make sure you don’t log sensitive information. This includes:
– security-sensitive information, such as passwords or encryption keys;
– financial information, such as credit card numbers;
– and personally identifiable information, such as full names, email addresses, and birth dates.
Failing to safeguard this information could land you in dire financial and legal troubles.
Even though the primary target audience for log messages is people, you might need to parse log entries for various reasons.
Unfortunately, you might struggle with this if your logs are plain-text messages. Parsing these might require complex—and brittle—regular expressions. It’s better to log in an easy-to-parse format from the beginning. The practice of doing this is called “structured logging.” Nowadays, a popular format for structured logging is JSON.
To leverage structured logging in Rails, you could use a library such as Lograge.
There will always be a small performance hit associated with logging—this is inevitable and not exclusive to Rails. But there are simple steps you can take to mitigate these impacts.
First, think about the way you use levels in your application. The debug level will always cause a bigger performance hit than fatal due to the number of strings being evaluated.
Also, too many calls to the logger on your code might cause a performance hit. Consider the following line of code:
logger.info "The result is #{expensive_calculation}"
The line above will incur a performance hit, even if the currently configured log level doesn’t allow info messages. This is because—aside from the call to a costly method—Ruby has to evaluate the string and perform concatenation.
In other languages, the solution is to check whether the desired level is included in the allowed output level before attempting to call.
Ruby allows a more elegant solution:
logger.info {"The result is #{expensive_calculation}"}
It might not look different, but it is. In the line above, we’re passing a block as an argument for the info method. Due to lazy evaluation, the contents of the block (including the costly computations) will only be executed if info is enabled.
The importance of logging in software development is hard to overstate. If you want to achieve high-quality software, it’s essential to implement a proper logging strategy. Failing to do so is like driving through a highway blindfolded.
In today’s post, we offered an introductory guide to logging in Ruby on Rails and a list of best practices you can start employing right away. Some are Ruby/Ruby on Rails-centric, and others are applicable to different programming languages or platforms.
So what should your next steps be?
I’d advise you to leverage logging beyond mere troubleshooting. Sure, debugging and troubleshooting are the main and most obvious uses for logs. Logging offers invaluable help here.
But logging can also be used in more proactive ways. Instead of putting out fires, what if you could prevent them from starting in the first place?
Well, you can. Most software organizations nowadays generate enormous amounts of log data every day. These logs contain many valuable insights, but they’re lying there, dormant. By applying a log management solution such as SolarWinds® Loggly®, you can extract the knowledge stuck in your log files and put it to good use, transforming it into proactive insights to help you with the decision-making process.
This post was written by Carlos Schults. Carlos is a .NET software developer with experience in desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices capable of helping you improve app health, such as code review, automated testing, and continuous build.