Logging System

The application includes a comprehensive logging system that replaces console.log with structured, configurable logging.

Features

  • Multiple Log Levels: ERROR, WARN, INFO, DEBUG, TRACE
  • Context-Aware Logging: Each component can have its own logger context
  • Performance Logging: Built-in timing functions
  • Log Viewer: UI component for viewing and filtering logs
  • Export Capability: Export logs as JSON
  • Configurable: Control log levels, formatting, and output

Basic Usage

In React Components

import { useComponentLogger } from '../hooks/useLogger';

const MyComponent: React.FC = () => {
  const log = useComponentLogger("MyComponent");

  const handleClick = () => {
    log.info("Button clicked");
    log.debug("Component state", { state: currentState });
  };

  return <button onClick={handleClick}>Click me</button>;
};

Direct Logger Usage

import { logger } from "../utils/logger";

// Global logger
logger.info("Application started");
logger.error("Critical error", { error: new Error("Something went wrong") });

Context-Specific Logger

import { createLogger } from "../utils/logger";

const apiLogger = createLogger("API");
apiLogger.info("API call started", { endpoint: "/users" });

Log Levels

ERROR (0)

For critical errors that need immediate attention.

log.error("Failed to load user data", { userId: 123 }, error);

WARN (1)

For warnings that don’t break functionality but should be investigated.

log.warn("API response was slower than expected", { responseTime: 2000 });

INFO (2)

For general information about application flow.

log.info("User logged in", { userId: 123, timestamp: new Date() });

DEBUG (3)

For detailed debugging information.

log.debug("Component re-rendered", {
  props: currentProps,
  state: currentState,
});

TRACE (4)

For very detailed tracing information.

log.trace("Function called", { parameters: args });

Performance Logging

Use the timing functions to measure performance:

const log = useComponentLogger("DataLoader");

const loadData = async () => {
  log.time("data-load");

  try {
    const data = await fetchData();
    log.info("Data loaded successfully", { recordCount: data.length });
  } catch (error) {
    log.error("Failed to load data", { error });
  } finally {
    log.timeEnd("data-load");
  }
};

Configuration

Setting Log Level

import { logger, LogLevel } from "../utils/logger";

// Set global log level
logger.setLevel(LogLevel.DEBUG);

// Configure logger settings
logger.setConfig({
  enableTimestamp: true,
  enableContext: true,
  enableColors: true,
  maxEntries: 1000,
});

Environment-Based Configuration

The logger automatically adjusts based on the environment:

  • Development: DEBUG level by default
  • Production: INFO level by default

Log Viewer Component

The LogViewer component provides a UI for viewing and managing logs:

import LogViewer from '../components/common/LogViewer';

const [showLogViewer, setShowLogViewer] = useState(false);

return (
  <div>
    <button onClick={() => setShowLogViewer(true)}>
      View Logs
    </button>

    <LogViewer
      isOpen={showLogViewer}
      onClose={() => setShowLogViewer(false)}
    />
  </div>
);

Log Viewer Features

  • Level Filtering: Show/hide specific log levels
  • Search: Search through log messages and contexts
  • Export: Export filtered logs as JSON
  • Clear: Clear all logs
  • Real-time Updates: Automatically updates as new logs are added

Best Practices

1. Use Appropriate Log Levels

// ✅ Good
log.error("Database connection failed", { error });
log.warn("API response slow", { responseTime: 5000 });
log.info("User action completed", { action: "save", userId: 123 });
log.debug("Component state changed", { prevState, newState });

// ❌ Avoid
console.log("Something happened");
console.error("Everything is broken");

2. Include Context

// ✅ Good
log.info("File uploaded", {
  fileName: "document.pdf",
  fileSize: 1024000,
  userId: 123,
});

// ❌ Avoid
log.info("File uploaded");

3. Use Structured Data

// ✅ Good
log.debug("API response", {
  endpoint: "/api/users",
  status: 200,
  responseTime: 150,
  data: { users: [...] }
});

// ❌ Avoid
log.debug("API response: " + JSON.stringify(data));

4. Handle Errors Properly

// ✅ Good
try {
  const result = await riskyOperation();
  log.info("Operation completed", { result });
} catch (error) {
  log.error("Operation failed", {
    operation: "riskyOperation",
    error: error.message,
    stack: error.stack,
  });
}

5. Use Performance Logging

// ✅ Good
const processData = async (data: any[]) => {
  log.time("data-processing");

  try {
    const result = await heavyProcessing(data);
    log.info("Data processing completed", {
      inputSize: data.length,
      outputSize: result.length,
    });
  } finally {
    log.timeEnd("data-processing");
  }
};

Migration from console.log

Replace existing console.log statements:

// Before
console.log("Component mounted");
console.warn("API call failed:", error);
console.error("Critical error:", error);

// After
const log = useComponentLogger("MyComponent");
log.info("Component mounted");
log.warn("API call failed", { error });
log.error("Critical error", { error });

Advanced Usage

Custom Logger Configuration

import { Logger, LogLevel } from "../utils/logger";

const customLogger = new Logger({
  level: LogLevel.TRACE,
  enableTimestamp: false,
  enableContext: true,
  enableColors: false,
  maxEntries: 500,
});

Log Subscription

import { logger } from "../utils/logger";

const unsubscribe = logger.subscribe((logEntry) => {
  // Custom log handling
  if (logEntry.level === LogLevel.ERROR) {
    // Send to error reporting service
    sendToErrorService(logEntry);
  }
});

// Don't forget to unsubscribe
unsubscribe();

Export Logs Programmatically

import { logger } from "../utils/logger";

const exportLogs = () => {
  const logText = logger.exportLogs();
  const blob = new Blob([logText], { type: "text/plain" });
  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = "application-logs.txt";
  a.click();

  URL.revokeObjectURL(url);
};