Logging Best Practices, Part 4: Text-Based Logging
Isn't all logging pretty much the same? Logs appear by default, like magic, without any further intervention by teams other than simply starting a system… right?
While logging may seem like simple magic, there's a lot to consider. Logs don't just automatically appear for all levels of your architecture, and any logs that do automatically appear probably don't have all of the details that you need to successfully understand what a system is doing.
We've talked about actionable logs, log levels, logs for all components and needs, the different methodologies for generating logs, and an overview of text-based logs and structured logs in parts 1, 2, and 3 of this Logging Best Practices series. Now let's explore best practices specifically for text-based logging.
While you certainly can just pass a basic message with a text-based logging system, there are some things to consider when thinking about best practices for text-based logging.
Use Plain Language
First, if you need to log in text-based messages only, use plain language strings. Keep the strings simple and fast to read. You should not use words and sentence structures that will confuse people who may not speak the same language. You're not writing the next great novel, so don't worry if your message sentence structure is exactly the same every time. Also, remove every unnecessary word in a message. Think in terms of what your message looks like when duplicated thousands of times, and a tired ops engineer is scanning a bunch of filtered messages trying to understand what happened.
As an example, compare these two sets of mock log lines:
2020-07-01T13:00:00.00Z 192.168.0.1 jenkins - Error: "build failed #24: Error in pipeline"
2020-07-01T13:00:00.00Z 192.168.0.1 jenkins - Notice: "build succeeded #24: Deployment finished"
2020-07-01T13:00:00.00Z 192.168.0.1 jenkins - Error: "The pipeline build labeled #24 failed when building with the error 'Error in pipeline' and a further Java stacktrace. Please go see the build on the Jenkins console to learn more at this address: https://fakejenkins.fake/fakeorg/fakerepo/24"
2020-07-01T13:00:00.00Z 192.168.0.1 jenkins - Notice: "Success! The pipeline build labeled #24 succeeded when building with the message 'Deployment finished'. Please go see the build on the Jenkins console to learn more at this address: https://fakejenkins.fake/fakeorg/fakerepo/24"
Which set do you think is easier and faster to parse for your brain when you start seeing hundreds of them? The first set, of course. Reduce your word count, and make the message simple. (By the way, note the use of log levels. In this example set, you would be sending down to the Notice or INFO level. You could tune your production instance to only generate and send Error or ERR level, instead.)
Use the Team's Common Language
Any messages you do send in a text-based system should be human-readable, and they should be in the language of the country where the app will be used. Typically, most teams pick English as a universal language when spanning multiple countries, but if the logs will only be used by French-speaking teams, for example, then use French.
When you do create text-based logging messages, use tokenizable strings. What does that mean? Essentially, you're using variables to generate your messages and including data from the environment generating the logs. This action ensures readability. You're passing data to yourself in a way that's a bit easier to parse while also staying easy to read. Unfortunately, note that this path does require regex or some other parsing system, which takes time and overhead. Generally, regex should be a last resort. It's complex, time-consuming, and frustrating to maintain consistently. Remember the old adage when you think of regex: When you have a hammer, everything looks like a nail.
Keep these best practices in mind when thinking about structured logging, which is where we're headed next.