118 Resolver Middleware for Logging and Monitoring
118 Resolver Middleware for Logging and Monitoring
Middleware has become the backbone of the modern web ecosystem, especially in applications built on a microservices architecture or a complex monolith. One powerful approach in the world of Node.js API development is the use of a resolver middleware for managing logging and monitoring. In this article, I’ll take a deep dive into the implementation of resolver middleware number 118, a pattern I’ve found to be very effective for building a logging and monitoring pipeline. We’ll also walk through an example implementation, a simulation, and a performance analysis based on real-world experience.
What Is a Resolver Middleware?
A resolver middleware is a function, or a series of functions, that runs a particular process (such as logging, authentication, monitoring, and so on) before or after the main request is handled by the server. This concept is very commonly used in frameworks like Express.js and Koa, and even in GraphQL middleware.
The advantage of a resolver middleware is how easily it lets you manage cross-cutting concerns such as logging and monitoring without polluting your core business logic. You simply attach the resolver to a specific route or endpoint, and all the logging-related logic runs automatically.
Logging and Monitoring: Why Do They Matter?
Before we dive into the middleware itself, let’s remind ourselves: why do we care about logging and monitoring?
- Logging: Helps with debugging, auditing, and tracing the request/response flow.
- Monitoring: Detects problems early, provides performance insights, and enables precise scaling efforts.
In real-world scenarios, good logging can reduce downtime, and many “small incidents” can be diagnosed before they escalate into a major crisis.
Resolver Middleware #118: A Practical Pattern
Once I reached the hundreds in terms of resolver count, I noticed a specific pattern in resolver #118 that I’ve used across several Angular backend and Node.js projects. This pattern:
- Separates request/response logging in detail.
- Measures the processing time of each request (performance monitoring).
- Is flexible: it can plug into a variety of stacks (Express, Koa, Fastify, and so on).
- Supports optional telemetry pushes to external services like Datadog or Grafana.
Let’s take a look at the flow diagram for this logging middleware.
flowchart LR
A[Incoming Request] --> B[Middleware #118]
B --> C{Logging Mode?}
C --Yes--> D[Log Request]
C --No--> E[Skip Logging]
D --> F[Start Timer]
E --> F
F --> G[Process Handler]
G --> H[Stop Timer & Log Response]
H --> I[Push Metric to Monitoring]
I --> J[Send Response]
Example Implementation in Express.js
Here is an example of resolver middleware #118 for logging and monitoring. The core code is simple yet powerful.
1const fs = require('fs');
2const path = require('path');
3
4function middleware118(options = {}) {
5 return function (req, res, next) {
6 const {
7 logRequest = true,
8 logResponse = true,
9 monitorTime = true,
10 telemetryPush = false
11 } = options;
12 const startTime = Date.now();
13
14 if (logRequest) {
15 console.log(`[REQ] ${req.method} ${req.url} @ ${new Date().toISOString()}`);
16 }
17
18 // Simulate pushing to external monitoring
19 function pushTelemetry(durationMs) {
20 if (telemetryPush) {
21 // Example: push to a file as a dummy
22 fs.appendFileSync(
23 path.join(__dirname, "metrics.log"),
24 `endpoint=${req.url},duration=${durationMs}ms\n`
25 );
26 }
27 }
28
29 // Listen for the response to finish
30 res.on('finish', () => {
31 const duration = Date.now() - startTime;
32 if (logResponse) {
33 console.log(
34 `[RES] ${req.method} ${req.url} -> ${res.statusCode} in ${duration}ms`
35 );
36 }
37
38 // Monitoring
39 if (monitorTime) {
40 pushTelemetry(duration);
41 }
42 });
43
44 next();
45 };
46}
47
48// Usage in Express
49const express = require('express');
50const app = express();
51
52app.use(middleware118({ logRequest: true, telemetryPush: true }));
53
54app.get('/hello', (req, res) => {
55 setTimeout(() => {
56 res.json({ message: "World!", time: Date.now() });
57 }, Math.floor(Math.random() * 150));
58});
59
60app.listen(3000, () => {
61 console.log('Server running at http://localhost:3000');
62});Simulation and Analysis
Let’s run a simulation of 3 requests to the /hello endpoint.
| Request | Response Time (ms) | Status | Log Request | Log Response | Telemetry Push |
|---|---|---|---|---|---|
| #1 | 52 | 200 | Yes | Yes | Yes |
| #2 | 101 | 200 | Yes | Yes | Yes |
| #3 | 37 | 200 | Yes | Yes | Yes |
For each request, the following output will appear in the console:
1[REQ] GET /hello @ 2024-06-15T08:00:00.000Z
2[RES] GET /hello -> 200 in 52ms
3[REQ] GET /hello @ 2024-06-15T08:00:01.000Z
4[RES] GET /hello -> 200 in 101ms
5[REQ] GET /hello @ 2024-06-15T08:00:02.000Z
6[RES] GET /hello -> 200 in 37msAnd the metrics.log file will contain:
1endpoint=/hello,duration=52ms
2endpoint=/hello,duration=101ms
3endpoint=/hello,duration=37msMiddleware #118 Feature Table
| Feature | Description | Customizable |
|---|---|---|
| Log Request | Records the method, endpoint, and request arrival time | Yes |
| Log Response | Logs the status and response time | Yes |
| Performance Timer | Measures the processing time of each endpoint | Yes |
| Telemetry Push | Saves metrics to a file/log/monitoring service | Yes |
| Plug & Play | Works with Express, Koa, Fastify, and GraphQL | Yes |
Conclusion
In my opinion, resolver middleware #118 is one of those patterns that is essential to have in any modern Node.js project, especially as the need for observability and auditing continues to grow. With this pattern, implementing logging and monitoring becomes cleaner, more flexible, and more scalable without adding complexity to your core API.
Here are a few tips from using it in real-world projects:
- Make sure the middleware is loaded before your main routes for comprehensive logging.
- Don’t forget to disable telemetry pushes in the development environment.
- Extend the middleware with a traceID for distributed tracing (if you need it).
Don’t hesitate to fork and modify this middleware to suit your team’s and project’s needs. Happy experimenting, and may your application’s observability level up another notch!