Skip to content

[Code Quality] RotatingStreamWriter._rotationDisabled is one-way: a single non-IO exception silently disables rotation forever, file grows unbounded #1067

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Warning

File: src/Servy.Core/IO/RotatingStreamWriter.cs

Lines: 40, 234, 429, 470-475, 482

Code snippet:

private bool _rotationDisabled;
...
private void CheckRotation()
{
    if (_rotationDisabled || _writer == null) return;
    ...
}

private void Rotate()
{
    if (_rotationDisabled || _timeProvider() < _rotationCooldownUntil) return;
    ...
    catch (Exception ex)
    {
        Logger.Error($"Log rotation critical failure: {ex.Message}. Rotation will be disabled until service restart.", ex);
        _rotationDisabled = true;
        break;
    }
    ...
    if (success)
    {
        _rotationCooldownUntil = DateTime.MinValue;
        _rotationDisabled = false;   // <-- only reset path
        EnforceMaxRotations();
    }
}

Explanation:
_rotationDisabled is a circuit breaker that opens on any non-IOException during File.Move (transient permission glitch, AV scanner holding a handle with a non-IO error, network share blip on a UNC log path). Once set to true, every subsequent CheckRotation and Rotate immediately returns. The only path that resets it (_rotationDisabled = false) is inside if (success) after a successful File.Move — but Rotate returns early on line 429 before File.Move is ever attempted. So the breaker is one-way until process restart.

The error log promises "until service restart", but the consequence is unbounded log file growth — for a service designed to manage long-running child processes producing logs, this defeats the purpose of having rotation at all.

Suggested fix:
Either add a time-based half-open state (e.g. attempt rotation again after N minutes/hours of being disabled), or move the reset out of the success branch and put it under a separate "trip count below threshold + cooldown elapsed" condition. At minimum, log a periodic reminder so the operator notices a service whose rotation has been silently dead for days.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions