UNPKG

houser-js-utils

Version:

A comprehensive collection of TypeScript utility functions for common development tasks including array manipulation, string processing, date handling, random number generation, validation, and much more.

1 lines 21.4 kB
{"version":3,"file":"LoggingUtils.mjs","sources":["../src/LoggingUtils.ts"],"sourcesContent":["/**\n * @module LoggingUtils\n * @description A comprehensive collection of utility functions for logging, performance monitoring, and debugging.\n * Provides methods for logging messages with different severity levels (debug, info, warn, error),\n * managing log entries with storage, filtering, and export/import capabilities,\n * measuring performance and memory usage, and formatting console output with grouping.\n * @example\n * ```typescript\n * import { LoggingUtils } from 'houser-js-utils';\n *\n * // Basic logging with different levels\n * LoggingUtils.debug(\"Processing started\", { prefix: \"DataService\" });\n * LoggingUtils.info(\"User logged in\", { data: { userId: \"123\" } });\n * LoggingUtils.warn(\"High memory usage\", { data: { usage: \"85%\" } });\n * LoggingUtils.error(\"API call failed\", { data: error, stackTrace: true });\n *\n * // Performance measurement\n * const endMeasurement = LoggingUtils.measurePerformance(\"Data processing\");\n * // ... do some work ...\n * endMeasurement();\n *\n * // Memory usage tracking\n * LoggingUtils.measureMemory(\"After data load\");\n * ```\n */\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\ninterface LogOptions {\n level?: LogLevel;\n timestamp?: boolean;\n prefix?: string;\n group?: boolean;\n groupCollapsed?: boolean;\n stackTrace?: boolean;\n data?: unknown;\n}\n\ninterface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n data?: unknown;\n stack?: string;\n}\n\ninterface PerformanceMemory {\n totalJSHeapSize: number;\n usedJSHeapSize: number;\n jsHeapSizeLimit: number;\n}\n\ndeclare global {\n interface Performance {\n memory?: PerformanceMemory;\n }\n}\n\nexport const LoggingUtils = {\n /**\n * Default configuration options for logging.\n * @default\n * ```typescript\n * {\n * level: \"info\",\n * timestamp: true,\n * prefix: \"\",\n * group: false,\n * groupCollapsed: false,\n * stackTrace: false\n * }\n * ```\n */\n defaultOptions: {\n level: \"info\" as LogLevel,\n timestamp: true,\n prefix: \"\",\n group: false,\n groupCollapsed: false,\n stackTrace: false,\n },\n\n /**\n * Internal storage for all log entries.\n * @default []\n */\n logEntries: [] as LogEntry[],\n\n /**\n * Maximum number of log entries to store before removing oldest entries.\n * @default 1000\n */\n maxLogEntries: 1000,\n\n /**\n * Clears all stored log entries from memory.\n * @example\n * ```typescript\n * // Clear all stored logs\n * LoggingUtils.clearLogEntries();\n * console.log(\"Logs cleared\");\n *\n * // Clear logs after exporting\n * const logs = LoggingUtils.exportLogEntries();\n * LoggingUtils.clearLogEntries();\n * ```\n */\n clearLogEntries(): void {\n this.logEntries = [];\n },\n\n /**\n * Logs a debug message with optional metadata.\n * @param message - The debug message to log\n * @param options - Optional configuration for the log entry\n * @example\n * ```typescript\n * // Simple debug message\n * LoggingUtils.debug(\"Processing started\");\n *\n * // Debug with data and prefix\n * LoggingUtils.debug(\"Data processed\", {\n * prefix: \"DataService\",\n * data: { count: 100, status: \"success\" }\n * });\n *\n * // Debug with grouping\n * LoggingUtils.debug(\"Complex operation\", {\n * group: true,\n * groupCollapsed: true,\n * data: { steps: [\"step1\", \"step2\"] }\n * });\n * ```\n */\n debug(message: string, options: Omit<LogOptions, \"level\"> = {}): void {\n this.log(message, { ...options, level: \"debug\" });\n },\n\n /**\n * Logs an error message with optional stack trace and metadata.\n * @param message - The error message to log\n * @param options - Optional configuration for the log entry\n * @example\n * ```typescript\n * try {\n * // ... some code that might throw\n * } catch (error) {\n * LoggingUtils.error(\"Failed to process data\", {\n * data: error,\n * prefix: \"DataService\",\n * stackTrace: true\n * });\n * }\n *\n * // Error with custom data\n * LoggingUtils.error(\"API request failed\", {\n * data: {\n * status: 500,\n * endpoint: \"/api/data\",\n * response: \"Internal Server Error\"\n * }\n * });\n * ```\n */\n error(message: string, options: Omit<LogOptions, \"level\"> = {}): void {\n this.log(message, { ...options, level: \"error\", stackTrace: true });\n },\n\n /**\n * Exports all stored log entries as a formatted JSON string.\n * @returns A JSON string containing all log entries\n * @example\n * ```typescript\n * // Export logs to file\n * const logs = LoggingUtils.exportLogEntries();\n * const blob = new Blob([logs], { type: 'application/json' });\n * const url = URL.createObjectURL(blob);\n *\n * // Export logs to server\n * const logs = LoggingUtils.exportLogEntries();\n * await fetch('/api/logs', {\n * method: 'POST',\n * body: logs,\n * headers: { 'Content-Type': 'application/json' }\n * });\n * ```\n */\n exportLogEntries(): string {\n return JSON.stringify(this.logEntries, null, 2);\n },\n\n /**\n * Formats a number of bytes into a human-readable string with appropriate unit.\n * @param bytes - The number of bytes to format\n * @returns A formatted string with appropriate unit (B, KB, MB, GB, TB)\n * @example\n * ```typescript\n * const size = LoggingUtils.formatBytes(1024 * 1024);\n * console.log(size); // \"1.00 MB\"\n *\n * const smallSize = LoggingUtils.formatBytes(500);\n * console.log(smallSize); // \"500.00 B\"\n *\n * const largeSize = LoggingUtils.formatBytes(1024 * 1024 * 1024 * 2);\n * console.log(largeSize); // \"2.00 GB\"\n * ```\n */\n formatBytes(bytes: number): string {\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)} ${units[unitIndex]}`;\n },\n\n /**\n * Gets a copy of all stored log entries.\n * @returns An array of log entries\n * @example\n * ```typescript\n * // Get all logs\n * const allLogs = LoggingUtils.getLogEntries();\n * console.log(`Total logs: ${allLogs.length}`);\n *\n * // Filter logs in memory\n * const errorLogs = LoggingUtils.getLogEntries()\n * .filter(log => log.level === 'error');\n * ```\n */\n getLogEntries(): LogEntry[] {\n return [...this.logEntries];\n },\n\n /**\n * Gets log entries filtered by a specific log level.\n * @param level - The log level to filter by\n * @returns An array of filtered log entries\n * @example\n * ```typescript\n * // Get all error logs\n * const errorLogs = LoggingUtils.getLogEntriesByLevel(\"error\");\n * console.log(`Error count: ${errorLogs.length}`);\n *\n * // Get all debug logs\n * const debugLogs = LoggingUtils.getLogEntriesByLevel(\"debug\");\n * console.log(`Debug count: ${debugLogs.length}`);\n * ```\n */\n getLogEntriesByLevel(level: LogLevel): LogEntry[] {\n return this.logEntries.filter((entry) => entry.level === level);\n },\n\n /**\n * Gets log entries within a specific time range.\n * @param startTime - The start time of the range\n * @param endTime - The end time of the range\n * @returns An array of filtered log entries\n * @example\n * ```typescript\n * // Get logs from today\n * const today = new Date();\n * const yesterday = new Date(today);\n * yesterday.setDate(yesterday.getDate() - 1);\n *\n * const todayLogs = LoggingUtils.getLogEntriesByTimeRange(yesterday, today);\n * console.log(`Logs in last 24 hours: ${todayLogs.length}`);\n *\n * // Get logs from specific time period\n * const startDate = new Date(\"2024-01-01\");\n * const endDate = new Date(\"2024-01-02\");\n * const periodLogs = LoggingUtils.getLogEntriesByTimeRange(startDate, endDate);\n * ```\n */\n getLogEntriesByTimeRange(startTime: Date, endTime: Date): LogEntry[] {\n return this.logEntries.filter(\n (entry) =>\n new Date(entry.timestamp) >= startTime &&\n new Date(entry.timestamp) <= endTime\n );\n },\n\n /**\n * Logs an informational message with optional metadata.\n * @param message - The info message to log\n * @param options - Optional configuration for the log entry\n * @example\n * ```typescript\n * // Simple info message\n * LoggingUtils.info(\"Application started\");\n *\n * // Info with data and prefix\n * LoggingUtils.info(\"User logged in\", {\n * prefix: \"Auth\",\n * data: { userId: \"123\", role: \"admin\" }\n * });\n *\n * // Info with grouping\n * LoggingUtils.info(\"Configuration loaded\", {\n * group: true,\n * data: { settings: { theme: \"dark\", language: \"en\" } }\n * });\n * ```\n */\n info(message: string, options: Omit<LogOptions, \"level\"> = {}): void {\n this.log(message, { ...options, level: \"info\" });\n },\n\n /**\n * Imports log entries from a JSON string.\n * @param json - The JSON string containing log entries\n * @throws {Error} If the JSON string is invalid or cannot be parsed\n * @example\n * ```typescript\n * // Import logs from file\n * const json = await fetch('/logs.json').then(r => r.text());\n * LoggingUtils.importLogEntries(json);\n *\n * // Import logs from server\n * const response = await fetch('/api/logs');\n * const json = await response.text();\n * LoggingUtils.importLogEntries(json);\n * ```\n */\n importLogEntries(json: string): void {\n try {\n const entries = JSON.parse(json) as LogEntry[];\n this.logEntries = entries.slice(-this.maxLogEntries);\n } catch (error) {\n this.error(\"Failed to import log entries\", { data: error });\n }\n },\n\n /**\n * Logs a message with the specified options.\n * @param message - The message to log\n * @param options - Configuration options for the log entry\n * @example\n * ```typescript\n * // Custom log with all options\n * LoggingUtils.log(\"Custom message\", {\n * level: \"info\",\n * prefix: \"Custom\",\n * group: true,\n * groupCollapsed: true,\n * stackTrace: true,\n * data: { custom: \"data\" }\n * });\n *\n * // Simple log with default options\n * LoggingUtils.log(\"Simple message\");\n * ```\n */\n log(message: string, options: LogOptions = {}): void {\n const opts = { ...this.defaultOptions, ...options };\n const timestamp = new Date().toISOString();\n const prefix = opts.prefix ? `[${opts.prefix}] ` : \"\";\n const fullMessage = `${prefix}${message}`;\n\n // Create log entry\n const entry: LogEntry = {\n timestamp,\n level: opts.level || \"info\",\n message: fullMessage,\n data: opts.data,\n };\n\n // Add stack trace if requested\n if (opts.stackTrace) {\n entry.stack = new Error().stack;\n }\n\n // Store log entry\n this.logEntries.push(entry);\n if (this.logEntries.length > this.maxLogEntries) {\n this.logEntries.shift();\n }\n\n // Group logs if requested\n if (opts.group) {\n if (opts.groupCollapsed) {\n console.groupCollapsed(fullMessage);\n } else {\n console.group(fullMessage);\n }\n }\n\n // Log with appropriate console method\n switch (opts.level) {\n case \"debug\":\n console.debug(fullMessage, opts.data || \"\");\n break;\n case \"info\":\n console.info(fullMessage, opts.data || \"\");\n break;\n case \"warn\":\n console.warn(fullMessage, opts.data || \"\");\n break;\n case \"error\":\n console.error(fullMessage, opts.data || \"\");\n if (entry.stack) {\n console.error(entry.stack);\n }\n break;\n }\n\n // End group if started\n if (opts.group) {\n console.groupEnd();\n }\n },\n\n /**\n * Logs memory usage information if available in the browser.\n * @param label - The label for the memory measurement\n * @example\n * ```typescript\n * // Log memory usage\n * LoggingUtils.measureMemory(\"After data load\");\n * // Output: Memory: After data load { used: \"50.2 MB\", total: \"100.0 MB\", limit: \"500.0 MB\" }\n *\n * // Track memory usage over time\n * setInterval(() => {\n * LoggingUtils.measureMemory(\"Periodic check\");\n * }, 60000);\n * ```\n */\n measureMemory(label: string): void {\n if (performance.memory) {\n const { usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit } =\n performance.memory;\n this.debug(`Memory: ${label}`, {\n data: {\n used: this.formatBytes(usedJSHeapSize),\n total: this.formatBytes(totalJSHeapSize),\n limit: this.formatBytes(jsHeapSizeLimit),\n },\n });\n }\n },\n\n /**\n * Creates a performance measurement for synchronous operations.\n * @param label - The label for the performance measurement\n * @returns A function to end the measurement and log the duration\n * @example\n * ```typescript\n * // Measure a synchronous operation\n * const endMeasurement = LoggingUtils.measurePerformance(\"Data processing\");\n * // ... do some work ...\n * endMeasurement();\n * // Output: Performance: Data processing { duration: \"123.45ms\" }\n *\n * // Measure multiple operations\n * const endTotal = LoggingUtils.measurePerformance(\"Total processing\");\n *\n * const endStep1 = LoggingUtils.measurePerformance(\"Step 1\");\n * // ... do step 1 ...\n * endStep1();\n *\n * const endStep2 = LoggingUtils.measurePerformance(\"Step 2\");\n * // ... do step 2 ...\n * endStep2();\n *\n * endTotal();\n * ```\n */\n measurePerformance(label: string): () => void {\n const startTime = performance.now();\n return () => {\n const endTime = performance.now();\n const duration = endTime - startTime;\n this.debug(`Performance: ${label}`, {\n data: { duration: `${duration.toFixed(2)}ms` },\n });\n };\n },\n\n /**\n * Measures the performance of an asynchronous function.\n * @param label - The label for the performance measurement\n * @param fn - The async function to measure\n * @returns A Promise with the function result\n * @example\n * ```typescript\n * // Measure an API call\n * const result = await LoggingUtils.measureAsyncPerformance(\n * \"API call\",\n * async () => await fetchData()\n * );\n *\n * // Measure multiple async operations\n * const results = await Promise.all([\n * LoggingUtils.measureAsyncPerformance(\"API 1\", () => fetchData1()),\n * LoggingUtils.measureAsyncPerformance(\"API 2\", () => fetchData2())\n * ]);\n * ```\n */\n async measureAsyncPerformance<T>(\n label: string,\n fn: () => Promise<T>\n ): Promise<T> {\n const startTime = performance.now();\n try {\n const result = await fn();\n const endTime = performance.now();\n const duration = endTime - startTime;\n this.debug(`Performance: ${label}`, {\n data: { duration: `${duration.toFixed(2)}ms` },\n });\n return result;\n } catch (error) {\n const endTime = performance.now();\n const duration = endTime - startTime;\n this.error(`Performance: ${label}`, {\n data: { duration: `${duration.toFixed(2)}ms`, error },\n });\n throw error;\n }\n },\n\n /**\n * Logs a warning message with optional metadata.\n * @param message - The warning message to log\n * @param options - Optional configuration for the log entry\n * @example\n * ```typescript\n * // Simple warning\n * LoggingUtils.warn(\"Resource usage high\");\n *\n * // Warning with data and prefix\n * LoggingUtils.warn(\"High memory usage\", {\n * prefix: \"System\",\n * data: { cpu: \"80%\", memory: \"75%\" }\n * });\n *\n * // Warning with grouping\n * LoggingUtils.warn(\"Multiple issues detected\", {\n * group: true,\n * data: { issues: [\"CPU\", \"Memory\", \"Disk\"] }\n * });\n * ```\n */\n warn(message: string, options: Omit<LogOptions, \"level\"> = {}): void {\n this.log(message, { ...options, level: \"warn\" });\n },\n};\n"],"names":[],"mappings":"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"}