feat(logging-p0): complete Wave 2 - Auth/Extractor/Cleaner services transformed

This commit is contained in:
Misaka
2026-04-04 10:39:06 +08:00
parent 24d9bfebaf
commit 78a3066904
14 changed files with 3183 additions and 416 deletions

View File

@@ -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 })
}
})
}