mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	feat(llm): update pipeline steps
This commit is contained in:
		
							
								
								
									
										426
									
								
								apps/server/src/services/llm/pipeline/logging_service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								apps/server/src/services/llm/pipeline/logging_service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,426 @@ | ||||
| /** | ||||
|  * Logging Service - Phase 2.3 Implementation | ||||
|  *  | ||||
|  * Structured logging with: | ||||
|  * - Proper log levels | ||||
|  * - Request ID tracking | ||||
|  * - Conditional debug logging | ||||
|  * - No production debug statements | ||||
|  */ | ||||
|  | ||||
| import log from '../../../log.js'; | ||||
| import configurationService from './configuration_service.js'; | ||||
|  | ||||
| // Log levels | ||||
| export enum LogLevel { | ||||
|     ERROR = 'error', | ||||
|     WARN = 'warn', | ||||
|     INFO = 'info', | ||||
|     DEBUG = 'debug' | ||||
| } | ||||
|  | ||||
| // Log entry interface | ||||
| export interface LogEntry { | ||||
|     timestamp: Date; | ||||
|     level: LogLevel; | ||||
|     requestId?: string; | ||||
|     message: string; | ||||
|     data?: any; | ||||
|     error?: Error; | ||||
|     duration?: number; | ||||
| } | ||||
|  | ||||
| // Structured log data | ||||
| export interface LogContext { | ||||
|     requestId?: string; | ||||
|     userId?: string; | ||||
|     sessionId?: string; | ||||
|     provider?: string; | ||||
|     model?: string; | ||||
|     operation?: string; | ||||
|     [key: string]: any; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Logging Service Implementation | ||||
|  */ | ||||
| export class LoggingService { | ||||
|     private enabled: boolean = true; | ||||
|     private logLevel: LogLevel = LogLevel.INFO; | ||||
|     private debugEnabled: boolean = false; | ||||
|     private requestContexts: Map<string, LogContext> = new Map(); | ||||
|     private logBuffer: LogEntry[] = []; | ||||
|     private readonly MAX_BUFFER_SIZE = 1000; | ||||
|  | ||||
|     constructor() { | ||||
|         this.initialize(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Initialize logging configuration | ||||
|      */ | ||||
|     private initialize(): void { | ||||
|         try { | ||||
|             const debugConfig = configurationService.getDebugConfig(); | ||||
|             this.enabled = debugConfig.enabled; | ||||
|             this.debugEnabled = debugConfig.logLevel === 'debug'; | ||||
|             this.logLevel = this.parseLogLevel(debugConfig.logLevel); | ||||
|         } catch (error) { | ||||
|             // Fall back to defaults if configuration is not available | ||||
|             this.enabled = true; | ||||
|             this.logLevel = LogLevel.INFO; | ||||
|             this.debugEnabled = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parse log level from string | ||||
|      */ | ||||
|     private parseLogLevel(level: string): LogLevel { | ||||
|         switch (level?.toLowerCase()) { | ||||
|             case 'error': return LogLevel.ERROR; | ||||
|             case 'warn': return LogLevel.WARN; | ||||
|             case 'info': return LogLevel.INFO; | ||||
|             case 'debug': return LogLevel.DEBUG; | ||||
|             default: return LogLevel.INFO; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if a log level should be logged | ||||
|      */ | ||||
|     private shouldLog(level: LogLevel): boolean { | ||||
|         if (!this.enabled) return false; | ||||
|          | ||||
|         const levels = [LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG]; | ||||
|         const currentIndex = levels.indexOf(this.logLevel); | ||||
|         const messageIndex = levels.indexOf(level); | ||||
|          | ||||
|         return messageIndex <= currentIndex; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Format log message with context | ||||
|      */ | ||||
|     private formatMessage(message: string, context?: LogContext): string { | ||||
|         if (!context?.requestId) { | ||||
|             return message; | ||||
|         } | ||||
|         return `[${context.requestId}] ${message}`; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write log entry | ||||
|      */ | ||||
|     private writeLog(entry: LogEntry): void { | ||||
|         // Add to buffer for debugging | ||||
|         this.bufferLog(entry); | ||||
|          | ||||
|         // Skip debug logs in production | ||||
|         if (entry.level === LogLevel.DEBUG && !this.debugEnabled) { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         // Format message with request ID if present | ||||
|         const formattedMessage = this.formatMessage(entry.message, { requestId: entry.requestId }); | ||||
|          | ||||
|         // Log based on level | ||||
|         switch (entry.level) { | ||||
|             case LogLevel.ERROR: | ||||
|                 if (entry.error) { | ||||
|                     log.error(formattedMessage, entry.error); | ||||
|                 } else { | ||||
|                     log.error(formattedMessage, entry.data); | ||||
|                 } | ||||
|                 break; | ||||
|                  | ||||
|             case LogLevel.WARN: | ||||
|                 log.warn(formattedMessage, entry.data); | ||||
|                 break; | ||||
|                  | ||||
|             case LogLevel.INFO: | ||||
|                 if (entry.data && Object.keys(entry.data).length > 0) { | ||||
|                     log.info(`${formattedMessage} - ${JSON.stringify(entry.data)}`); | ||||
|                 } else { | ||||
|                     log.info(formattedMessage); | ||||
|                 } | ||||
|                 break; | ||||
|                  | ||||
|             case LogLevel.DEBUG: | ||||
|                 // Only log debug messages if debug is enabled | ||||
|                 if (this.debugEnabled) { | ||||
|                     if (entry.data) { | ||||
|                         log.info(`[DEBUG] ${formattedMessage} - ${JSON.stringify(entry.data)}`); | ||||
|                     } else { | ||||
|                         log.info(`[DEBUG] ${formattedMessage}`); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Buffer log entry for debugging | ||||
|      */ | ||||
|     private bufferLog(entry: LogEntry): void { | ||||
|         this.logBuffer.push(entry); | ||||
|          | ||||
|         // Trim buffer if it exceeds max size | ||||
|         if (this.logBuffer.length > this.MAX_BUFFER_SIZE) { | ||||
|             this.logBuffer = this.logBuffer.slice(-this.MAX_BUFFER_SIZE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Main logging method | ||||
|      */ | ||||
|     log(level: LogLevel, message: string, data?: any): void { | ||||
|         if (!this.shouldLog(level)) return; | ||||
|          | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level, | ||||
|             message, | ||||
|             data: data instanceof Error ? undefined : data, | ||||
|             error: data instanceof Error ? data : undefined | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Log with request context | ||||
|      */ | ||||
|     logWithContext(level: LogLevel, message: string, context: LogContext, data?: any): void { | ||||
|         if (!this.shouldLog(level)) return; | ||||
|          | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level, | ||||
|             requestId: context.requestId, | ||||
|             message, | ||||
|             data: { ...context, ...data } | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a logger with a fixed request ID | ||||
|      */ | ||||
|     withRequestId(requestId: string): { | ||||
|         requestId: string; | ||||
|         log: (level: LogLevel, message: string, data?: any) => void; | ||||
|         error: (message: string, error?: Error | any) => void; | ||||
|         warn: (message: string, data?: any) => void; | ||||
|         info: (message: string, data?: any) => void; | ||||
|         debug: (message: string, data?: any) => void; | ||||
|         startTimer: (operation: string) => () => void; | ||||
|     } { | ||||
|         const self = this; | ||||
|          | ||||
|         return { | ||||
|             requestId, | ||||
|              | ||||
|             log(level: LogLevel, message: string, data?: any): void { | ||||
|                 self.logWithContext(level, message, { requestId }, data); | ||||
|             }, | ||||
|              | ||||
|             error(message: string, error?: Error | any): void { | ||||
|                 self.logWithContext(LogLevel.ERROR, message, { requestId }, error); | ||||
|             }, | ||||
|              | ||||
|             warn(message: string, data?: any): void { | ||||
|                 self.logWithContext(LogLevel.WARN, message, { requestId }, data); | ||||
|             }, | ||||
|              | ||||
|             info(message: string, data?: any): void { | ||||
|                 self.logWithContext(LogLevel.INFO, message, { requestId }, data); | ||||
|             }, | ||||
|              | ||||
|             debug(message: string, data?: any): void { | ||||
|                 self.logWithContext(LogLevel.DEBUG, message, { requestId }, data); | ||||
|             }, | ||||
|              | ||||
|             startTimer(operation: string): () => void { | ||||
|                 const startTime = Date.now(); | ||||
|                 return () => { | ||||
|                     const duration = Date.now() - startTime; | ||||
|                     self.logWithContext(LogLevel.DEBUG, `${operation} completed`, { requestId }, { duration }); | ||||
|                 }; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Start a timer for performance tracking | ||||
|      */ | ||||
|     startTimer(operation: string, requestId?: string): () => void { | ||||
|         const startTime = Date.now(); | ||||
|          | ||||
|         return () => { | ||||
|             const duration = Date.now() - startTime; | ||||
|             const entry: LogEntry = { | ||||
|                 timestamp: new Date(), | ||||
|                 level: LogLevel.DEBUG, | ||||
|                 requestId, | ||||
|                 message: `${operation} completed in ${duration}ms`, | ||||
|                 duration | ||||
|             }; | ||||
|              | ||||
|             if (this.shouldLog(LogLevel.DEBUG)) { | ||||
|                 this.writeLog(entry); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Log error with stack trace | ||||
|      */ | ||||
|     error(message: string, error?: Error | any, requestId?: string): void { | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level: LogLevel.ERROR, | ||||
|             requestId, | ||||
|             message, | ||||
|             error: error instanceof Error ? error : new Error(String(error)) | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Log warning | ||||
|      */ | ||||
|     warn(message: string, data?: any, requestId?: string): void { | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level: LogLevel.WARN, | ||||
|             requestId, | ||||
|             message, | ||||
|             data | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Log info | ||||
|      */ | ||||
|     info(message: string, data?: any, requestId?: string): void { | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level: LogLevel.INFO, | ||||
|             requestId, | ||||
|             message, | ||||
|             data | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Log debug (only in debug mode) | ||||
|      */ | ||||
|     debug(message: string, data?: any, requestId?: string): void { | ||||
|         if (!this.debugEnabled) return; | ||||
|          | ||||
|         const entry: LogEntry = { | ||||
|             timestamp: new Date(), | ||||
|             level: LogLevel.DEBUG, | ||||
|             requestId, | ||||
|             message, | ||||
|             data | ||||
|         }; | ||||
|          | ||||
|         this.writeLog(entry); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set request context | ||||
|      */ | ||||
|     setRequestContext(requestId: string, context: LogContext): void { | ||||
|         this.requestContexts.set(requestId, context); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get request context | ||||
|      */ | ||||
|     getRequestContext(requestId: string): LogContext | undefined { | ||||
|         return this.requestContexts.get(requestId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear request context | ||||
|      */ | ||||
|     clearRequestContext(requestId: string): void { | ||||
|         this.requestContexts.delete(requestId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get recent logs for debugging | ||||
|      */ | ||||
|     getRecentLogs(count: number = 100, level?: LogLevel): LogEntry[] { | ||||
|         let logs = [...this.logBuffer]; | ||||
|          | ||||
|         if (level) { | ||||
|             logs = logs.filter(entry => entry.level === level); | ||||
|         } | ||||
|          | ||||
|         return logs.slice(-count); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear log buffer | ||||
|      */ | ||||
|     clearBuffer(): void { | ||||
|         this.logBuffer = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set log level dynamically | ||||
|      */ | ||||
|     setLogLevel(level: LogLevel): void { | ||||
|         this.logLevel = level; | ||||
|         this.debugEnabled = level === LogLevel.DEBUG; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get current log level | ||||
|      */ | ||||
|     getLogLevel(): LogLevel { | ||||
|         return this.logLevel; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Enable/disable logging | ||||
|      */ | ||||
|     setEnabled(enabled: boolean): void { | ||||
|         this.enabled = enabled; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if logging is enabled | ||||
|      */ | ||||
|     isEnabled(): boolean { | ||||
|         return this.enabled; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if debug logging is enabled | ||||
|      */ | ||||
|     isDebugEnabled(): boolean { | ||||
|         return this.debugEnabled; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reload configuration | ||||
|      */ | ||||
|     reloadConfiguration(): void { | ||||
|         this.initialize(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Export singleton instance | ||||
| const loggingService = new LoggingService(); | ||||
| export default loggingService; | ||||
		Reference in New Issue
	
	Block a user