LogStage: zero-cost structured logging in Scala. Part 1: Overview
7Mind glad to present new tool for the best logging ever. In this article, I will tell about core features of structured logging, why do we need it and how it could help us. And also, how Logstage can help you to speed up productivity and increase transparency of your applications’ lifecycle. Here is an official page of Logstage, where you can find up-to-date information
Yet another logger? 🧐
Yes! There is a plenty of logging solutions (Airframe, Scribe, slf4j, etc). But nobody can output logs in a structured way.
So what is structured logging?
Logging should be well-readable by humans and machines. But it’s too hard satisfy both sides. For machines logs can be in XML, JSON formats, for example. People prefers plain text which is absolutely hard for querying by searching engines. The goal of structured logging is to solve these sorts of problems and allow additional analytics.
Common use cases:
● the liveness of your system;
○ DB calls;
○ HTTP flow;
○ payment processing;
○ scheduling (CRON jobs, spark, etc);
○ user behavior;
○ A/B testing;
We always write code…
val user = "JohnDoe"
logger.debug(s"Received a message from $user")
“Logger should be easy to read and easier to deal with”
Here is a basic example of it’s usage:
And the console colored output is:
Or JSON for your search engines, respectively:
7mind glad to present first-class logging framework for Scala.
Let’s define main features and compare with other frameworks.
1. Macro-based structuring and context extraction
You can build log messages with string interpolation where logger will output:
- argument names, types (ordered);
- static information (file, line, class, function);
- static part of your log message (message template or interpolation context*);
Interpolation context can help you query messages with the same structure but different parameter values to fetch information for specific API, for example.
2. Aliasing for references
You can replace variables and structures names by yours:
3. Dynamic context & method granularity
For experienced developers, definition of MDC is known. This means, that you can fill your logger with key-value context.
Logstage allows you to do it per each class, per each function. Maximum granularity and it’s completely up to you!
Here is an example:
As you can see, you modify logger by custom data (requested user id and it’s company and method name just if you need, for instance). And output is follow:
And later, if you will have some issues with specific user during revoking token, just query by it’s id and api name and you will find it.
4. SL4J backend
A drop-in replacement for LogBack, route your legacy routes also!
5. Production readiness out-of-box
Izumi Logstage gives you list of needed configurations to start using logger immediately:
- console sink;
- plain text or JSON rendering for messages;
- asynchronous wrappers for sinks (single worker thread at the moment);
- ZIO, Cats and Monix Support
(Fiber id tracking also supported)
6. Automatic structure identifiers
As we wrote in first feature, interpolation context could be very helpful. Here is an example of output:
The last key `@template` in JSON above refers exactly to interpolation context. So, to dump list of specific log message, just filter by this template and vulnerable part of your program will be found as fast as possible!
You can fully customize your logger by your rendering policies (with colors or not, rendering layout, plain text or json)
8. DI — friendly
You can use your lovely DI framework if you prefer to configure your logger with different configurations.
For example, you can minimize your router for local usage purpose and have powerful tool while running on live:
9. Almost no dependencies
No more Jar hells while using log libs!
In next article we will dive into coding. I will show you real usage case of logger. We will define custom Database Sink to sending logs there and how we could extract structured log data and work with it.