feat(logging-p0): complete Wave 2 - Auth/Extractor/Cleaner services transformed
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
*
|
||||
* Provides comprehensive error serialization and formatting for logging.
|
||||
* Captures full error context including stack traces, causes, and custom properties.
|
||||
*
|
||||
* Enhanced with request context tracking for distributed tracing support.
|
||||
*/
|
||||
|
||||
import type { ErrorLike, SerializedError } from '../../types/errors'
|
||||
import { isProduction } from './shared'
|
||||
import { getRequestId } from './request-context'
|
||||
|
||||
/**
|
||||
* Check if value is an Error or Error-like object
|
||||
@@ -152,6 +155,29 @@ export function extractErrorContext(error: SerializedError): {
|
||||
/**
|
||||
* Format error for console/file logging
|
||||
* Returns a formatted string with all error details
|
||||
*
|
||||
* @param error - The error to format (Error object or Error-like)
|
||||
* @param context - Optional context for logging
|
||||
* @param context.operation - Business operation being performed (e.g., 'extract', 'clean', 'validate')
|
||||
* @param context.module - Module/Service name where error occurred
|
||||
* @param context.userId - User ID performing the operation
|
||||
* @param context.requestId - Request/trace ID for distributed tracing (auto-injected if not provided)
|
||||
* @param context.batchId - Batch identifier for batch operations
|
||||
* @param context.duration - Operation duration in milliseconds
|
||||
* @param context.orderNumbers - Order numbers related to the operation
|
||||
* @param context.materialCodes - Material codes related to the operation
|
||||
* @returns Object with formatted message and metadata for logging
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const { message, metadata } = formatErrorForLogging(error, {
|
||||
* operation: 'extract',
|
||||
* userId: 'user123',
|
||||
* batchId: 'batch-001',
|
||||
* duration: 1500
|
||||
* })
|
||||
* logger.error(message, metadata)
|
||||
* ```
|
||||
*/
|
||||
export function formatErrorForLogging(
|
||||
error: unknown,
|
||||
@@ -159,6 +185,11 @@ export function formatErrorForLogging(
|
||||
operation?: string
|
||||
module?: string
|
||||
userId?: string
|
||||
requestId?: string
|
||||
batchId?: string
|
||||
duration?: number
|
||||
orderNumbers?: string[]
|
||||
materialCodes?: string[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
): {
|
||||
@@ -170,11 +201,27 @@ export function formatErrorForLogging(
|
||||
const errorToLog = isProd ? sanitizeError(serialized) : serialized
|
||||
const errorContext = extractErrorContext(errorToLog)
|
||||
|
||||
// Auto-inject requestId from async context if not explicitly provided
|
||||
const autoRequestId = getRequestId()
|
||||
const requestId = context?.requestId || autoRequestId
|
||||
|
||||
const metadata: Record<string, unknown> = {
|
||||
error: errorToLog,
|
||||
...(requestId && { requestId }),
|
||||
...context
|
||||
}
|
||||
|
||||
// Remove undefined context fields to keep logs clean
|
||||
if (context) {
|
||||
const cleanMetadata: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(metadata)) {
|
||||
if (value !== undefined) {
|
||||
cleanMetadata[key] = value
|
||||
}
|
||||
}
|
||||
Object.assign(metadata, cleanMetadata)
|
||||
}
|
||||
|
||||
// Add error location context if available
|
||||
if (errorContext.fileName) {
|
||||
metadata.errorLocation = {
|
||||
@@ -202,6 +249,28 @@ export function formatErrorForLogging(
|
||||
/**
|
||||
* Log error with full context
|
||||
* Wrapper for logger.error that ensures complete error information is captured
|
||||
*
|
||||
* @param logger - Logger instance with error method
|
||||
* @param error - The error to log (Error object or Error-like)
|
||||
* @param options - Logging options
|
||||
* @param options.message - Custom message to prepend to error message
|
||||
* @param options.operation - Business operation being performed
|
||||
* @param options.module - Module/Service name
|
||||
* @param options.userId - User ID performing the operation
|
||||
* @param options.requestId - Request/trace ID (auto-injected if not provided)
|
||||
* @param options.batchId - Batch identifier for batch operations
|
||||
* @param options.duration - Operation duration in milliseconds
|
||||
* @param options.context - Additional custom context fields
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* logError(logger, error, {
|
||||
* operation: 'extract',
|
||||
* userId: 'user123',
|
||||
* message: 'Failed to process order',
|
||||
* duration: 1500
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function logError(
|
||||
logger: { error: (message: string, meta?: Record<string, unknown>) => void },
|
||||
@@ -211,14 +280,29 @@ export function logError(
|
||||
operation?: string
|
||||
module?: string
|
||||
userId?: string
|
||||
requestId?: string
|
||||
batchId?: string
|
||||
duration?: number
|
||||
context?: Record<string, unknown>
|
||||
} = {}
|
||||
): void {
|
||||
const { message: customMessage, operation, module: moduleName, userId, context } = options
|
||||
const {
|
||||
message: customMessage,
|
||||
operation,
|
||||
module: moduleName,
|
||||
userId,
|
||||
requestId,
|
||||
batchId,
|
||||
duration,
|
||||
context
|
||||
} = options
|
||||
const { message, metadata } = formatErrorForLogging(error, {
|
||||
operation,
|
||||
module: moduleName,
|
||||
userId,
|
||||
requestId,
|
||||
batchId,
|
||||
duration,
|
||||
...context
|
||||
})
|
||||
|
||||
@@ -241,3 +325,77 @@ export function throwAfterLogging(
|
||||
logError(logger, error, options)
|
||||
throw error
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced error logging helper with automatic context injection
|
||||
*
|
||||
* Simplifies error logging by automatically injecting requestId from async context
|
||||
* and providing a concise API for common logging scenarios.
|
||||
*
|
||||
* @param logger - Logger instance with error method
|
||||
* @param error - The error to log (Error object or Error-like)
|
||||
* @param context - Business context for the error
|
||||
* @param context.operation - Business operation (REQUIRED for enhanced logging)
|
||||
* @param context.userId - User ID performing the operation
|
||||
* @param context.batchId - Batch identifier for batch operations
|
||||
* @param context.duration - Operation duration in milliseconds (e.g., from performance monitoring)
|
||||
* @param context.orderNumbers - Order numbers related to the operation
|
||||
* @param context.materialCodes - Material codes related to the operation
|
||||
* @param context.module - Module/Service name (defaults to 'unknown' if not provided)
|
||||
* @param customMessage - Optional custom message to prepend (if not provided, uses error message)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { enhancedLogError } from './error-utils'
|
||||
*
|
||||
* // Simple usage with auto-injected requestId
|
||||
* enhancedLogError(logger, error, { operation: 'extract', userId: 'user123' })
|
||||
*
|
||||
* // With performance metrics
|
||||
* const duration = Date.now() - startTime
|
||||
* enhancedLogError(logger, error, {
|
||||
* operation: 'clean',
|
||||
* userId: 'user456',
|
||||
* batchId: 'batch-001',
|
||||
* duration,
|
||||
* orderNumbers: ['ORD-123', 'ORD-124']
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function enhancedLogError(
|
||||
logger: { error: (message: string, meta?: Record<string, unknown>) => void },
|
||||
error: unknown,
|
||||
context: {
|
||||
operation: string
|
||||
userId?: string
|
||||
batchId?: string
|
||||
duration?: number
|
||||
orderNumbers?: string[]
|
||||
materialCodes?: string[]
|
||||
module?: string
|
||||
},
|
||||
customMessage?: string
|
||||
): void {
|
||||
const {
|
||||
operation,
|
||||
userId,
|
||||
batchId,
|
||||
duration,
|
||||
orderNumbers,
|
||||
materialCodes,
|
||||
module: moduleName
|
||||
} = context
|
||||
|
||||
logError(logger, error, {
|
||||
message: customMessage,
|
||||
operation,
|
||||
module: moduleName,
|
||||
userId,
|
||||
batchId,
|
||||
duration,
|
||||
context: {
|
||||
...(orderNumbers && { orderNumbers }),
|
||||
...(materialCodes && { materialCodes })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user