Skip to content

[duplicate-code] Duplicate Code Pattern: Logger Functions with Repeated Mutex Lock Patterns #971

@github-actions

Description

@github-actions

🔍 Duplicate Code Pattern: Logger Functions with Mutex Lock Patterns

Part of duplicate code analysis: #970

Summary

The logger package contains 12+ nearly identical functions with repeated mutex lock/unlock patterns across three files. Each set of 4 functions (Info/Warn/Error/Debug) follows the exact same structure, differing only in the log level parameter.

Duplication Details

Pattern: Repeated Logger Function Structure

  • Severity: High
  • Occurrences: 12+ instances across 3 files
  • Locations:
    • internal/logger/file_logger.go - Lines 114-151 (4 functions: LogInfo, LogWarn, LogError, LogDebug)
    • internal/logger/server_file_logger.go - Lines 162-211 (4 functions: LogInfoWithServer, LogWarnWithServer, LogErrorWithServer, LogDebugWithServer)
    • internal/logger/markdown_logger.go - Lines 187-204 (4 functions: LogInfoMd, LogWarnMd, LogErrorMd, LogDebugMd)

Code Sample - file_logger.go Pattern:

func LogInfo(category, format string, args ...interface{}) {
	globalLoggerMu.RLock()
	defer globalLoggerMu.RUnlock()

	if globalFileLogger != nil {
		globalFileLogger.Log(LogLevelInfo, category, format, args...)
	}
}

func LogWarn(category, format string, args ...interface{}) {
	globalLoggerMu.RLock()
	defer globalLoggerMu.RUnlock()

	if globalFileLogger != nil {
		globalFileLogger.Log(LogLevelWarn, category, format, args...)
	}
}
// ... LogError and LogDebug follow the same pattern

Code Sample - server_file_logger.go Pattern:

func LogInfoWithServer(serverID, category, format string, args ...interface{}) {
	globalServerLoggerMu.RLock()
	defer globalServerLoggerMu.RUnlock()

	if globalServerFileLogger != nil {
		globalServerFileLogger.Log(serverID, LogLevelInfo, category, format, args...)
	}

	// Also log to the main log file for unified view
	LogInfo(category, "[%s] %s", serverID, fmt.Sprintf(format, args...))
}
// ... LogWarnWithServer, LogErrorWithServer, LogDebugWithServer follow the same pattern

Impact Analysis

  • Maintainability: High Impact - Any change to logging behavior (e.g., adding tracing, modifying mutex strategy) requires updating 12+ functions identically
  • Bug Risk: High - If one function is updated but others are missed, inconsistent logging behavior results
  • Code Bloat: ~120+ lines of repetitive code (12 functions × ~10 lines each)
  • Testing Complexity: Each function requires separate testing, increasing test maintenance

Refactoring Recommendations

1. Extract Generic Logger Helper Function (Recommended)

Extract common mutex lock/unlock and nil-check pattern into a generic helper:

// In file_logger.go
func logWithLevel(level LogLevel, category, format string, args ...interface{}) {
	globalLoggerMu.RLock()
	defer globalLoggerMu.RUnlock()

	if globalFileLogger != nil {
		globalFileLogger.Log(level, category, format, args...)
	}
}

func LogInfo(category, format string, args ...interface{}) {
	logWithLevel(LogLevelInfo, category, format, args...)
}

func LogWarn(category, format string, args ...interface{}) {
	logWithLevel(LogLevelWarn, category, format, args...)
}

func LogError(category, format string, args ...interface{}) {
	logWithLevel(LogLevelError, category, format, args...)
}

func LogDebug(category, format string, args ...interface{}) {
	logWithLevel(LogLevelDebug, category, format, args...)
}
  • Location: internal/logger/file_logger.go (add logWithLevel helper)
  • Estimated Effort: 2-3 hours
  • Benefits:
    • Reduces code from ~40 lines to ~20 lines per file
    • Centralizes mutex handling logic
    • Makes future changes safer (single point of modification)

2. Apply Same Pattern to server_file_logger.go

// In server_file_logger.go
func logWithLevelAndServer(serverID string, level LogLevel, category, format string, args ...interface{}) {
	globalServerLoggerMu.RLock()
	defer globalServerLoggerMu.RUnlock()

	if globalServerFileLogger != nil {
		globalServerFileLogger.Log(serverID, level, category, format, args...)
	}

	// Also log to the main log file for unified view
	switch level {
	case LogLevelInfo:
		LogInfo(category, "[%s] %s", serverID, fmt.Sprintf(format, args...))
	case LogLevelWarn:
		LogWarn(category, "[%s] %s", serverID, fmt.Sprintf(format, args...))
	// ... other levels
	}
}
  • Location: internal/logger/server_file_logger.go
  • Estimated Effort: 2-3 hours
  • Benefits: Same as above, plus eliminates redundant dual-logging logic

3. Apply Same Pattern to markdown_logger.go

Similar refactoring for LogInfoMd, LogWarnMd, LogErrorMd, LogDebugMd:

// In markdown_logger.go
func logWithMarkdownLevel(level LogLevel, category, format string, args ...interface{}) {
	logFn := getLogFunctionForLevel(level)
	logWithMarkdown(level, logFn, category, format, args...)
}
  • Location: internal/logger/markdown_logger.go
  • Estimated Effort: 1-2 hours

Implementation Checklist

  • Review duplication findings with team
  • Create helper function logWithLevel in file_logger.go
  • Refactor LogInfo, LogWarn, LogError, LogDebug to use helper
  • Create helper function logWithLevelAndServer in server_file_logger.go
  • Refactor LogInfoWithServer, LogWarnWithServer, etc. to use helper
  • Create helper function for markdown logger
  • Refactor LogInfoMd, LogWarnMd, etc. to use helper
  • Run make test-all to verify no functionality broken
  • Run make lint to ensure code quality
  • Update any affected tests if needed

Parent Issue

See parent analysis report: #970
Related to #970

AI generated by Duplicate Code Detector

  • expires on Feb 23, 2026, 10:32 AM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions