JavaScript Console Methods: log, warn & errors
Master the JavaScript console API beyond console.log. Learn console.warn, console.error, console.table, console.time, and other essential methods with practical examples for effective debugging.
Most developers use console.log() as their primary debugging tool. Drop a log statement, check the output, repeat. While that approach works for simple cases, the Console API provides over a dozen methods designed for specific debugging scenarios: formatting output, timing execution, grouping related messages, asserting conditions, and displaying data as interactive tables.
This tutorial covers every console method you will actually use in real projects, organized from the essentials (log, warn, error) to the specialized methods that save time when debugging complex data structures and performance issues.
The Three Core Methods: log, warn, and error
These are the methods you will use most often. Each produces output with different visual styling and filtering behavior in Chrome DevTools (and other browser consoles).
console.log()
The most basic output method. Prints a message to the console with no special formatting:
const user = { name: 'Alice', role: 'admin', lastLogin: '2026-03-01' };
console.log('User loaded:', user);
// Output: User loaded: {name: 'Alice', role: 'admin', lastLogin: '2026-03-01'}
console.log('Items in cart:', 3, '| Total:', '$47.99');
// Output: Items in cart: 3 | Total: $47.99console.log() accepts any number of arguments. Objects are displayed as expandable trees in the browser console, which makes them inspectable without converting to strings first.
console.warn()
Prints a message styled as a warning: yellow background, warning icon. More importantly, console.warn() messages are filterable in DevTools. You can show only warnings, hide them, or search for them:
function connectToDatabase(config) {
if (!config.ssl) {
console.warn('Database connection is not using SSL. This is insecure in production.');
}
if (config.poolSize > 50) {
console.warn('Connection pool size', config.poolSize, 'exceeds recommended maximum of 50.');
}
// ... continue with connection
}
connectToDatabase({ host: 'localhost', port: 5432, ssl: false, poolSize: 100 });Use console.warn() when something is not an error but should be addressed: deprecated API usage, missing optional configuration, approaching a limit, or using a fallback value.
console.error()
Prints a message styled as an error: red background, error icon, and (in most browsers) includes a stack trace showing where the error was logged:
async function fetchUserProfile(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
console.error(
'Failed to fetch user profile.',
'Status:', response.status,
'User ID:', userId
);
return null;
}
return await response.json();
} catch (networkError) {
console.error('Network error fetching user profile:', networkError);
return null;
}
}console.error() outputs to stderr in Node.js (while console.log() and console.warn() go to stdout), which matters for log aggregation systems that process these streams separately.
Comparison: log vs warn vs error
| Method | Visual Style | Filtering | Stack Trace | Node.js Stream | Use Case |
|---|---|---|---|---|---|
console.log() | Default (no color) | "Info" level | No | stdout | General debugging output |
console.warn() | Yellow background | "Warning" level | Yes (some browsers) | stdout | Non-critical issues, deprecations |
console.error() | Red background | "Error" level | Yes | stderr | Errors, failures, exceptions |
Formatting Console Output
String Substitution
The console supports C-style format specifiers in the first argument:
const productName = 'Wireless Keyboard';
const price = 49.99;
const inStock = true;
console.log('Product: %s | Price: $%f | In Stock: %o', productName, price, inStock);
// Output: Product: Wireless Keyboard | Price: $49.99 | In Stock: true
console.log('Items found: %d', 42);
// Output: Items found: 42| Specifier | Description | Example |
|---|---|---|
%s | String | console.log('%s', 'hello') |
%d or %i | Integer | console.log('%d items', 5) |
%f | Float | console.log('$%f', 9.99) |
%o | Optimally formatted object | console.log('%o', {a: 1}) |
%O | Generic JavaScript object | console.log('%O', document.body) |
%c | CSS styling | console.log('%cStyled', 'color: red') |
CSS Styling with %c
You can apply CSS styles to console output, which is useful for creating visually distinct log categories:
console.log(
'%c PRODUCTION WARNING %c API rate limit approaching threshold',
'background: #ff6600; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold;',
'color: #ff6600; font-weight: normal;'
);
console.log(
'%c SUCCESS %c User authentication completed in 234ms',
'background: #22c55e; color: white; padding: 2px 6px; border-radius: 3px;',
'color: #22c55e;'
);
console.log(
'%c DEBUG %c Cache miss for key: user_profile_12345',
'background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px;',
'color: #6366f1;'
);Each %c resets the style, so you can style different parts of a message independently.
Data Display Methods
console.table()
Displays arrays and objects as interactive, sortable tables. This is invaluable for inspecting structured data:
const inventory = [
{ product: 'Mechanical Keyboard', price: 149.99, stock: 23, category: 'Peripherals' },
{ product: 'USB-C Hub', price: 39.99, stock: 156, category: 'Accessories' },
{ product: '27" Monitor', price: 349.99, stock: 8, category: 'Displays' },
{ product: 'Webcam HD', price: 79.99, stock: 42, category: 'Peripherals' },
];
console.table(inventory);
// Displays a formatted table with (index), product, price, stock, category columnsYou can select specific columns to display:
// Show only product and price columns
console.table(inventory, ['product', 'price']);console.table() also works with objects (keys become the row index):
const serverMetrics = {
cpu: { usage: '62%', cores: 8, temp: '71C' },
memory: { usage: '78%', total: '32GB', free: '7GB' },
disk: { usage: '45%', total: '500GB', free: '275GB' },
};
console.table(serverMetrics);console.dir()
Displays an object as an expandable tree with all its properties, including non-enumerable ones. While console.log() also shows objects interactively in most browsers, console.dir() is specifically designed for DOM elements:
const element = document.querySelector('#app');
console.log(element); // Shows the HTML representation
console.dir(element); // Shows the JavaScript object with all propertiesFor plain objects, console.dir() accepts an options parameter in Node.js:
// Node.js — show nested objects to 5 levels deep
console.dir(complexObject, { depth: 5, colors: true });console.dirxml()
Displays an element as XML/HTML markup. This is the default behavior of console.log() for DOM elements in most browsers:
console.dirxml(document.querySelector('nav'));
// Displays the full HTML tree of the nav elementAssertion and Conditional Logging
console.assert()
Writes an error message only if the first argument is falsy. This is cleaner than wrapping console.error() in if statements:
function processPayment(order) {
console.assert(order.total > 0, 'Order total must be positive. Got:', order.total);
console.assert(order.items.length > 0, 'Order must have at least one item');
console.assert(order.paymentMethod, 'Payment method is required');
// If assertions fail, they print errors but do NOT stop execution
// Processing continues regardless
return submitToGateway(order);
}
processPayment({ total: 0, items: [], paymentMethod: null });
// Console shows three error messages (one per failed assertion)Assertions Do Not Throw
Unlike assert in testing frameworks, console.assert() does not throw an error or stop execution. It only logs a message when the condition is falsy. Never rely on it for error handling in production code.
console.count() and console.countReset()
Counts how many times a particular label has been logged. Useful for tracking how often a function runs or an event fires:
function handleClick(buttonId) {
console.count(buttonId);
// First call with 'submit': "submit: 1"
// Second call with 'submit': "submit: 2"
// First call with 'cancel': "cancel: 1"
}
handleClick('submit');
handleClick('submit');
handleClick('cancel');
handleClick('submit');
// Output:
// submit: 1
// submit: 2
// cancel: 1
// submit: 3
console.countReset('submit'); // Resets the 'submit' counter to 0Timing Methods
console.time(), console.timeLog(), and console.timeEnd()
Measures elapsed time between starting and ending a named timer. This is simpler and more readable than manual Date.now() subtraction:
console.time('dataProcessing');
const rawData = generateLargeDataset(100000);
console.timeLog('dataProcessing', 'Dataset generated');
// Output: dataProcessing: 142.3ms Dataset generated
const filtered = rawData.filter(item => item.active);
console.timeLog('dataProcessing', 'Filtering complete');
// Output: dataProcessing: 198.7ms Filtering complete
const sorted = filtered.sort((a, b) => b.priority - a.priority);
console.timeEnd('dataProcessing');
// Output: dataProcessing: 234.1msKey behaviors:
console.time(label)starts a timerconsole.timeLog(label, ...data)prints the elapsed time without stopping the timer (can be called multiple times)console.timeEnd(label)prints the elapsed time and removes the timer- You can run multiple named timers simultaneously
console.time('total');
console.time('fetch');
const response = await fetch('/api/users');
console.timeEnd('fetch');
// Output: fetch: 89ms
console.time('parse');
const data = await response.json();
console.timeEnd('parse');
// Output: parse: 12ms
console.timeEnd('total');
// Output: total: 103msGrouping Methods
console.group() and console.groupEnd()
Groups related log messages under a collapsible label. The introduction to console.group() covers advanced patterns, but here is the essential usage:
console.group('User Authentication');
console.log('Checking credentials...');
console.log('Token valid: true');
console.log('Permissions loaded: admin, editor');
console.groupEnd();
// Output (collapsible):
// ▼ User Authentication
// Checking credentials...
// Token valid: true
// Permissions loaded: admin, editorconsole.groupCollapsed()
Same as console.group() but the group starts collapsed. Useful for verbose output that you only need to inspect occasionally:
console.groupCollapsed('API Response Details');
console.log('Status: 200');
console.log('Headers:', { 'content-type': 'application/json' });
console.log('Body:', { users: ['...'] });
console.groupEnd();
// Output shows "▶ API Response Details" (collapsed by default)Clearing the Console
console.clear()
Clears all console output. In browsers, it also displays a "Console was cleared" message:
// Clear previous output before starting a new debug session
console.clear();
console.log('Fresh debugging session started');DevTools Shortcut
In Chrome DevTools, press Ctrl+L (Windows/Linux) or Cmd+K (Mac) to clear the console without adding a log statement to your code.
Stack Trace Method
console.trace()
Prints a stack trace showing the call path to the current line. This tells you not just what happened, but how your code got there:
function processOrder(order) {
validateOrder(order);
}
function validateOrder(order) {
checkInventory(order.items);
}
function checkInventory(items) {
console.trace('Checking inventory for', items.length, 'items');
}
processOrder({ items: ['keyboard', 'mouse'] });
// Output:
// Checking inventory for 2 items
// at checkInventory (app.js:10)
// at validateOrder (app.js:6)
// at processOrder (app.js:2)This is covered in greater depth in the stack traces guide.
Best Practices
Console Usage Guidelines
Follow these practices to make your debugging sessions more productive.
Use the right method for the right purpose. Reserve console.error() for actual errors and console.warn() for genuine warnings. If you use console.error() for debug output, real errors get lost in the noise.
Remove console statements before production. Console calls in production code slow down the application, leak internal data, and clutter user consoles. Use a build tool like Terser or an ESLint rule (no-console) to strip them automatically.
Use console.table() for structured data. Any time you log an array of objects, console.table() is faster to read than expanding nested objects in console.log() output.
Name your timers descriptively. console.time('fetch') tells you nothing in a file with ten fetch calls. Use console.time('fetchUserProfile') or console.time('loadDashboardData') instead.
Prefer console.assert() over conditional logging. Instead of if (!value) console.error(...), use console.assert(value, 'message'). It is more concise and clearly communicates intent.
Common Mistakes and How to Avoid Them
Avoid These Console Pitfalls
These mistakes waste debugging time and create misleading output.
Logging objects that mutate after the log call. console.log() captures a reference to the object, not a snapshot. If the object changes after logging, the console shows the updated value when you expand it:
const cart = { items: 1 };
console.log(cart); // Shows {items: 1} initially...
cart.items = 5; // ...but expanding it later shows {items: 5}
// Fix: log a snapshot
console.log(JSON.parse(JSON.stringify(cart)));
// Or use the spread operator for shallow copies
console.log({ ...cart });Using console.log() for everything. When your console has 200 log messages at the same level, finding the important ones is impossible. Use warn and error appropriately so you can filter by level in DevTools.
Forgetting to close console.group(). Every console.group() needs a matching console.groupEnd(). Missing the end call causes all subsequent logs to appear indented inside the group.
String concatenation instead of comma separation. Concatenating objects with + converts them to [object Object]. Always pass objects as separate arguments:
const user = { name: 'Alice' };
// Bad — shows: "User: [object Object]"
console.log('User: ' + user);
// Good — shows expandable object
console.log('User:', user);Console Methods Quick Reference
| Method | Purpose | Example |
|---|---|---|
console.log() | General output | console.log('value:', x) |
console.warn() | Warning message | console.warn('Deprecated API') |
console.error() | Error message | console.error('Failed:', err) |
console.table() | Tabular data | console.table(arrayOfObjects) |
console.dir() | Object inspection | console.dir(element) |
console.assert() | Conditional error | console.assert(x > 0, 'msg') |
console.count() | Execution counter | console.count('clicks') |
console.time() | Start timer | console.time('load') |
console.timeLog() | Check timer | console.timeLog('load') |
console.timeEnd() | Stop timer | console.timeEnd('load') |
console.group() | Start group | console.group('Auth') |
console.groupEnd() | End group | console.groupEnd() |
console.trace() | Stack trace | console.trace('Here') |
console.clear() | Clear console | console.clear() |
Next Steps
Practice with console.group() for organized logging
Explore grouping logs with console.group() to learn advanced patterns for organizing complex debugging sessions.
Learn browser DevTools debugging
Master basic JavaScript debugging tips including breakpoints, watch expressions, and the Sources panel for debugging without console statements.
Understand stack traces
Learn to read and understand JavaScript stack traces so you can trace errors back to their root cause instead of guessing.
Rune AI
Key Insights
- Use severity levels intentionally:
console.warn()for deprecations and risky configs,console.error()for actual failures,console.log()for general output console.table()replaces manual inspection: any time you expand nested objects to read data, a table view is faster- Named timers beat manual Date calculations:
console.time()with descriptive labels measures performance without cluttering code - Always remove console calls from production: use ESLint
no-consolerules or build-tool plugins to strip them automatically - Log snapshots, not references: spread objects or use
JSON.parse(JSON.stringify())to capture the value at the moment of logging, not the mutated state
Frequently Asked Questions
Is console.log() bad for performance?
Does console.log() work the same in all browsers?
What is the difference between console.log() and console.dir()?
Should I use console.log() or a debugger?
Can I override console methods?
Conclusion
The JavaScript Console API offers far more than console.log(). Using console.warn() and console.error() for appropriate severity levels, console.table() for structured data, console.time() for performance measurements, and console.group() for organized output transforms the browser console from a simple print tool into a genuine debugging environment.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.