Building a Lightweight Logging System for Node.js Apps
Half a developer’s career is spent debugging the code they write in the other half of their career. This makes it crucial to simplify the debugging process. We dedicate significant time brainstorming and building solutions to make debugging easier.
A major part of debugging involves examining logs. To make this task manageable, we aim to create logs that are easier to read and navigate. In my opinion, logs should be both detailed and structured while remaining engaging and intuitive.
While there are many logging packages available in the market, I prefer not to burden my projects with heavy dependencies. Instead, I developed a lightweight logging system tailored to my project’s specific requirements. While it may not be a universal solution, it is adaptable and can be modified to suit other use cases.
Let’s first examine the structure of my project before diving into the design of the logging system. Chances are, your project may follow a similar pattern.
Project Structure
My project is organized into levels:
- Level 0: System logs such as server start, external connections, and cluster spinning.
- Level 1: Callable functions, endpoints, and services.
- Level 2: The facade layer, where the main logic integrates all components.
- Level 3: The core logic layer where actual operations take place.
Logging Requirements
Tabbed Display for Log Levels
Logs from different levels should appear in separate tabs to enhance readability.
Visual Separation Using Colors
Logs should be visually distinct. For instance, if two functions are called consecutively, their logs should have different colors.
Error logs should be given priority and are displayed in red for immediate attention.
Symbolic Differentiation
To further distinguish log types, symbols or emojis can be added. These visual cues make logs more engaging and easier to identify at a glance. I got the emojis from here, you can use whatever you need.
Implementation
Here’s a snippet of code showcasing how to implement this system. Feel free to adapt it to your needs:
export const logger = (message, level = 0, color = 'Cyan', error = false) => {
const symbol = error ? SYMBOLS["error"] : level == MAX_LEVEL ? SYMBOLS["pointer"] : SYMBOLS["done"];
const newlines = '\n'.repeat(MAX_LEVEL - level);
const tabs = '\t'.repeat(level);
console.log(`\x1b[${COLORS[error ? 'Red' : color]}m ${newlines}${tabs} ${symbol} ${message} \x1b[0m`);
}
const MAX_LEVEL = 3;
const COLORS = {
"Black": "30",
"Red": "31",
"Green": "32",
"Yellow": "33",
"Blue": "34",
"Magenta": "35",
"Cyan": "36",
"White": "37"
}
const SYMBOLS = {
"pointer": "➜",
"done": "✔️",
"success": "🎉",
"error": "🧯",
"warning": "😡"
}
const BG_COLORS = {
"Black": "40",
"Red": "41",
"Green": "42",
"Yellow": "43",
"Blue": "44",
"Magenta": "45",
"Cyan": "46",
"White": "47"
}
logger("App is running")
Conclusion
This lightweight logging system helps streamline debugging by introducing structure, color coding, and symbolic differentiation. By organizing logs into levels, providing visual cues, and adding personality through emojis, you can make debugging less of a chore and more of a manageable, even enjoyable, task.
Feel free to modify and expand this system to fit your project’s needs. Happy debugging!