javascript
examples
examples.jsâĄjavascript
/**
* 20.4 Debugging Techniques - Examples
*
* Demonstrates various debugging approaches and techniques
*/
// ============================================
// PART 1: Console Debugging Methods
// ============================================
/**
* Complete console API demonstration
*/
function demonstrateConsoleMethods() {
console.log('=== Console Methods Demo ===\n');
// Basic logging
console.log('Regular log message');
console.info('âšī¸ Info message');
console.warn('â ī¸ Warning message');
console.error('â Error message');
console.debug('đ Debug message (may be hidden in console)');
// Formatting with placeholders
console.log('String: %s, Number: %d, Object: %o', 'hello', 42, { a: 1 });
// Styled output (browser only)
console.log(
'%cStyled Text',
'color: blue; font-size: 20px; font-weight: bold;'
);
// Table display
console.log('\n--- console.table() ---');
const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'user' },
];
console.table(users);
console.table(users, ['name', 'role']); // Specific columns
// Object inspection
console.log('\n--- console.dir() ---');
console.dir({ nested: { deep: { value: 42 } } }, { depth: 3 });
// Grouping
console.log('\n--- console.group() ---');
console.group('User Processing');
console.log('Fetching user...');
console.log('Validating...');
console.group('Nested Group');
console.log('Inner operation');
console.groupEnd();
console.log('Complete');
console.groupEnd();
// Collapsed group
console.groupCollapsed('Collapsed Details');
console.log('Hidden by default');
console.log('Must expand to see');
console.groupEnd();
// Timing
console.log('\n--- console.time() ---');
console.time('operation');
// Simulate work
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
console.timeLog('operation', 'Intermediate checkpoint');
console.timeEnd('operation');
// Counting
console.log('\n--- console.count() ---');
function trackFunction(name) {
console.count(name);
}
trackFunction('functionA');
trackFunction('functionA');
trackFunction('functionB');
trackFunction('functionA');
console.countReset('functionA');
trackFunction('functionA');
// Stack trace
console.log('\n--- console.trace() ---');
function level1() {
level2();
}
function level2() {
level3();
}
function level3() {
console.trace('Stack trace from level3');
}
level1();
// Assertions
console.log('\n--- console.assert() ---');
console.assert(true, 'This will not show');
console.assert(false, 'This assertion failed!');
console.assert(1 === 2, 'Math is broken:', 1, '!==', 2);
console.log('\n');
}
// ============================================
// PART 2: Custom Debug Logger
// ============================================
/**
* Advanced debug logger with levels and namespaces
*/
class DebugLogger {
static levels = {
TRACE: 0,
DEBUG: 1,
INFO: 2,
WARN: 3,
ERROR: 4,
SILENT: 5,
};
static currentLevel = DebugLogger.levels.DEBUG;
static enabledNamespaces = new Set(['*']);
static timers = new Map();
constructor(namespace) {
this.namespace = namespace;
this.color = this.generateColor(namespace);
}
generateColor(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
const hue = hash % 360;
return `hsl(${hue}, 70%, 40%)`;
}
isEnabled() {
if (DebugLogger.enabledNamespaces.has('*')) return true;
if (DebugLogger.enabledNamespaces.has(this.namespace)) return true;
for (const pattern of DebugLogger.enabledNamespaces) {
if (pattern.endsWith('*')) {
const prefix = pattern.slice(0, -1);
if (this.namespace.startsWith(prefix)) return true;
}
}
return false;
}
log(level, ...args) {
if (!this.isEnabled()) return;
if (DebugLogger.levels[level] < DebugLogger.currentLevel) return;
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] [${level}] [${this.namespace}]`;
// In Node.js
if (typeof window === 'undefined') {
console.log(prefix, ...args);
return;
}
// In browser with colors
console.log(
`%c${prefix}`,
`color: ${this.color}; font-weight: bold;`,
...args
);
}
trace(...args) {
this.log('TRACE', ...args);
}
debug(...args) {
this.log('DEBUG', ...args);
}
info(...args) {
this.log('INFO', ...args);
}
warn(...args) {
this.log('WARN', ...args);
}
error(...args) {
this.log('ERROR', ...args);
}
time(label) {
DebugLogger.timers.set(`${this.namespace}:${label}`, Date.now());
}
timeEnd(label) {
const key = `${this.namespace}:${label}`;
const start = DebugLogger.timers.get(key);
if (start) {
const duration = Date.now() - start;
this.debug(`${label}: ${duration}ms`);
DebugLogger.timers.delete(key);
}
}
group(label) {
if (!this.isEnabled()) return;
console.group(`[${this.namespace}] ${label}`);
}
groupEnd() {
if (!this.isEnabled()) return;
console.groupEnd();
}
table(data, columns) {
if (!this.isEnabled()) return;
console.log(`[${this.namespace}]`);
console.table(data, columns);
}
static setLevel(level) {
DebugLogger.currentLevel = DebugLogger.levels[level] ?? level;
}
static enable(namespace) {
DebugLogger.enabledNamespaces.add(namespace);
}
static disable(namespace) {
DebugLogger.enabledNamespaces.delete(namespace);
}
static enableAll() {
DebugLogger.enabledNamespaces.add('*');
}
static disableAll() {
DebugLogger.enabledNamespaces.clear();
}
}
function demonstrateDebugLogger() {
console.log('=== Custom Debug Logger Demo ===\n');
const appLog = new DebugLogger('app');
const dbLog = new DebugLogger('app:database');
const authLog = new DebugLogger('app:auth');
// Different log levels
appLog.trace('This is a trace message');
appLog.debug('This is a debug message');
appLog.info('Application started');
appLog.warn('Configuration missing, using defaults');
appLog.error('Failed to connect to service');
// Timing
dbLog.time('query');
// Simulate query
setTimeout(() => {
dbLog.timeEnd('query');
}, 100);
// Grouping
authLog.group('Authentication Flow');
authLog.debug('Checking credentials');
authLog.debug('Verifying token');
authLog.info('User authenticated');
authLog.groupEnd();
// Table data
dbLog.table([
{ query: 'SELECT *', time: '5ms' },
{ query: 'UPDATE users', time: '10ms' },
]);
console.log('\n');
}
// ============================================
// PART 3: Debugging Patterns
// ============================================
/**
* Common debugging patterns and techniques
*/
// Pattern 1: Conditional debugging
function debugConditionally(data, options = {}) {
const debug = options.debug || false;
if (debug) console.log('Input data:', data);
const result = data.map((x) => x * 2);
if (debug) console.log('Processed result:', result);
return result;
}
// Pattern 2: Debug decorator/wrapper
function withDebug(fn, label) {
return function (...args) {
console.group(`${label || fn.name}()`);
console.log('Arguments:', args);
console.time('execution');
try {
const result = fn.apply(this, args);
if (result instanceof Promise) {
return result
.then((r) => {
console.log('Resolved:', r);
console.timeEnd('execution');
console.groupEnd();
return r;
})
.catch((e) => {
console.error('Rejected:', e);
console.timeEnd('execution');
console.groupEnd();
throw e;
});
}
console.log('Returned:', result);
console.timeEnd('execution');
console.groupEnd();
return result;
} catch (e) {
console.error('Threw:', e);
console.timeEnd('execution');
console.groupEnd();
throw e;
}
};
}
// Pattern 3: Object inspection helper
function inspect(obj, options = {}) {
const { depth = 2, colors = true, showHidden = false } = options;
function formatValue(value, currentDepth) {
if (currentDepth > depth) return '[...]';
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (typeof value === 'function') {
return `[Function: ${value.name || 'anonymous'}]`;
}
if (typeof value === 'symbol') {
return value.toString();
}
if (Array.isArray(value)) {
if (value.length === 0) return '[]';
const items = value.map((v) => formatValue(v, currentDepth + 1));
return `[${items.join(', ')}]`;
}
if (typeof value === 'object') {
const keys = showHidden
? Object.getOwnPropertyNames(value)
: Object.keys(value);
if (keys.length === 0) return '{}';
const pairs = keys.map(
(k) => `${k}: ${formatValue(value[k], currentDepth + 1)}`
);
return `{ ${pairs.join(', ')} }`;
}
if (typeof value === 'string') {
return `"${value}"`;
}
return String(value);
}
return formatValue(obj, 0);
}
// Pattern 4: State snapshot
function createStateSnapshot(obj) {
const timestamp = new Date().toISOString();
const snapshot = JSON.parse(JSON.stringify(obj));
return {
timestamp,
data: snapshot,
compare(other) {
const diff = {};
const allKeys = new Set([
...Object.keys(snapshot),
...Object.keys(other.data),
]);
for (const key of allKeys) {
if (JSON.stringify(snapshot[key]) !== JSON.stringify(other.data[key])) {
diff[key] = {
before: snapshot[key],
after: other.data[key],
};
}
}
return diff;
},
log() {
console.log(`Snapshot at ${timestamp}:`, snapshot);
},
};
}
function demonstrateDebuggingPatterns() {
console.log('=== Debugging Patterns Demo ===\n');
// Conditional debugging
console.log('--- Conditional Debug ---');
debugConditionally([1, 2, 3], { debug: true });
// Debug wrapper
console.log('\n--- Debug Wrapper ---');
function add(a, b) {
return a + b;
}
const debugAdd = withDebug(add, 'add');
debugAdd(5, 3);
// Object inspection
console.log('\n--- Object Inspection ---');
const complexObj = {
name: 'Test',
nested: { deep: { value: 42 } },
fn: function test() {},
arr: [1, 2, { x: 3 }],
};
console.log(inspect(complexObj, { depth: 3 }));
// State snapshots
console.log('\n--- State Snapshots ---');
const state = { count: 0, items: [] };
const snapshot1 = createStateSnapshot(state);
snapshot1.log();
state.count = 5;
state.items.push('item1');
const snapshot2 = createStateSnapshot(state);
snapshot2.log();
console.log('Diff:', snapshot1.compare(snapshot2));
console.log('\n');
}
// ============================================
// PART 4: Error Tracking & Reporting
// ============================================
/**
* Error tracking system
*/
class ErrorTracker {
constructor() {
this.errors = [];
this.maxErrors = 100;
this.handlers = [];
}
capture(error, context = {}) {
const errorInfo = {
id: this.generateId(),
timestamp: new Date().toISOString(),
message: error.message,
name: error.name,
stack: this.parseStack(error.stack),
context,
url: typeof window !== 'undefined' ? window.location.href : null,
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : null,
};
this.errors.push(errorInfo);
// Keep only recent errors
if (this.errors.length > this.maxErrors) {
this.errors.shift();
}
// Notify handlers
this.handlers.forEach((handler) => handler(errorInfo));
return errorInfo;
}
parseStack(stack) {
if (!stack) return [];
return stack
.split('\n')
.slice(1)
.map((line) => {
const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
if (match) {
return {
function: match[1],
file: match[2],
line: parseInt(match[3]),
column: parseInt(match[4]),
};
}
return { raw: line.trim() };
})
.filter(Boolean);
}
generateId() {
return 'err_' + Math.random().toString(36).substr(2, 9);
}
onError(handler) {
this.handlers.push(handler);
return () => {
const index = this.handlers.indexOf(handler);
if (index !== -1) this.handlers.splice(index, 1);
};
}
getErrors() {
return [...this.errors];
}
getErrorById(id) {
return this.errors.find((e) => e.id === id);
}
getRecentErrors(count = 10) {
return this.errors.slice(-count);
}
clearErrors() {
this.errors = [];
}
// Generate report
generateReport() {
const errorsByType = {};
for (const error of this.errors) {
const key = error.name || 'Unknown';
if (!errorsByType[key]) {
errorsByType[key] = [];
}
errorsByType[key].push(error);
}
return {
totalErrors: this.errors.length,
errorsByType,
mostRecent: this.errors[this.errors.length - 1],
generatedAt: new Date().toISOString(),
};
}
}
function demonstrateErrorTracking() {
console.log('=== Error Tracking Demo ===\n');
const tracker = new ErrorTracker();
// Add error handler
tracker.onError((error) => {
console.log(`[ErrorTracker] Captured: ${error.message}`);
});
// Capture some errors
try {
throw new Error('Something went wrong');
} catch (e) {
tracker.capture(e, { userId: 123, action: 'submit' });
}
try {
throw new TypeError('Invalid type provided');
} catch (e) {
tracker.capture(e, { input: 'string', expected: 'number' });
}
try {
throw new ReferenceError('Variable not defined');
} catch (e) {
tracker.capture(e);
}
// Generate report
console.log('\nError Report:');
console.log(JSON.stringify(tracker.generateReport(), null, 2));
console.log('\n');
}
// ============================================
// PART 5: Performance Debugging
// ============================================
/**
* Performance debugging utilities
*/
class PerformanceProfiler {
constructor() {
this.measurements = new Map();
this.marks = new Map();
}
mark(name) {
this.marks.set(name, performance.now());
}
measure(name, startMark, endMark) {
const start = this.marks.get(startMark);
const end = endMark ? this.marks.get(endMark) : performance.now();
if (start === undefined) {
console.warn(`Mark "${startMark}" not found`);
return null;
}
const duration = end - start;
if (!this.measurements.has(name)) {
this.measurements.set(name, []);
}
this.measurements.get(name).push(duration);
return duration;
}
// Measure function execution
profile(fn, label) {
const profiler = this;
return function (...args) {
const name = label || fn.name || 'anonymous';
const start = performance.now();
const result = fn.apply(this, args);
if (result instanceof Promise) {
return result.finally(() => {
const duration = performance.now() - start;
profiler.recordDuration(name, duration);
});
}
const duration = performance.now() - start;
profiler.recordDuration(name, duration);
return result;
};
}
recordDuration(name, duration) {
if (!this.measurements.has(name)) {
this.measurements.set(name, []);
}
this.measurements.get(name).push(duration);
}
getStats(name) {
const measurements = this.measurements.get(name);
if (!measurements || measurements.length === 0) {
return null;
}
const sorted = [...measurements].sort((a, b) => a - b);
const sum = sorted.reduce((a, b) => a + b, 0);
return {
name,
count: sorted.length,
total: sum,
mean: sum / sorted.length,
min: sorted[0],
max: sorted[sorted.length - 1],
median: sorted[Math.floor(sorted.length / 2)],
p95: sorted[Math.floor(sorted.length * 0.95)],
};
}
getAllStats() {
const stats = {};
for (const name of this.measurements.keys()) {
stats[name] = this.getStats(name);
}
return stats;
}
report() {
console.log('\n=== Performance Report ===');
const stats = this.getAllStats();
const data = Object.values(stats).map((s) => ({
Name: s.name,
Calls: s.count,
'Mean (ms)': s.mean.toFixed(2),
'Min (ms)': s.min.toFixed(2),
'Max (ms)': s.max.toFixed(2),
}));
console.table(data);
}
reset() {
this.measurements.clear();
this.marks.clear();
}
}
function demonstratePerformanceDebugging() {
console.log('=== Performance Debugging Demo ===\n');
const profiler = new PerformanceProfiler();
// Profile a function
function slowOperation(n) {
let result = 0;
for (let i = 0; i < n; i++) {
result += Math.sqrt(i);
}
return result;
}
const profiledOp = profiler.profile(slowOperation, 'slowOperation');
// Run multiple times
for (let i = 0; i < 5; i++) {
profiledOp(100000 + i * 10000);
}
// Manual marks
profiler.mark('start');
// Simulate work
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
profiler.mark('checkpoint');
for (let i = 0; i < 500000; i++) {
sum -= i / 2;
}
profiler.measure('phase1', 'start', 'checkpoint');
profiler.measure('phase2', 'checkpoint');
profiler.measure('total', 'start');
// Show report
profiler.report();
console.log('\n');
}
// ============================================
// PART 6: Memory Debugging Helpers
// ============================================
/**
* Memory usage monitoring (Node.js)
*/
function getMemoryUsage() {
if (typeof process !== 'undefined' && process.memoryUsage) {
const usage = process.memoryUsage();
return {
heapUsed: (usage.heapUsed / 1024 / 1024).toFixed(2) + ' MB',
heapTotal: (usage.heapTotal / 1024 / 1024).toFixed(2) + ' MB',
external: (usage.external / 1024 / 1024).toFixed(2) + ' MB',
rss: (usage.rss / 1024 / 1024).toFixed(2) + ' MB',
};
}
// Browser fallback (limited info)
if (typeof performance !== 'undefined' && performance.memory) {
return {
usedJSHeapSize:
(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
totalJSHeapSize:
(performance.memory.totalJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
};
}
return { message: 'Memory info not available' };
}
/**
* Track object allocations
*/
class AllocationTracker {
constructor() {
this.allocations = new Map();
this.counter = 0;
}
track(obj, label = '') {
const id = ++this.counter;
const info = {
id,
label,
type: obj?.constructor?.name || typeof obj,
size: this.estimateSize(obj),
timestamp: Date.now(),
stack: new Error().stack?.split('\n').slice(2, 5).join('\n'),
};
this.allocations.set(id, info);
// Return cleanup function
return () => {
this.allocations.delete(id);
};
}
estimateSize(obj) {
const seen = new WeakSet();
function estimate(value) {
if (value === null || value === undefined) return 0;
switch (typeof value) {
case 'boolean':
return 4;
case 'number':
return 8;
case 'string':
return value.length * 2;
case 'object':
if (seen.has(value)) return 0;
seen.add(value);
if (Array.isArray(value)) {
return value.reduce((sum, item) => sum + estimate(item), 0);
}
return Object.entries(value).reduce(
(sum, [k, v]) => sum + k.length * 2 + estimate(v),
0
);
default:
return 0;
}
}
return estimate(obj);
}
getActive() {
return Array.from(this.allocations.values());
}
report() {
const active = this.getActive();
console.log(`\nActive allocations: ${active.length}`);
console.log(
'Total estimated size:',
active.reduce((sum, a) => sum + a.size, 0),
'bytes'
);
console.table(
active.map((a) => ({
ID: a.id,
Label: a.label,
Type: a.type,
'Size (bytes)': a.size,
}))
);
}
}
function demonstrateMemoryDebugging() {
console.log('=== Memory Debugging Demo ===\n');
console.log('Memory usage:');
console.log(getMemoryUsage());
const tracker = new AllocationTracker();
// Track some allocations
const cleanup1 = tracker.track({ data: 'hello' }, 'small object');
const cleanup2 = tracker.track(new Array(1000).fill(0), 'large array');
const cleanup3 = tracker.track(
{
nested: { deep: { value: 'x'.repeat(1000) } },
},
'nested object'
);
console.log('\nAfter allocations:');
tracker.report();
// Clean up one
cleanup2();
console.log('\nAfter cleanup:');
tracker.report();
console.log('\n');
}
// ============================================
// RUN ALL EXAMPLES
// ============================================
console.log('ââââââââââââââââââââââââââââââââââââââââââââââ');
console.log('â Debugging Techniques Examples â');
console.log('ââââââââââââââââââââââââââââââââââââââââââââââ\n');
demonstrateConsoleMethods();
demonstrateDebugLogger();
demonstrateDebuggingPatterns();
demonstrateErrorTracking();
demonstratePerformanceDebugging();
demonstrateMemoryDebugging();
console.log('All examples completed!');
// Export for use
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
DebugLogger,
withDebug,
inspect,
createStateSnapshot,
ErrorTracker,
PerformanceProfiler,
AllocationTracker,
getMemoryUsage,
};
}