Skip to content

[duplicate-code] Duplicate Code Pattern: ServerFileLogger.Close() Reinvents closeLogFile Sync+Close Pattern #8172

Closed

Description

@github-actions

🔍 Duplicate Code Pattern: ServerFileLogger.Close() Sync+Close Loop

Part of duplicate code analysis: #8170

Summary

internal/logger/server_file_logger.go's Close() method manually implements a multi-file Sync+Close loop with firstErr tracking, while the existing closeLogFile helper in internal/logger/common.go already encapsulates the per-file Sync+Close pattern. The per-file helper is unused inside Close(), leading to divergent implementations of the same Sync→log→Close sequence.

Duplication Details

Pattern: Repeated per-file Sync+Close with firstErr collection

  • Severity: Medium
  • Occurrences: 2 related instances
  • Locations:
    • internal/logger/server_file_logger.go — ServerFileLogger.Close() (lines 105–134)
    • internal/logger/common.go — closeLogFile() (lines 351–365)

ServerFileLogger.Close() inner loop (server_file_logger.go:113-133):

var firstErr error
for serverID, file := range sfl.files {
    if err := file.Sync(); err != nil {
        log.Printf("WARNING: Failed to sync log file for server %s: %v", serverID, err)
        if firstErr == nil {
            firstErr = err
        }
    }
    if err := file.Close(); err != nil {
        log.Printf("WARNING: Failed to close log file for server %s: %v", serverID, err)
        if firstErr == nil {
            firstErr = err
        }
    }
}

Existing closeLogFile helper (common.go:351-365):

func closeLogFile(file *os.File, mu *sync.Mutex, loggerName string) error {
    if file == nil {
        return nil
    }
    if err := file.Sync(); err != nil {
        log.Printf("WARNING: Failed to sync log file for %s: %v", loggerName, err)
    }
    return file.Close()
}

The two diverge on sync-error handling: closeLogFile discards sync errors (logs only), while the Close() loop tracks sync errors in firstErr. This inconsistency may be unintentional.

Impact Analysis

  • Maintainability: Adding a new step to file cleanup (e.g., chmod or rename) would need to be applied in both locations
  • Bug Risk: The inconsistent sync-error tracking between the two implementations is a latent behavioral discrepancy — one suppresses sync errors, the other propagates them
  • Code Bloat: ~15 lines of the Close() loop replicate logic already expressed in closeLogFile

Refactoring Recommendations

1. Extract syncAndCloseFile helper (align error handling first)

Decide on consistent sync-error behavior, then extract:

// syncAndCloseFile syncs and closes a single file, returning the first error encountered.
// Both sync and close errors are returned (unlike closeLogFile which discards sync errors).
func syncAndCloseFile(file *os.File, loggerName string) error {
    var firstErr error
    if err := file.Sync(); err != nil {
        log.Printf("WARNING: Failed to sync log file for %s: %v", loggerName, err)
        firstErr = err
    }
    if err := file.Close(); err != nil {
        log.Printf("WARNING: Failed to close log file for %s: %v", loggerName, err)
        if firstErr == nil {
            firstErr = err
        }
    }
    return firstErr
}

ServerFileLogger.Close() inner loop becomes:

var firstErr error
for serverID, file := range sfl.files {
    if err := syncAndCloseFile(file, "server:"+serverID); err != nil && firstErr == nil {
        firstErr = err
    }
}

2. (Alternative) Make closeLogFile track sync errors and use it in the loop

If sync-error propagation is desired, update closeLogFile to return both errors and reuse it.

Implementation Checklist

  • Decide on canonical sync-error behavior (propagate vs. log-only)
  • Extract syncAndCloseFile (or update closeLogFile) in common.go
  • Refactor ServerFileLogger.Close() to use the shared helper
  • Verify tests pass

Parent Issue

See parent analysis report: #8170
Related to #8170

Generated by Duplicate Code Detector · 936.3 AIC · ⊞ 35.7K · ◷

  • expires on Jul 4, 2026, 3:46 AM UTC

Activity

  1. github-actions commented on Jun 28, 2026

    @github-actions
    ContributorAuthor

    🤖 This is an automated response from Repo Assist.

    This has already been addressed in the current codebase. ServerFileLogger.Close() now calls closeLogFile(file, nil, "server "+serverID) in its loop (line 115) instead of inlining the sync/close logic. The per-file sync-and-close is centralized through the shared helper, eliminating the duplication described in the issue. This issue can be closed.

    Warning

    Firewall blocked 1 domain

    The following domain was blocked by the firewall during workflow execution:

    • proxy.golang.org

    To allow these domains, add them to the network.allowed list in your workflow frontmatter:

    network:
      allowed:
        - defaults
        - "proxy.golang.org"

    See Network Configuration for more information.

    Generated by Repo Assist · 180.3 AIC · ⊞ 12.2K · ◷
    Comment /repo-assist to run again

    Add this agentic workflows to your repo

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@851905c06e905bf362a9f6cc54f912e3df747d55
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions