Master debug logging in Node.js and other apps. Learn best practices, avoid common mistakes, and monitor logs efficiently for faster troubleshooting.

Debug logging enables developers to track what their code is doing while it executes. It involves adding logs (messages) that demonstrate what occurs within the program. These messages help you monitor the app’s operation, verify variable values, and identify potential app trouble spots.

🛠️Want to learn how to quickly identify and fix app performance issues? Check out our guide on Fixing Application Performance Issues for practical tips and tools.

Logging is not just about debugging. We also have different log levels that group messages by their importance. For example:

  • Error logs show serious issues that need fixing
  • Warning logs point out things that might cause big problems.
  • Info logs record normal events like when a service starts or a user successfully logs in.

Identifying issues in your code can be challenging without proper logging. However, through proper logging, developers can keep things running smoothly, identify bugs more quickly, and gain a deeper understanding of how their apps behave.

Table of Contents

Why Debug Logging Matters

Here are some of the main reasons and aims of debug logs that every developer should know about:

  • Debug logs make it easy to find bugs by keeping track of what happens. This will help you identify and correct errors in your code.
  • By displaying every action the program takes while running, debug logs allow you to see how an application flows
  • Debug logs will provide you with specific information that will help you identify the issue when it occurs.
  • Developers can identify issues more quickly and more effectively by using the detailed information provided by debug logs.

Implementing Debug Logging

Different methods exist for debug logging. The approach you take depends on the project size and control needs.

Prerequisite:

A basic knowledge will help you understand the code. Also, install Node.js and npm on your computer.

Node.js App:


const express = require('express');
const app = express();
const PORT = 3000;

// user database.
const users = {
  1: { id: 1, name: 'Jane Doe', email: '[email protected]' },
  2: { id: 2, name: 'John Smith', email: '[email protected]' },
};

app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
  const user = users[userId];

  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  res.json(user);
});

app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

High-traffic Node.js applications can experience error spikes that affect users.

⚡ Reduce errors and boost performance in your Node.js apps! Learn how to monitor, detect, and resolve issues in high-traffic applications before they affect your users.

1. Using console.debug()

You don’t have to set up console.debug() for it to work. It does the same thing as console.log(). It works well for minor projects and learning.

Example:


app.get('/user/:id', (req, res) => {
  const userId = req.params.id;

  console.debug(`Request received for user ID: ${userId}`);

  const user = users[userId];

  if (!user) {
    console.error(`User with ID ${userId} not found`);
    return res.status(404).json({ error: 'User not found' });
  }

  console.debug(`User found:`, user);
  res.json(user);
});

Run the app:


Node index.js 

Go to /user/1 route using Postman or your browser.  The debug messages will appear on your terminal.

Terminal output showing debug log messages for /user/1 request in Node.js Express app

When to use console.debug():

  • For quick tests or very small scripts.
  • When you don’t need log filtering, log formatting, or saving logs to files.
  • In small or personal projects.

2. Using Winston

Winston is a popular logging tool. It gives you more control with log levels, formatting, and transports (like files or external services), and more

1. First, install Winston:


npm install winston

2. Set up Winston:


const winston = require('winston');

const logger = winston.createLogger({
  level: 'debug',
  format: winston.format.simple(),
  transports: [
    new winston.transports.File({ filename: 'app.log' })
  ]
});

Let’s understand this:

  • const logger = winston.createLogger({...})” create a new logger instance.
  • level: ‘debug’” will capture logs from debug level.
  • format: winston.format.simple()” makes logs readable in plain text.
  • transports” sends logs to app.log instead of the console.

3. Include Winston logs in your application:


app.get('/user/:id', (req, res) => {
  const userId = req.params.id;

  logger.debug(`Request received for user ID: ${userId}`);

  const user = users[userId];

  if (!user) {
    console.error(`User with ID ${userId} not found`);
    return res.status(404).json({ error: 'User not found' });
  }

  logger.debug(`User found:`, user);
  res.json(user);
});

Output:

Example of Winston logger writing debug messages to app.log file in a Node.js application.

All log messages are written to app.log.

When to use:

  • In a production environment.
  • When you need structured logging and log files
  • When your team needs consistent and readable logs across the app.

3. Using Middleware to Monitor and Analyze Logs

