javascript

examples

examples.js⚡
/**
 * 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,
  };
}
Examples - JavaScript Tutorial | DeepML