Tutorials Logic, IN info@tutorialslogic.com

Node.js Console, Logging, Inspection, and Debug Output

What is the Node.js Console?

The Node.js console module provides a simple debugging console similar to the JavaScript console mechanism provided by web browsers. It's a global object that can be accessed from anywhere in your Node.js application without requiring an explicit import. The console module writes to stdout (standard output) and stderr (standard error), making it essential for logging, debugging, and monitoring application behavior.

While the console is primarily used during development for debugging purposes, it's also commonly used in production for logging important events, errors, and system information. However, for production applications, it's recommended to use dedicated logging libraries like Winston, Bunyan, or Pino for more advanced features like log levels, file rotation, and structured logging.

Basic Console Methods

The console module provides several methods for different types of output. Here are the most commonly used methods:

Method Description Output Stream
console.log() Prints standard output with a newline stdout
console.info() Alias for log() - informational messages stdout
console.error() Prints error messages to standard error stderr
console.warn() Alias for error() - warning messages stderr
console.debug() Alias for log() - debug messages stdout

Basic Console Methods - JavaScript Example

Basic Console Methods - JavaScript Example
// Basic logging

console.log('Hello, Node.js!');

console.log('User logged in:', 'alice@example.com');



// Multiple arguments

console.log('Name:', 'Alice', 'Age:', 25, 'Active:', true);

// Output: Name: Alice Age: 25 Active: true



// String substitution (printf-style)

console.log('User %s is %d years old', 'Bob', 30);

// Output: User Bob is 30 years old



// Informational message

console.info('Server started on port 3000');



// Warning message

console.warn('API rate limit approaching');



// Error message

console.error('Database connection failed');



// Debug message

console.debug('Request headers:', { 'Content-Type': 'application/json' });

Advanced Console Methods

The console.dir() method uses util.inspect() to print a detailed representation of an object, showing all properties including non-enumerable ones.

The console.table() method displays tabular data as a table, making it easy to visualize arrays and objects.

These methods measure the execution time of code blocks, useful for performance profiling and optimization.

The console.trace() method prints a stack trace showing the call path to the current point in the code, useful for debugging complex call chains.

The console.assert() method writes an error message to the console if the assertion is false. If the assertion is true, nothing happens.

These methods count how many times a particular label has been called, useful for tracking function calls or loop iterations.

These methods create collapsible groups in the console output, making it easier to organize related log messages.

console.dir() Examples

console.dir() Examples
const user = {

    name: 'Alice',

    age: 25,

    address: {

        city: 'New York',

        country: 'USA'

    },

    hobbies: ['reading', 'coding', 'gaming']

};



// console.log() shows a simple representation

console.log(user);



// console.dir() shows more details with options

console.dir(user, { depth: null, colors: true });



// Inspect with custom depth

const nested = { a: { b: { c: { d: { e: 'deep' } } } } };

console.dir(nested, { depth: 3 });  // Shows up to 3 levels



// Show hidden properties

console.dir(user, { showHidden: true });

console.table() Examples

console.table() Examples
// Array of objects

const users = [

    { id: 1, name: 'Alice', role: 'Admin' },

    { id: 2, name: 'Bob', role: 'User' },

    { id: 3, name: 'Charlie', role: 'Moderator' }

];



console.table(users);

/*

+---------+----+-----------+-------------+
| (index) | id |   name    |    role     |
+---------+----+-----------+-------------+
|    0    | 1  |  'Alice'  |  'Admin'    |
|    1    | 2  |   'Bob'   |   'User'    |
|    2    | 3  | 'Charlie' | 'Moderator' |
+---------+----+-----------+-------------+

*/


// Display specific columns

console.table(users, ['name', 'role']);



// Simple array

const fruits = ['Apple', 'Banana', 'Orange'];

console.table(fruits);



// Object

const config = {

    host: 'localhost',

    port: 3000,

    database: 'myapp'

};

console.table(config);

Timing Operations

Timing Operations
// Basic timing

console.time('loop');

for (let i = 0; i < 1000000; i++) {

    // Some operation

}

console.timeEnd('loop');

// Output: loop: 5.234ms



// Multiple timers

console.time('database-query');

console.time('api-call');



// Simulate async operations

