Because that seems like where the ecosystem is heading, I wanted to replace the log-based logger we use at Zenlist with one built on tracing. But for whatever reason, tracing's JSON logger just never really felt quite right.
You know what that means: time to build our own! If you want to learn how to build your own logger, want to understand more about tracing layers, or just want to come along, let's take this journey together.
In this article, we'll discover how to build a logger that prints out moment-in-time events. In the next article, we'll look at how we can use tracing's period-of-time spans to provide better structured logs.
Laying the groundwork
Let's get the boring stuff out of the way. cargo new --bin and whatnot.
First we'll need to pull in some dependencies. tracing is the heart of the ecosystem. tracing-subscriber is the crate responsible for capturing spans and events and doing something with them. And since our custom logger will output structured JSON, let's bring in serde_json too.
In our main.rs, we'll start with a very basic program: set up the custom logger, and then use info! to log something simple.
With that out of the way, let's dive right in and create our custom logger. tracing-subscriber exposes a trait called Layer for functionality that deals with tracing spans and events. That's where we want to start.
And if we run this, we get — drum roll please — well nothing. That's not surprising: we haven't implemented any of Layer's methods yet.
So let's start poking around. In tracing, any time info!, error!, or their friends get called, an Event gets created.
And woah! Look at that! The Layer interface has an on_event method. Let's see what we can do.
Enough println! exploration. We have enough to make this look like a real JSON logger.
Instead of a PrintlnVisitor, let's create a JsonVisitor that can build up a JSON object.
And in our event handler, let's build up a JSON object and print it at the end.
Running it gives us...
And there we have it. We have a Layer that we can add to tracing-subscriber that will log structured events. In the next article, we'll look at how we can use tracing's period-of-time spans to provide even more rich context information to our logs.