Part of duplicate code analysis: #2754
Summary
Six logger types in internal/logger/ each implement nearly identical initialization code using the same initLogger(...) call pattern with inline setup and error-handler closures. This results in ~26 lines of structural boilerplate repeated per logger type.
Duplication Details
Pattern: Repeated initLogger setup/error-handler closures per logger type
- Severity: High
- Occurrences: 6 logger initializers
- Locations:
internal/logger/file_logger.go (lines ~29–54)
internal/logger/markdown_logger.go (lines ~29–54)
internal/logger/jsonl_logger.go (lines ~42–68)
internal/logger/server_file_logger.go (lines ~28–54)
internal/logger/tools_logger.go (lines ~44–82)
internal/logger/rpc_logger.go (similar pattern)
Code Sample — FileLogger:
logger, err := initLogger(
logDir, fileName, os.O_APPEND,
func(file *os.File, logDir, fileName string) (*FileLogger, error) {
fl := &FileLogger{
logDir: logDir,
fileName: fileName,
logFile: file,
logger: log.New(file, "", 0),
}
log.Printf("Logging to file: %s", filepath.Join(logDir, fileName))
return fl, nil
},
func(err error, logDir, fileName string) (*FileLogger, error) {
log.Printf("WARNING: Failed to initialize log file: %v", err)
log.Printf("WARNING: Falling back to stdout for logging")
fl := &FileLogger{
logDir: logDir,
fileName: fileName,
useFallback: true,
logger: log.New(os.Stdout, "", 0),
}
return fl, nil
},
)
initGlobalFileLogger(logger)
MarkdownLogger has an identical outer structure with a different struct type and flag values. Same applies to JSONL, RPC, ServerFile, and Tools loggers.
Impact Analysis
- Maintainability: Any change to the init pattern (e.g., adding a new field) must be repeated in all 6 files
- Bug Risk: Divergence between loggers is already present (some log startup messages, others silently fall back)
- Code Bloat: ~150 lines that could be reduced to ~30 lines with named setup/error functions
Refactoring Recommendations
-
Extract named setup and error functions per logger type
- Replace closures with named
setupXLogger and handleXLoggerError functions
- Estimated effort: 2–3 hours
- File: keep in respective
*_logger.go files
-
Standardize fallback logging across all loggers (currently inconsistent: some print warnings, others are silent)
Implementation Checklist
Parent Issue
See parent analysis report: #2754
Related to #2754
Generated by Duplicate Code Detector · ◷
Part of duplicate code analysis: #2754
Summary
Six logger types in
internal/logger/each implement nearly identical initialization code using the sameinitLogger(...)call pattern with inline setup and error-handler closures. This results in ~26 lines of structural boilerplate repeated per logger type.Duplication Details
Pattern: Repeated
initLoggersetup/error-handler closures per logger typeinternal/logger/file_logger.go(lines ~29–54)internal/logger/markdown_logger.go(lines ~29–54)internal/logger/jsonl_logger.go(lines ~42–68)internal/logger/server_file_logger.go(lines ~28–54)internal/logger/tools_logger.go(lines ~44–82)internal/logger/rpc_logger.go(similar pattern)Code Sample — FileLogger:
MarkdownLogger has an identical outer structure with a different struct type and flag values. Same applies to JSONL, RPC, ServerFile, and Tools loggers.
Impact Analysis
Refactoring Recommendations
Extract named setup and error functions per logger type
setupXLoggerandhandleXLoggerErrorfunctions*_logger.gofilesStandardize fallback logging across all loggers (currently inconsistent: some print warnings, others are silent)
Implementation Checklist
make test-unitto verify no regressionsmake agent-finishedto confirm build and lint passParent Issue
See parent analysis report: #2754
Related to #2754