Back

/ 4 min read

Node.js Logging: The Art of Digital Breadcrumbs

The Crumb Conundrum

You’re lost in the woods of your Node.js application. Async operations are flying left and right, and you can’t seem to keep track of what’s happening where. Sound familiar? Of course it does. You’re a developer, after all. You’ve been here before, and you’ll be here again.

But what if I told you there was a way to leave a trail of breadcrumbs through your code? A way to always know where you’ve been and where you’re going? Enter the world of async storage and logging in Node.js.

Async Storage: Your Magical Backpack

Imagine you had a magical backpack. No matter where you go or what you do, it’s always with you, always accessible. That’s async storage in a nutshell. It’s like a context-aware data store that follows your code through the twists and turns of asynchronous operations.

import { AsyncLocalStorage } from 'node:async_hooks';
type StoreData = {
user?: string;
};
const asyncLocalStorage = new AsyncLocalStorage<StoreData>();
asyncLocalStorage.run({}, () => {
const store = asyncLocalStorage.getStore();
if (store) {
store.user = 'Alice';
setTimeout(() => {
console.log(store.user); // Prints 'Alice'
}, 100);
}
});

Look at that! Even after a 100ms nap, our code still knows who Alice is. It’s like magic, but better—it’s TypeScript.

Where the Magic Happens

Now, you might be thinking, “Great, another tool I’ll never use.” But hold your horses, dear developer. This isn’t just some fancy trick to impress your coworkers (though it will). It’s a game-changer for real-world scenarios.

  1. Web Servers: The Request Whisperer

    Picture this: You’re handling a gazillion requests, and you need to keep track of each one. Without async storage, you’d be passing context around like a hot potato. With it? It’s smooth sailing.

    import http from 'node:http';
    import { AsyncLocalStorage } from 'node:async_hooks';
    type RequestContext = {
    requestId: number;
    };
    const asyncLocalStorage = new AsyncLocalStorage<RequestContext>();
    const server = http.createServer((req, res) => {
    asyncLocalStorage.run({} as RequestContext, () => {
    const store = asyncLocalStorage.getStore();
    if (store) {
    store.requestId = Date.now();
    handleRequest(req, res);
    }
    });
    });
    function handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
    const store = asyncLocalStorage.getStore();
    if (store) {
    console.log(`Processing request ${store.requestId}`);
    // ... more request handling logic
    }
    }
    server.listen(3000);

    Your server is now a mind reader, always knowing which request it’s dealing with. Impressive, right?

  2. Logging: The Digital Sherlock

    Logs are the breadcrumbs of the digital world. But what good are they if you can’t tell which crumb belongs to which trail? Async storage to the rescue!

    import { AsyncLocalStorage } from 'node:async_hooks';
    type LogContext = {
    traceId: string;
    };
    const asyncLocalStorage = new AsyncLocalStorage<LogContext>();
    function log(message: string) {
    const store = asyncLocalStorage.getStore();
    if (store) {
    console.log(`[${store.traceId}] ${message}`);
    }
    }
    asyncLocalStorage.run({} as LogContext, () => {
    const store = asyncLocalStorage.getStore();
    if (store) {
    store.traceId = `TRACE-${Date.now()}`;
    log('Operation started');
    setTimeout(() => {
    log('Operation completed');
    }, 100);
    }
    });

    Now your logs are like a well-organized crime scene. Every piece of evidence (log) is tagged and bagged (traced). Sherlock would be proud.

The Art of Crumb-Laying

Now that you’re armed with this magical backpack, you might be tempted to stuff it full of everything you can think of. But hold on there, packrat. Here are some golden rules for using async storage:

  1. Initialize Early, Initialize Often: Set up your async storage at the start of your async context. It’s like packing your backpack before the hike, not halfway up the mountain.

  2. Type It Up: Use TypeScript to define your store data. It’s like labeling the compartments in your backpack. You’ll thank yourself later.

  3. Clean Up Your Crumbs: Clear out sensitive data when you’re done. Don’t leave your trail for just anyone to follow.

  4. Expect the Unexpected: Implement error handling. Sometimes your magical backpack might not be there when you reach for it. Be prepared.

Embrace the Crumbs

Async storage in Node.js isn’t just a feature—it’s a superpower. It allows you to write code that’s not just functional, but contextual. It’s the difference between leaving a trail of breadcrumbs and leaving a trail of GPS coordinates.

Whether you’re building web servers, implementing logging systems, or just trying to make sense of the async chaos, async storage is your new best friend. It’s the tool you didn’t know you needed, but won’t be able to live without.

So go forth, dear developer. Embrace the crumbs. Let your code tell a story, not just execute a function. And remember, in the async woods of Node.js, a well-placed breadcrumb can make all the difference between being lost and being found.

Happy coding, and may your async operations always find their way home!