setTimeout(() => {

    console.timeEnd('database-query');

    // Output: database-query: 100.123ms

}, 100);



setTimeout(() => {

    console.timeEnd('api-call');

    // Output: api-call: 200.456ms

}, 200);



// console.timeLog() - intermediate timing

console.time('process');

// Do some work

console.timeLog('process', 'Step 1 complete');

// Do more work

console.timeLog('process', 'Step 2 complete');

// Finish

console.timeEnd('process');



// Practical example: comparing algorithms

console.time('Array.push');

const arr1 = [];

for (let i = 0; i < 100000; i++) {

    arr1.push(i);

}

console.timeEnd('Array.push');



console.time('Array[i]');

const arr2 = [];

for (let i = 0; i < 100000; i++) {

    arr2[i] = i;

}

console.timeEnd('Array[i]');

Stack Traces

Stack Traces
function first() {

    second();

}



function second() {

    third();

}



function third() {

    console.trace('How did we get here?');

}



first();

/*

Output:

Trace: How did we get here?

    at third (trace.js:10:13)

    at second (trace.js:6:5)

    at first (trace.js:2:5)

    at Object.<anonymous> (trace.js:13:1)

*/



// Practical use: debugging middleware chain

function middleware1(req, res, next) {

    console.trace('Middleware 1');

    next();

}



function middleware2(req, res, next) {

    console.trace('Middleware 2');

    next();

}

Assertions

Assertions
const age = 15;



// Assertion passes - no output

console.assert(age >= 0, 'Age must be positive');



// Assertion fails - prints error

console.assert(age >= 18, 'User must be 18 or older');

// Output: Assertion failed: User must be 18 or older



// Multiple arguments

const user = { name: 'Alice', role: 'user' };

console.assert(user.role === 'admin', 'User is not admin:', user);



// Practical use: validating function inputs

function divide(a, b) {

    console.assert(b !== 0, 'Division by zero!');

    return a / b;

}



divide(10, 2);  // No output

divide(10, 0);  // Assertion failed: Division by zero!



// Note: console.assert() does NOT throw an error

// It only logs to console - execution continues

Counting Operations

Counting Operations
// Basic counting

console.count('clicks');  // clicks: 1

console.count('clicks');  // clicks: 2

console.count('clicks');  // clicks: 3



// Different labels

console.count('api-calls');   // api-calls: 1

console.count('db-queries');  // db-queries: 1

console.count('api-calls');   // api-calls: 2



// Reset counter

console.countReset('clicks');

console.count('clicks');  // clicks: 1



// Practical example: tracking function calls

function processUser(user) {

    console.count('processUser called');

    // Process user...

}



processUser({ name: 'Alice' });  // processUser called: 1

processUser({ name: 'Bob' });    // processUser called: 2

processUser({ name: 'Charlie' }); // processUser called: 3



// Track different event types

function handleEvent(type) {

    console.count(`event-${type}`);

}



handleEvent('click');    // event-click: 1

handleEvent('scroll');   // event-scroll: 1

handleEvent('click');    // event-click: 2

Grouping Console Output

Grouping Console Output
// Basic grouping

console.group('User Details');

console.log('Name: Alice');

console.log('Age: 25');

console.log('Role: Admin');

console.groupEnd();



// Nested groups

console.group('Application Startup');

console.log('Loading configuration...');



console.group('Database Connection');

console.log('Host: localhost');

console.log('Port: 5432');

console.log('Status: Connected');

console.groupEnd();



console.group('Server Initialization');

console.log('Port: 3000');

console.log('Status: Listening');

console.groupEnd();



console.groupEnd();



// Collapsed group (groupCollapsed)

console.groupCollapsed('Debug Info');

console.log('This group starts collapsed');

console.log('Click to expand');

console.groupEnd();



// Practical example: API request logging

function logApiRequest(method, url, data) {

    console.group(`${method} ${url}`);

    console.log('Timestamp:', new Date().toISOString());

    console.log('Headers:', { 'Content-Type': 'application/json' });

    console.log('Body:', data);

    console.groupEnd();

}



logApiRequest('POST', '/api/users', { name: 'Alice', email: 'alice@example.com' });

Console Method Reference

