Part of duplicate code analysis: #4540
Summary
Each of the four concrete logger types in internal/logger/ (FileLogger, MarkdownLogger, JSONLLogger, ToolsLogger) follows an identical structural pattern with a setup*Logger function and a handle*LoggerError function, plus a global variable + mutex pair. This results in 8 near-identical function pairs across 4 files.
Duplication Details
Pattern: Logger type setup/error-handler boilerplate
Each Init*Logger also repeats the same two-liner:
func InitXxxLogger(logDir, fileName string) error {
logger, err := initLogger(logDir, fileName, os.O_APPEND, setupXxxLogger, handleXxxLoggerError)
initGlobalLogger(&globalXxxMu, &globalXxxLogger, logger)
return err
}
Impact Analysis
- Maintainability: Adding a new logger type requires creating 3 new boilerplate items (setup function, error handler, Init function) following an implicit convention — easy to get wrong.
- Bug Risk: A divergence in one logger's fallback behavior may not be caught if the pattern isn't checked across all types. For example,
JSONLLogger intentionally has no fallback but this is only visible by reading each file.
- Code Bloat: ~50 lines of near-identical structural code spread across 4 files.
Refactoring Recommendations
-
Document the pattern explicitly (low-effort quick win)
- Add a comment in
global_helpers.go or a new LOGGER_PATTERN.md explaining the setup/error-handler convention so future authors know what to implement.
-
Introduce a loggerFactory[T] interface or type alias (medium effort)
- Define a type for the setup and error-handler function pair and centralize their invocation in
initLogger, so each concrete logger only needs to supply struct initialization logic.
- Estimated effort: 2–3 hours
- Benefits: New logger types are just struct definitions + field mappings; no boilerplate functions needed.
Implementation Checklist
Parent Issue
See parent analysis report: #4540
Related to #4540
Generated by Duplicate Code Detector · ● 1.9M · ◷
Part of duplicate code analysis: #4540
Summary
Each of the four concrete logger types in
internal/logger/(FileLogger,MarkdownLogger,JSONLLogger,ToolsLogger) follows an identical structural pattern with asetup*Loggerfunction and ahandle*LoggerErrorfunction, plus a global variable + mutex pair. This results in 8 near-identical function pairs across 4 files.Duplication Details
Pattern: Logger type setup/error-handler boilerplate
Severity: Medium
Occurrences: 4 logger types × 2 functions = 8 functions with the same signature and responsibility
Locations:
internal/logger/file_logger.go(lines 27–55):setupFileLogger/handleFileLoggerErrorinternal/logger/tools_logger.go(lines 44–80):setupToolsLogger/handleToolsLoggerErrorinternal/logger/markdown_logger.go(lines 28–51):setupMarkdownLogger/handleMarkdownLoggerErrorinternal/logger/jsonl_logger.go(lines 41–65):setupJSONLLogger/handleJSONLLoggerErrorCode Sample (setup functions follow the same shape):
Each
Init*Loggeralso repeats the same two-liner:Impact Analysis
JSONLLoggerintentionally has no fallback but this is only visible by reading each file.Refactoring Recommendations
Document the pattern explicitly (low-effort quick win)
global_helpers.goor a newLOGGER_PATTERN.mdexplaining the setup/error-handler convention so future authors know what to implement.Introduce a
loggerFactory[T]interface or type alias (medium effort)initLogger, so each concrete logger only needs to supply struct initialization logic.Implementation Checklist
useFallbackbehavior is consistent across logger typesmake testto ensure no regressionsParent Issue
See parent analysis report: #4540
Related to #4540