Error handling and logging are foundational practices for maintaining stability, debugging issues, and understanding the behavior of Node.js applications. Node.js provides built-in error handling capabilities and allows developers to integrate logging systems to capture, log, and analyze errors in both development and production environments.
Error handling is a strategy for managing unexpected conditions in code execution. It helps prevent application crashes, enhances the user experience by providing informative error messages, and allows developers to log errors for debugging and monitoring.
Node.js errors can be broadly categorized as:
try...catch
blocks.Understanding these types helps developers apply the most suitable handling strategy.
Using try...catch
is the primary method for handling synchronous errors.
try {
let result = riskyOperation();
console.log(result);
} catch (error) {
console.error("An error occurred:", error.message);
}
Output: If riskyOperation
throws an error, it will be caught, and an error message will be displayed instead of crashing the program.
Asynchronous operations often involve callbacks, async/await
, or promises.
Callback Pattern: Handle errors by including an error parameter in the callback.
const fs = require('fs');
fs.readFile('nonexistent.txt', 'utf-8', (err, data) => {
if (err) {
console.error("File read error:", err.message);
return;
}
console.log("File content:", data);
});
.catch()
: Promises allow handling errors at the end of the chain with .catch()
.
fetch('https://invalid-url')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Fetch error:", error.message));
async/await
with try...catch
:
async function fetchData() {
try {
const response = await fetch('https://invalid-url');
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Async error:", error.message);
}
}
Creating custom error classes helps in categorizing errors and improving error messages.
class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = "NotFoundError";
this.statusCode = 404;
}
}
try {
throw new NotFoundError("Data not found.");
} catch (error) {
console.error(`${error.name} (${error.statusCode}): ${error.message}`);
}
NotFoundError (404): Data not found.
In Node.js, you can set global error handlers to catch unhandled errors.
process.on('uncaughtException', (error) => {
console.error("Uncaught exception:", error.message);
process.exit(1); // optional
});
process.on('unhandledRejection', (reason, promise) => {
console.error("Unhandled rejection:", reason);
});
Logging involves capturing application events and errors for analysis. Logging in development differs from production:
Popular Node.js logging libraries include:
npm install winston
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' })
]
});
logger.info("This is an info message.");
logger.error("This is an error message.");
logger.info("User accessed homepage.");
logger.warn("User tried to access deprecated endpoint.");
logger.error("Database connection failed.");
{"level":"info","message":"User accessed homepage."}
{"level":"warn","message":"User tried to access deprecated endpoint."}
{"level":"error","message":"Database connection failed."}
const express = require('express');
const winston = require('winston');
const app = express();
const port = 3000;
const logger = winston.createLogger({
level: 'info',
format: winston.format.simple(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'application.log' })
]
});
app.get('/', (req, res) => {
logger.info("Homepage accessed.");
res.send("Welcome to the homepage.");
});
app.get('/error', (req, res) => {
try {
throw new Error("Intentional error");
} catch (error) {
logger.error(error.message);
res.status(500).send("Something went wrong.");
}
});
app.listen(port, () => {
logger.info(`Server running on http://localhost:${port}`);
});
/
logs an info message./error
logs an error message due to the intentional error.Output: If we access /error
, the console and log file display:
{"level":"info","message":"Server running on http://localhost:3000"}
{"level":"info","message":"Homepage accessed."}
{"level":"error","message":"Intentional error"}
Error handling and logging are essential components in building reliable Node.js applications. Proper error handling ensures applications remain stable and recover from unexpected issues. Implementing logging with tools like Winston provides insight into application health and aids in debugging and analysis. Happy Coding!❤️