-
-
Notifications
You must be signed in to change notification settings - Fork 162
Fix: Close log file in Builder.Close() to prevent resource leak #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Close log file in Builder.Close() to prevent resource leak #239
Conversation
Added proper cleanup of the log file opened in buildLogger() to prevent file descriptor leaks in long-running deployments. The Close() method now properly closes both the log file and RabbitMQ connections. Changes: - Added nil-safe log file closure in Builder.Close() method - Added comprehensive unit tests to verify log file is properly closed - Tests cover normal case, nil log file, and builder without log file
WalkthroughThe Builder's Close() method is enhanced to properly close log files before cleaning up RabbitMQ connections, with error handling included. Three unit tests verify the file closure behavior across scenarios with active log files, no log files, and nil log files. Changes
Sequence DiagramsequenceDiagram
participant Caller
participant Builder
participant LogFile
participant RabbitMQ
Caller->>Builder: Close()
alt logsFile is open
Builder->>LogFile: Close()
LogFile-->>Builder: (success or error)
end
alt RabbitMQ connections exist
Builder->>RabbitMQ: Close/cleanup
RabbitMQ-->>Builder: (done)
end
Builder-->>Caller: return (error if any)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
builder/builder.go (1)
80-98: Consider collecting all cleanup errors rather than returning on first failure.The current implementation returns immediately if closing the log file fails, which means the RabbitMQ connections won't be closed. For cleanup methods, it's generally better to attempt all cleanup operations and aggregate any errors encountered.
Here's a more robust approach:
func (b *Builder) Close() error { + var errs []error + // Close log file if it was opened if b.logsFile != nil { if err := b.logsFile.Close(); err != nil { - return err + errs = append(errs, fmt.Errorf("failed to close log file: %w", err)) } } // Close RabbitMQ connections if b.rabbitMQConnection != nil { if err := b.rabbitMQChannel.Close(); err != nil { - return err + errs = append(errs, fmt.Errorf("failed to close RabbitMQ channel: %w", err)) } if err := b.rabbitMQConnection.Close(); err != nil { - return err + errs = append(errs, fmt.Errorf("failed to close RabbitMQ connection: %w", err)) } } - return nil + + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil }Note:
errors.Join()requires Go 1.20+. If using an older version, you could combine the errors manually or use a library.builder/builder_test.go (2)
56-65: Consider removing redundant test.
TestBuilderClose_NilLogFileis functionally identical toTestBuilderClose_NoLogFilesinceNewBuilder()already initializeslogsFiletonil. Both tests exercise the same code path and nil-safety check.You could remove this test to reduce duplication, or if you want to keep it for explicit documentation purposes, consider adding a comment explaining why both tests exist.
1-65: Consider adding test coverage for concurrent resource cleanup.The current tests cover log file closure in isolation, but there's no test that verifies both the log file and RabbitMQ connections are closed together. Since
Close()handles both types of resources, it would be valuable to test that scenario.Consider adding a test like:
func TestBuilderClose_LogFileAndRabbitMQ(t *testing.T) { // Create a temporary directory for the test tmpDir := t.TempDir() logFilePath := tmpDir + "/test.log" // Create a builder instance builder := NewBuilder() // Build logger loggingConfig := parser.Logging{ LogsPath: logFilePath, } err := builder.buildLogger(loggingConfig) assert.NoError(t, err) // Mock or set up RabbitMQ connections (if feasible in test environment) // This would require either mocking or a test RabbitMQ instance // Close should clean up both resources err = builder.Close() assert.NoError(t, err) // Verify both resources are closed _, err = builder.logsFile.WriteString("test") assert.Error(t, err) }Note: This may require mocking RabbitMQ connections or setting up a test instance, which could be complex. At minimum, consider testing that Close() doesn't fail when both
logsFileandrabbitMQConnectionare set.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
builder/builder.go(1 hunks)builder/builder_test.go(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
builder/builder_test.go (2)
builder/builder.go (2)
NewBuilder(174-176)Builder(26-33)parser/configurations_parser.go (1)
Logging(26-31)
🔇 Additional comments (2)
builder/builder_test.go (2)
47-54: LGTM!This test correctly verifies that
Close()handles the case where no log file was opened. The nil safety check in the implementation prevents any panics.
40-44: No changes needed. The test correctly verifies closed file behavior.The verification confirms that writing to a closed file does produce an error containing "file already closed". Since the test uses
assert.Contains()for substring matching rather than an exact match, it will correctly pass even though the full error format includes the file path (e.g.,"write /tmp/test910371643: file already closed"). The test approach is sound and handles this behavior consistently.
|
Right now we close: I think this is the correct order, inverse of the opening. |
|
Tip For best results, initiate chat on the files or code changes. @AndreaBozzo, you're absolutely right! Closing resources in the reverse order of opening (LIFO - Last In, First Out) is indeed the correct approach. This ensures that dependencies are properly handled—the log file is closed first, then the RabbitMQ channel, and finally the underlying connection. This pattern prevents issues where a dependent resource might try to use an already-closed resource. Great work on implementing this properly, and thank you for the kind words! I'm glad the review was helpful. 🐰 |
|
Thank you @AndreaBozzo, I will review and merge in the next days :) |
mariocandela
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
Ty @mariocandela for the quick review! |
Summary
Fixes a resource leak where the log file opened in buildLogger() was never closed, causing file descriptor leaks in long-running deployments. This PR is optional and can wait mario's review.
Problem
The Builder.buildLogger() method opens a log file (b.logsFile) but the Close() method only closes RabbitMQ connections, leaving the file descriptor open for the lifetime of the application.
Solution
Added proper log file cleanup in Builder.Close() method with nil-safe check before closing to prevent panics.
Changes
Testing
All existing tests pass. New tests verify:
Coverage increased from 0% to 15.1% for builder package.
Impact
Summary by CodeRabbit
Bug Fixes
Tests