Winston allows changing the log format and level, and sending logs to the console or local storage. However, finding and combining logs from different environments with other system events is still necessary, especially in production.

That’s where Middleware comes in. It takes the logs and organizes them into a format that can be searched and used by multiple people.

With Middleware, you can:

  • Collect logs from multiple sources
  • Monitor live activities through dashboards
  • Filter and search logs.

Set Up and Configuration in Node.js

To track and analyze logs using Middleware in Node.js application, follow these steps:

1. Install the Middleware APM SDK

Install Middleware Node.js SDK via npm:


npm install @middleware.io/node-apm --save

2. Initialize Middleware in Your Application

At the top of your entry file (e.g., index.js), initialize the Middleware tracker with your service name and API key. This connects your application to the Middleware platform.


const tracker = require('@middleware.io/node-apm');

tracker.track({
    serviceName: "",
    accessToken: "",
    target: "https://ccang.middleware.io",
});

Replace <SERVICE_NAME> with your desired name and <API_KEY> with your Middleware API key.

3. Logging Using Middleware

Middleware’s logging capabilities enable a direct transfer of logs to the platform, with varying levels of severity.

Example:


app.get('/user/:id', (req, res) => {
  const userId = req.params.id;

  tracker.debug(`Request received for user ID: ${userId}`);

  const user = users[userId];

  if (!user) {
    console.error(`User with ID ${userId} not found`);
    return res.status(404).json({ error: 'User not found' });
  }

    tracker.debug(`User found:`, user);
  res.json(user);
});

Finally, start your server as usual:


node index.js 

Viewing and Analyzing Logs on Middleware

After sending requests to your application, go to the Middleware Dashboard:

1. Navigate to the Logs or APM section.

Middleware dashboard showing Logs and APM section for monitoring Node.js application activity.

2. Use filters to view logs by:

  • Severity (debug, error, etc.)
  • Time range
  • Service or route
Middleware dashboard filters showing options to view logs by severity, time range, and service route.

3. Click any log entry to inspect its metadata and JSON payload.

Middleware dashboard showing detailed log entry with metadata and JSON payload

You can still set up alerts for error spikes or performance issues.

Use Case: Finding and fixing performance problems with APIs

What if the /user/:id The endpoint in your staging environment suddenly started to slow down? You can use Middleware to filter debug logs for that route.

When you look at the logs, you see that the app only slows down when you request another service. Maybe taking longer than 2 seconds to respond.

You can set up an alert in Middleware to make sure that delays like these don’t go unnoticed again.

⏱️ Try Debugging Logs Now

Get started with Middleware in under a minute, no credit card required!

For instance:

“Let me know if response times for /user/:id go over 2 seconds more than 5 times in a minute.”

You’ll find the issues next time before they worsen or are exploited.

Common Mistakes to Avoid

There are some mistakes you’ll make that can reduce its entire purpose or even cause problems. Avoiding these mistakes will make you create more effective logs.

1. Over-logging Every Single Line

Your logs may become overflowing with information if you log every single operation. This makes it challenging to figure out the actual issue. Performance may also be impacted by excessive resource usage.

For instance:


app.get('/user/:id', (req, res) => {
  console.debug('Entered /user/:id route');
 
  const userId = req.params.id;
  console.debug(`Extracted userId: ${userId}`);
 
  console.debug('Attempting to access users object');
  const user = users[userId];
  console.debug(`User lookup complete: ${JSON.stringify(user)}`);
 
  if (!user) {
    console.error(`User with ID ${userId} not found`);
    return res.status(404).json({ error: 'User not found' });
  }

  console.debug('Preparing response...');
  res.json(user);
  console.debug('Response sent');
});

Record just the crucial actions, significant variable values, or unforeseen circumstances. Pay attention to the important details that enable you to understand the program’s flow.

2. Logging After the Error Has Already Happened

Many people only write logs after an error, so they miss the important steps that were taken before the error occurred. This way, you can’t determine what went wrong.


app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
  const user = users[userId];

  if (!user) {
    console.debug('User not found');
    return res.status(404).json({ error: 'User not found' });
  }

  res.json(user);
});

This tells you nothing about which user ID was requested or whether the code ran properly. Instead, you can insert logs before and during risky operations to capture the state and inputs that cause issues.

3. Using Debug Logs in Production Without Any Filters

