Tecnología

Por qué escribimos nuestra propia biblioteca de registradores

Registrador
Registrador Registrador Registrador

Temas

Unpopular opinion: you shouldn't use a logging library like pino, Winston, or roarr.

Why not? In short, because they weren't built for you. They weren't built for anyone, really. They were built for everyone - so almost by design, they are worse than a logger you could build yourself, because they come with the baggage of those pesky other people’s requirements. And building a logger for yourself, or your team, isn't hard! It's about the same amount “hard” as configuring a logger that you install from npm, but gives you far more flexibility.

Here's a logger MVP that you will fully understand in about three seconds. You can throw pretty much anything at it and it will produce fairly readable logs:

Need JSON output? Alright, here's v2:

Ok, you probably want debug, info, warn, and error levels. Fine.

congratulations, you now have a logger that's fully compatible with pretty much every log consumer that exists. Now let's make it interesting. What if you want to:

  • add a timestamp to every message
  • only allow certain literal types for the message parameter
  • support injecting data into the async context LINK
  • truncate large json messages
  • protect against circular references throwing errors
  • format http errors in a custom way to extract the most useful information
  • collect debug logs for a given request, and recall them if there's an error or a warning
  • send certain logs to your metrics collector
  • disable certain noisy logs based on an environment variable

Well you're in luck! Since you wrote your own logger, you can just… do those things. Go wild. Write some code. If you're familiar with JavaScript, I bet you can look at that list and think “yeah, I know how to do that”. But even if you’re in the top 1% JavaScript developers in terms of pino-expertise, I bet you don’t know how to do most of those without spending a lot of time learning its configuration rules, docs, quirks, companion libraries and tradeoffs.

A note about transports

I mentioned “send certain logs to your metrics collector” above. Honestly, that’s probably a bad idea for a few different reasons. First, recording metrics and writing logs are quite different concerns, and probably shouldn’t be handled by an all-powerful logger. But it’s also worth noting that transporting logs to other tools, like CloudWatch, Logstash, etc. is also a separate concern, and ideally should happen in a separate process - especially if you, like us, are writing in a (mostly) single-threaded language like nodejs.

There are plenty of options out there (stolen from roarr docs):

  • Beats for aggregating at a process level (written in Go).
  • logagent for aggregating at a process level (written in JavaScript).
  • Fluentd for aggregating logs at a container orchestration level (e.g. Kubernetes) (written in Ruby).

Or if you’re using a service like AWS lambda, simply writing to stdout will result in the logs going to CloudWatch automatically. Locally, you can use jq or your preferred crazy-bash-piping-filtering methods.

Should you listen to me?

I can hear the painful conversation with your CEO now - “sorry we didn’t deliver that feature on time. We needed to build our own logging library”. It might be hard to convince others, or even yourself, that it’d be faster to build something from scratch than to use something off the shelf. The trusty people over at pino have spent a long time thinking about the best way to design a logger, and they’re smart people. Why would you know loggers better? And won’t everyone you work with now need to learn your logger, rather than reading the comprehensive docs of a logger like pino? Won’t I need to maintain my own logger?

And the answer is you don’t know loggers better. But you do know yourself better. And the basic argument here is that loggers are simple enough that you can probably implement something that suits your needs more easily and maintainably than if you install and start configuring one. From experience, maintaining a logger rarely involves debugging the logger itself. Instead it’s usually a matter of tweaking its outputs - and that tends to be easier if it’s written in house. But if you find that pino can straightforwardly meet your needs, of course you should use it. Even then - you should still write your own logger. It’s just that the implementation might be even easier - by wrapping the dependency:

If vanilla pino is good enough for you, you should still make sure that your whole team uses it. This is also why it’s worth spending the time to sell your logger to the rest of your team. Whether you wrote it from scratch or you used a library, half the battle is communicating an internal standard - making a decision that works for your organization and sticking to it, updating it as requirements appear. Otherwise someone else might start using bunyan, and you’ll end up with an internal flame-war (or at least, inconsistent logs).

So don’t tear your hair out. Just write your own. Start with what you need and go from there. Or, you could copy and paste one of the highly unsophisticated ones above. If you do use one, just be aware - it's not for you!

Temas

Suscríbase al Dr. B boletín gratuito para recibir un informe semanal sobre lo último en atención médica y consejos basados en investigaciones para mantenerse sano y mentalmente sano.