Every developer has faced code that doesn't behave as expected. The ability to debug — to find and fix bugs — is one of the most important skills to master. JavaScript provides the console object with a variety of methods that are far richer than just console.log(). This article covers debugging techniques that will save you a lot of time.
console.log() — The Basics You Must Master
console.log() is the most common method, but there are a few tricks you might not know:
// Simple log
console.log('Hello world!');
console.log(42, true, 'string', null); // Multiple arguments allowed
// Using a label with an object
const user = { name: 'Andy', age: 25, city: 'Bandung' };
console.log('User data:', user); // Shows as an interactive object in DevTools
// Computed property name — a trick for automatic labels
const price = 50000;
const discount = 0.1;
console.log({ price, discount }); // { price: 50000, discount: 0.1 }
// Template literal for an informative message
console.log(`Price after discount: ${(price * (1 - discount)).toLocaleString('en-US')}`);
// CSS styling in the console (browser only)
console.log('%cAttention!', 'color: red; font-size: 20px; font-weight: bold;');
console.log('%cSuccess!', 'color: green; background: #e8f5e9; padding: 4px 8px;');
console.error(), console.warn(), console.info()
Use the method that fits the context to make filtering in DevTools easier:
// Shows with a red X icon — for errors
console.error('Failed to fetch data from the API!');
console.error('Error detail:', new Error('Network timeout'));
// Shows with a yellow triangle icon — for warnings
console.warn('This function is deprecated, use the new version.');
console.warn('The token will expire in 5 minutes.');
// Shows with a blue icon — for information
console.info('Application started successfully.');
console.info('Version:', '2.1.0', '| Environment:', 'development');
// In DevTools, you can filter by this level using the "Levels" dropdown
console.table() — Displaying Array/Object Data
This is a favorite method for debugging data that is an array of objects:
const products = [
{ id: 1, name: 'Laptop', price: 8000000, stock: 5 },
{ id: 2, name: 'Mouse', price: 150000, stock: 12 },
{ id: 3, name: 'Keyboard', price: 500000, stock: 8 }
];
// Shows in a neat table format in DevTools!
console.table(products);
// You can filter which columns to display
console.table(products, ['name', 'price']); // Only shows the name and price columns
console.group() — Grouping Logs
Very useful when complex code produces many logs:
function processOrder(order) {
console.group(`Processing Order #${order.id}`); // Start a group
console.log('Validating items...');
order.items.forEach(item => {
console.group(` Item: ${item.name}`);
console.log('Price:', item.price);
console.log('Qty:', item.qty);
console.log('Subtotal:', item.price * item.qty);
console.groupEnd(); // Close the sub-group
});
const total = order.items.reduce((sum, item) => sum + item.price * item.qty, 0);
console.log('Total:', total);
console.groupEnd(); // Close the main group
}
processOrder({
id: 'ORD-001',
items: [
{ name: 'Coffee', price: 25000, qty: 2 },
{ name: 'Bread', price: 15000, qty: 3 }
]
});
console.time() — Measuring Performance
Use this to measure how long an operation takes:
// Start the timer
console.time('filter-data');
// The operation you want to measure
const data = Array.from({ length: 100000 }, (_, i) => i);
const filtered = data.filter(n => n % 2 === 0 && n > 50000);
// Stop the timer and show the result
console.timeEnd('filter-data'); // Output: "filter-data: 5.23ms"
// You can have multiple timers at once with different names
console.time('fetch-api');
// ... fetch operation ...
console.timeEnd('fetch-api');
console.assert() — Validating a Condition
console.assert() only prints a message if the given condition is false:
function divide(a, b) {
console.assert(b !== 0, 'Error: The divisor cannot be zero!', { a, b });
return a / b;
}
divide(10, 2); // No output (the condition b !== 0 is true)
divide(10, 0); // Output: "Assertion failed: Error: The divisor cannot be zero! {a: 10, b: 0}"
// Useful for verifying assumptions in your code
const user = { name: 'Andy', role: 'admin' };
console.assert(user.role === 'admin', 'The user should be an admin!');
console.count() and console.trace()
// console.count() — count how many times this line is executed
function clickButton() {
console.count('button clicked');
// ...other logic
}
// Each time it's called: "button clicked: 1", "button clicked: 2", etc.
// console.trace() — show the call stack (who called this function)
function functionC() {
console.trace('Where was this function called from?');
}
function functionB() { functionC(); }
function functionA() { functionB(); }
functionA();
// The output will show: functionC <- functionB <- functionA
Additional Debugging Tips
- Use the
debuggerkeyword in your code to create an automatic breakpoint when DevTools is open. - In the browser's DevTools, use the Sources tab to add breakpoints visually.
- Remove or comment out all
console.log()calls before your code goes to production. - Consider using a logging library like
debugorwinstonfor large projects.
Conclusion
JavaScript's console object is far more than just console.log(). By taking advantage of console.table() for tabular data, console.group() for grouping output, console.time() for measuring performance, and console.assert() for validating assumptions, your debugging process will be far more efficient and focused. Make debugging skills part of your everyday coding habits to produce higher-quality code.