You shouldn’t keep debug logs on in production without any filters. It can cause performance issues, generate a large number of logs, and sometimes display confidential information.

Debug logging should only be enabled for a short period or for specific parts as needed, and the appropriate log levels should be configured for production.

4. Unclear Messages

It is challenging to determine the issue when logs are not appropriately formatted or contain insufficient information, such as user IDs and request IDs.

Use well-organized logging with clear messages and information about the issue.


logger.debug("User authentication failed", extra={"user_id": user.id, "reason": "invalid password"})

Best Practices for Efficient Debug Logging

Your logs will be simple to read and maintain if you adhere to recommended practices. It enables you to fix problems faster without compromising system performance or security.

1. Be Descriptive

Your debug message should clearly tell what your code is doing. Include critical information, such as the name of the currently running function and the values of relevant variables.

Example:


logger.debug(`Processing order #${order.id} for user ${user.id}`);

This message clearly explains what the program is doing and for whom, facilitating the identification of issues related to specific data.

2. Keep your Messages Short and Simple

Your log messages should be descriptive, yet concise and straightforward. When logs get huge, it can be challenging to read the messages. Aim for concise language that conveys the primary concept without adding unnecessary information.

Example:


// Good: clear and concise
logger.debug(`User login succeeded: ${user.username}`);

// Avoid: verbose messages
logger.debug(`User login attempt was successful for username: ${user.username} at time ${new Date().toISOString()}`);

Use other parts of the logging system (such as timestamps) rather than including everything in the message.

3. Use Consistent Formatting

You must be consistent so that your logs are easy to read and parse. It is best to utilize a consistent message style and include similar types of information in a specific order, such as operation names, IDs, or error codes.

Structured logging (like logging in JSON) can help search and analyze logs programmatically.

Example of consistent formatting:


logger.debug(`Order processed`, {
  orderId: order.id,
  userId: user.id,
  total: order.totalAmount
});

4. Avoid Logging Sensitive Data

Always consider security when building. Passwords, credit card numbers, authentication tokens, and other private user information should never be logged. Logs should be deleted to prevent privacy issues, as they may be retained for an extended period and accessed by multiple teams.

5. Don’t Overload Logs

We discussed this earlier while talking about the mistakes you shouldn’t make. Too many logs can overwhelm both developers and system resources. Don’t log everything. Focus on meaningful events and avoid logging inside very tight loops.

Conclusion

In conclusion, debug logging is important when it comes to building reliable applications. It helps you trace issues, understand app behavior, and make troubleshooting faster, especially in complex projects.

You should start using debug logs during development if you haven’t already. Middleware also adds value by centralizing and analyzing your logs on a large scale.

🛠️ Debug Smarter, Not Harder!

Stop hunting through scattered logs and guesswork. With Middleware, you can centralize all your debug logs, spot errors faster, and understand exactly what your code is doing in real time.

FAQ

What is debug logging?

Debug logging captures messages regarding what your app is doing. It allows you to understand the code better and identify mistakes. It is very beneficial during development and testing.

How is Debug Logging Different From Error or Info Logging?

Debug logs record general events that show regular operation.

Info logs record general events that show normal operation.

Error logs highlight serious problems that need fixing.

What Should I Avoid Logging at the Debug Level?

Avoid logging sensitive data or information that is not too important. Only log details that help with troubleshooting.

Does Debug Logging Affect Application Performance?

Yes! It can, especially when you log too much. Debug logs are typically blocked or filtered by log level in production.

How Can I Use Debug Logs to Troubleshoot Issues?

Check the debug logs to follow what your app was doing before a problem happened. The detailed messages help you trace steps and find out exactly where or why something went wrong.

📚 Also Read:

  • Latency Reduction: Strategies and best practices for reducing latency and optimizing performance in your applications.
  • 🐍 Python Logging: Learn how to implement effective logging in Python applications.
  • ☸️ Kubernetes Logging: Discover best practices for logging and monitoring Kubernetes clusters.
  • 🛡️ Audit Logs: Understand the role of audit logs in tracking system events, ensuring compliance, and maintaining operational integrity.
  • 🔍 Search Logs Effectively with Log Management: Learn strategies for efficiently searching and filtering logs using log management tools.
  • 📘 Application Logs: Explore the importance of application logs in monitoring user interactions and understanding system behavior.