Method Description Use Case
log() Standard output General logging
info() Informational messages System events
warn() Warning messages Potential issues
error() Error messages Errors and exceptions
debug() Debug messages Development debugging
dir() Object inspection Detailed object viewing
table() Tabular data display Arrays and objects
time() Start timer Performance measurement
timeEnd() End timer Performance measurement
timeLog() Intermediate timing Multi-step timing
trace() Stack trace Debugging call chains
assert() Conditional error Validation checks
count() Count occurrences Tracking calls
countReset() Reset counter Resetting counts
group() Start group Organizing output
groupEnd() End group Organizing output
clear() Clear console Cleaning output

Best Practices

  • Use appropriate log levels - Use error() for errors, warn() for warnings, info() for information
  • Remove console logs in production - Or use environment variables to control logging
  • Use structured logging - Consider using logging libraries like Winston or Pino for production
  • Don't log sensitive data - Never log passwords, API keys, or personal information
  • Use console.table() for arrays - Makes data much easier to read
  • Use console.time() for performance - Identify bottlenecks in your code
  • Group related logs - Use console.group() to organize complex output

Practical Example: Complete Logging System

Custom Logger

Custom Logger
// Simple custom logger with timestamps

class Logger {

    constructor(name) {

        this.name = name;

    }



    _timestamp() {

        return new Date().toISOString();

    }



    log(message, ...args) {

        console.log(`[${this._timestamp()}] [${this.name}] [LOG]`, message, ...args);

    }



    info(message, ...args) {

        console.info(`[${this._timestamp()}] [${this.name}] [INFO]`, message, ...args);

    }



    warn(message, ...args) {

        console.warn(`[${this._timestamp()}] [${this.name}] [WARN]`, message, ...args);

    }



    error(message, ...args) {

        console.error(`[${this._timestamp()}] [${this.name}] [ERROR]`, message, ...args);

        console.trace();

    }



    table(data) {

        console.log(`[${this._timestamp()}] [${this.name}] [TABLE]`);

        console.table(data);

    }



    time(label) {

        console.time(`[${this.name}] ${label}`);

    }



    timeEnd(label) {

        console.timeEnd(`[${this.name}] ${label}`);

    }

}



// Usage

const logger = new Logger('MyApp');



logger.info('Application started');

logger.log('Processing user request');

logger.warn('API rate limit approaching');

logger.error('Database connection failed');



logger.time('database-query');

// Simulate query

setTimeout(() => {

    logger.timeEnd('database-query');

}, 100);



const users = [

    { id: 1, name: 'Alice', status: 'active' },

    { id: 2, name: 'Bob', status: 'inactive' }

];

logger.table(users);

Console Destinations

The global console writes ordinary output to `process.stdout` and warnings or errors to `process.stderr`. The `Console` class creates another logger backed by streams you choose, which is useful for tests, files, or split destinations. Decide the destination by audience: command output, diagnostics, and machine-readable records should not be mixed casually.

Console methods are not consistently synchronous or asynchronous. Their behavior depends on the backing stream and platform. Do not assume a log call has reached disk before process exit, and do not place high-volume console output on a latency-sensitive request path without measuring backpressure and destination behavior.

For command-line programs, keep usable output on stdout so it can be piped, and keep diagnostics on stderr. Respect stream write errors and broken pipes. A CLI that prints banners into structured stdout breaks callers just as surely as malformed JSON.

  • Separate normal output from diagnostics.
  • Do not depend on one universal console timing behavior.
  • Use custom Console instances for controlled streams.
  • Keep machine-readable stdout free of decorative messages.

Debugging Methods

`log`, `info`, `warn`, and `error` differ mainly in intent and destination conventions. `dir` exposes inspection options, `table` helps scan tabular values, `trace` includes a stack, `count` tracks labeled occurrences, and `time`, `timeLog`, and `timeEnd` measure a labeled interval. Use stable labels so output can be compared across runs.

`console.assert()` reports when its expression is false; it is not an application invariant or test framework replacement. Logging an assertion does not necessarily stop unsafe execution. Throw or return a domain error when the program cannot continue, and use `node:test` assertions for automated verification.

