@@ -72,6 +72,128 @@ import (
7272//
7373// When adding a new logger type, follow this pattern to ensure consistent behavior.
7474
75+ // Initialization Pattern for Logger Types
76+ //
77+ // The logger package follows a consistent initialization pattern across all logger types,
78+ // with support for customizable fallback behavior. This section documents the pattern
79+ // and explains the different fallback strategies used by each logger type.
80+ //
81+ // Standard Initialization Pattern:
82+ //
83+ // All logger types use the initLogger() generic helper function for initialization:
84+ //
85+ // func Init*Logger(logDir, fileName string) error {
86+ // logger, err := initLogger(
87+ // logDir, fileName, fileFlags,
88+ // setupFunc, // Configure logger after file is opened
89+ // errorHandler, // Handle initialization failures
90+ // )
91+ // initGlobal*Logger(logger)
92+ // return err
93+ // }
94+ //
95+ // The initLogger() helper:
96+ // 1. Attempts to create the log directory (if needed)
97+ // 2. Opens the log file with specified flags (os.O_APPEND, os.O_TRUNC, etc.)
98+ // 3. Calls setupFunc to configure the logger instance
99+ // 4. On error, calls errorHandler to implement fallback behavior
100+ // 5. Returns the initialized logger and any error
101+ //
102+ // Fallback Behavior Strategies:
103+ //
104+ // Different logger types implement different fallback strategies based on their purpose:
105+ //
106+ // 1. FileLogger - Stdout Fallback:
107+ // - Purpose: Operational logs must always be visible
108+ // - Fallback: Redirects to stdout if log directory/file creation fails
109+ // - Error: Returns nil (never fails, always provides output)
110+ // - Use case: Critical operational messages that must be seen
111+ //
112+ // Example error handler:
113+ // func(err error, logDir, fileName string) (*FileLogger, error) {
114+ // log.Printf("WARNING: Failed to initialize log file: %v", err)
115+ // log.Printf("WARNING: Falling back to stdout for logging")
116+ // return &FileLogger{
117+ // logDir: logDir,
118+ // fileName: fileName,
119+ // useFallback: true,
120+ // logger: log.New(os.Stdout, "", 0),
121+ // }, nil
122+ // }
123+ //
124+ // 2. MarkdownLogger - Silent Fallback:
125+ // - Purpose: GitHub workflow preview logs (optional enhancement)
126+ // - Fallback: Sets useFallback flag, produces no output
127+ // - Error: Returns nil (never fails, silently disables)
128+ // - Use case: Nice-to-have logs that shouldn't block operations
129+ //
130+ // Example error handler:
131+ // func(err error, logDir, fileName string) (*MarkdownLogger, error) {
132+ // return &MarkdownLogger{
133+ // logDir: logDir,
134+ // fileName: fileName,
135+ // useFallback: true,
136+ // }, nil
137+ // }
138+ //
139+ // 3. JSONLLogger - Strict Mode:
140+ // - Purpose: Machine-readable RPC message logs for analysis
141+ // - Fallback: None - returns error immediately
142+ // - Error: Returns error to caller
143+ // - Use case: Structured data that requires file storage
144+ //
145+ // Example error handler:
146+ // func(err error, logDir, fileName string) (*JSONLLogger, error) {
147+ // return nil, err
148+ // }
149+ //
150+ // 4. ServerFileLogger - Unified Fallback:
151+ // - Purpose: Per-server log files for troubleshooting
152+ // - Fallback: Sets useFallback flag, logs to unified logger only
153+ // - Error: Returns nil (never fails, falls back to unified logging)
154+ // - Use case: Per-server logs are helpful but not required
155+ //
156+ // Note: ServerFileLogger doesn't use initLogger() because it creates
157+ // files on-demand, but follows the same fallback philosophy.
158+ //
159+ // Global Logger Management:
160+ //
161+ // After initialization, all logger types register themselves as global loggers
162+ // using the generic initGlobal*Logger() helpers from global_helpers.go:
163+ //
164+ // - initGlobalFileLogger()
165+ // - initGlobalJSONLLogger()
166+ // - initGlobalMarkdownLogger()
167+ // - initGlobalServerFileLogger()
168+ //
169+ // These helpers ensure thread-safe initialization with proper cleanup of any
170+ // existing logger instance.
171+ //
172+ // When to Use Each Logger Type:
173+ //
174+ // - FileLogger: Required operational logs (startup, errors, warnings)
175+ // - MarkdownLogger: Optional GitHub workflow preview logs
176+ // - JSONLLogger: Structured RPC message logs for tooling/analysis
177+ // - ServerFileLogger: Per-server troubleshooting logs
178+ //
179+ // Adding a New Logger Type:
180+ //
181+ // When adding a new logger type:
182+ // 1. Implement Close() method following the Close Pattern (above)
183+ // 2. Add type to closableLogger constraint in global_helpers.go
184+ // 3. Use initLogger() for initialization with appropriate fallback strategy
185+ // 4. Add initGlobal*Logger() and closeGlobal*Logger() helpers
186+ // 5. Document the fallback strategy and use case
187+ //
188+ // This consistent pattern ensures:
189+ // - Predictable behavior across all loggers
190+ // - Easy to understand fallback strategies
191+ // - Minimal code duplication
192+ // - Type-safe global logger management
193+ //
194+ // See file_logger.go, jsonl_logger.go, markdown_logger.go, and server_file_logger.go
195+ // for complete implementation examples.
196+
75197// closeLogFile is a common helper for closing log files with consistent error handling.
76198// It syncs buffered data before closing and handles errors appropriately.
77199// The mutex should already be held by the caller.
@@ -148,7 +270,7 @@ type loggerErrorHandlerFunc[T closableLogger] func(err error, logDir, fileName s
148270// - fileName: Name of the log file
149271// - flags: File opening flags (e.g., os.O_APPEND, os.O_TRUNC)
150272// - setup: Function to configure the logger after the file is opened
151- // - onError: Function to handle initialization errors (can return fallback or error )
273+ // - onError: Function to handle initialization errors (implements fallback strategy )
152274//
153275// Returns:
154276// - T: The initialized logger instance
@@ -157,7 +279,13 @@ type loggerErrorHandlerFunc[T closableLogger] func(err error, logDir, fileName s
157279// This function:
158280// 1. Attempts to open the log file with the specified flags
159281// 2. If successful, calls the setup function to configure the logger
160- // 3. If unsuccessful, calls the error handler to decide on fallback behavior
282+ // 3. If unsuccessful, calls the error handler to implement the logger's fallback strategy
283+ //
284+ // The onError handler determines the fallback behavior. See "Initialization Pattern for Logger Types"
285+ // documentation above for details on fallback strategies:
286+ // - FileLogger: Falls back to stdout
287+ // - MarkdownLogger: Silent fallback (no output)
288+ // - JSONLLogger: Returns error (no fallback)
161289func initLogger [T closableLogger ](
162290 logDir , fileName string ,
163291 flags int ,
0 commit comments