Logging Etiquette

Logging Etiquette

2025-05-26 —

[!WARNING] This page exists deep within bikeshed territory, proceed at your own risk!

What log message do you think is better?

  • Starting the car
  • Started the car successfully

Logging after the thing happens (AKA past tense) gives you the opportunity to include additional information like perhaps "how long did the operation take?" or most importantly: "did the operation succeed or fail?".

Logging before the thing happens also has some benefit, too. For example, what if your program crashes or takes an abnormally long time during the actual car starting? It would be nice to quickly pinpoint where that behavior came from.

For important operations, I like to log before and after, but make the more helpful one a higher level. For example:

[DEBUG] Starting the car
[INFO ] Started the car successfully

This gives the option to have both conveniences, without making your logs too verbose by default.

The rest of this page is dedicated to a few more tips about logging that I've acquired over the years. A properly logged application will be so much easier to debug, especially far down the road.

Structured logging

Once I discovered the elegance of structured logging, I never went back. For example, this now makes me cringe:

log.info(f"Started the car in {time} seconds")

Thanks to structlog in Python and tracing in Rust, you can separate the dynamic info from the actual log message:

log.info("Started the car", time_seconds=time)
info!(time_seconds=time, "Started the car")

Where this pattern really shines is when you're recording your application's logs to a database. Rather than having to write brittle patterns to "unformat" your plaintext logs, it just works the way you want it to without extra effort.

What's a failure and what's an error?

We can make a useful distinction between a failure and an error. A failure just means something failed. Maybe it was a socket connect attempt and the destination host was down. Maybe a login attempt failed because the wrong password was given. Not all failures are "bad", in fact they might even be good.

To the contrary, errors are always bad. It means something happened that the programmer didn't expect or can't possibly recover from. When your HTTP request gets a 500 "internal server error", that's an error.

If your programming environment doesn't have a dedicated level for failure, then you can choose any of info, debug, or trace, depending on how important the failure is:

[INFO] Failed to unlock keyring

Just don't use error! That's probably your first instinct, but then you lose the useful quality that all errors are bad.

Watch your word count

I'll repeat similar advice I have for comments in code: write the minimum number of words necessary to communicate something useful. For comments, they should be about WHY not HOW. For logs, they should mainly be about WHAT.

Write logs as succinctly as possible because it reduces the amount of cognitive overhead imposed on debuggers of the application in the future.

Know your audience

When you're writing a log message, you're effectively communicating into the future for an unknown amount of time and to an unknown number of people. I've written logs that have been emitted millions of times in production over the years.

You don't know exactly who will be reading your logs, so you have to write them for target audiences. Those audiences of a log gets narrower as the verbosity of increases.