Avoid logging the same error at every layer. Add context once at the boundary that owns it, preserve the original cause, and let the top-level request or worker handler record the final outcome. Duplicate stack traces increase cost and hide the first useful signal.

  • Choose methods and labels that communicate diagnostic intent.
  • Use timers for local investigation, not full production metrics.
  • Do not treat console.assert as control flow.
  • Log an error once with cause and request context.

Structured Production Logs

Production logs should be structured records with timestamp, severity, service and release identity, event name, request or trace ID, safe dimensions, and error classification. A human message can remain, but fields let systems filter and aggregate without parsing prose. Keep field names and units stable.

Redact authorization headers, cookies, passwords, tokens, database URLs, payment data, and private request bodies before they reach the logger. Masking after formatting may miss nested or copied values. Adopt an allowlist for high-risk events and set retention and access according to the data classification.

Use logs for discrete events, metrics for aggregates, and traces for causality across operations. Printing every object or request creates expensive noise and can block streams under load. Sample high-volume success events while retaining errors and security decisions according to policy.

  • Emit stable structured fields and correlation IDs.
  • Redact before serialization and transport.
  • Do not use logs as a substitute for metrics and traces.
  • Control volume, sampling, retention, and access.

Failure Diagnostics

Install top-level handling that records an unexpected failure, begins graceful shutdown, and exits nonzero. An uncaught exception leaves application state uncertain; logging and continuing can serve corrupt or partially initialized behavior. Unhandled promise rejections are fatal by default in current Node behavior when not handled according to process settings.

Preserve `Error` name, message, code, stack, and cause without exposing them to clients. Generate diagnostic reports or heap and CPU evidence through controlled operational procedures when ordinary logs are insufficient. Debug interfaces and reports can reveal secrets and must be access-restricted.

Test logging under destination failure, disk pressure, a closed pipe, rapid process termination, and very large objects. The error path is exactly where asynchronous buffers and dependencies are least reliable, so keep the fallback small and avoid recursive logging failures.

  • Exit after uncaught fatal state instead of continuing blindly.
  • Preserve error cause and safe operational context.
  • Restrict diagnostic reports and inspector access.
  • Test logger behavior when its destination fails.

Log Contract Verification

Treat important log events as an interface. Test the event name, severity, correlation field, error classification, and redaction result without asserting an entire timestamped line. Inject a destination or capture stream so tests do not replace the global console for unrelated code.

Exercise serialization failures, circular values, a destination that returns backpressure, and a destination that emits an error. Decide which records may be dropped and what minimal fallback is available. Logging must not recursively trigger the same failing logger or turn one request error into a process-wide outage.

Review a sample from the real collector after deployment. Local output can look correct while transport parsing, multiline stacks, field limits, or retention rules alter it. Confirm that an operator can find one request, distinguish a client rejection from a server defect, and follow the documented correlation fields without seeing protected data.

  • Assert stable fields instead of complete rendered lines.
  • Test redaction and destination failure behavior.
  • Prevent recursive failures in the logging path.
  • Verify records after the production collection pipeline.
Before you move on

Node.js Console Debugging Logging Mastery Check

5 checks
  • The Node.js console module provides a simple debugging console similar to the JavaScript console mechanism provided by web browsers.
  • It's a global object that can be accessed from anywhere in your Node.js application without requiring an explicit import.
  • The console module writes to stdout (standard output) and stderr (standard error), making it essential for logging, debugging, and monitoring application behavior.
  • While the console is primarily used during development for debugging purposes, it's also commonly used in production for logging important events, errors, and system information.
  • However, for production applications, it's recommended to use dedicated logging libraries like Winston, Bunyan, or Pino for more advanced features like log levels, file rotation, and structured logging.

Node JS Questions Learners Ask

stdout and stderr are separate streams. Normal program output belongs on stdout, while errors and diagnostics belong on stderr.

console.table is helpful when you need to inspect an array of similar objects, such as users, requests, rows, or configuration entries. It makes missing fields and unexpected values easier to spot than a long JSON dump. It is less useful for deeply nested objects or high-volume production logs.

console.time measures elapsed wall-clock time between console.time and console.timeEnd. If you start a timer, launch async work, and end the timer before awaiting or handling the callback, the measurement only covers scheduling, not completion.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.