Cross-language rolling-file library + tooling. The .NET port is the
reference implementation; JavaScript, Python, Vue components, and
a static demo site share the same on-disk format and policy
contract under spec/.
Writes append-only data to files that roll on time, size, or message count; retains rolled files by count, total size, and age; compresses rolled files with deferred semantics; survives crashes via startup scan + atomic temp-and-rename; coordinates multi-process writers through an OS advisory lock.
What it explicitly does not do: render log events, format text, batch events semantically, talk to any logger. The consumer hands in bytes; the library puts them on disk. That's the whole contract.
.NET has plenty of file-rolling code, but every option is coupled to a specific logger (Serilog, NLog, log4net, MEL) or violates the AOT contract any AOT-targeting consumer needs. A framework-neutral, AOT-clean, zero-transitive-deps rolling-file primitive doesn't exist today.
Pre-release. Phase 1 ships across the .NET reference implementation: time / size / count rolls, retention by count / total size / age, deferred gzip + brotli compression, atomic temp-and-rename, startup-scanner crash recovery, path-token expansion, AOT-clean output. The 132-test xunit suite covers the phase across net8.0 and net10.0. The public API surface is still settling; expect minor signature changes before 1.0.
using MMP.RollingFiles;
var policy = new FilesManagerPolicy(
Directory: "/var/log/myapp",
FileNameTemplate: "events",
Extension: ".log");
using var fm = new FilesManager(policy);
fm.AppendLine("hello world");Full language-by-language walkthrough — C# / Python / JS / Vue —
in docs/HOWTO.md.
- One public class (
FilesManager) plus its policy record, hooks class, and three enums. Internal helpers stay internal. - Zero transitive NuGet deps. BCL only. No
Microsoft.Extensions.*, no logging abstraction, no DI container. - AOT-clean by contract.
<IsAotCompatible>true</IsAotCompatible>- CI gate that runs
dotnet publish -p:PublishAot=trueagainst the sample on every push.
- CI gate that runs
- Renderer / policy split. The library never ships UI. The
FilesManagerPolicyrecord is the canonical shape any renderer (Vue, Blazor, CLI) binds to. UI components for the dashboard are documented in the design doc but live outside the library.
This is a monorepo with five workspaces plus a shared spec directory. Each workspace publishes (or deploys) to its own ecosystem; the shared spec keeps the implementations honest with each other.
.
├── spec/ cross-language v1 contract + conformance fixtures
│ ├── v1.md on-disk format + policy data model
│ └── fixtures/ sample policies + expected artefacts
├── packages/
│ ├── dotnet/ .NET reference implementation (NuGet)
│ │ ├── MMP.RollingFiles/ library
│ │ ├── MMP.RollingFiles.Tests/ xunit suite
│ │ └── samples/BasicRolling/ AOT-publishable sample, gates CI
│ ├── python/ Python port (PyPI; phase-1 alpha)
│ ├── javascript/ framework-neutral JS port (npm; @mmpworks/rollingfiles)
│ ├── vue/ Vue 3 + Vuetify 3 components (npm; @mmpworks/rollingfiles-vue)
│ └── web/ static demo / playground site (GitHub Pages)
├── tests/conformance/ cross-language conformance runners (stub)
└── .github/workflows/ per-language CI + release + GitHub Pages deploy
Each language uses its own ecosystem; npm workspaces wire the JS, Vue, and Web packages together at the root.
| Package | Built with | Published to | Trigger |
|---|---|---|---|
packages/dotnet/MMP.RollingFiles |
dotnet build |
NuGet | tag dotnet-vX.Y.Z |
packages/python |
pip install -e .[dev] |
PyPI (OIDC) | tag python-vX.Y.Z |
packages/javascript (@mmpworks/rollingfiles) |
npm + workspaces | npm (OIDC) | tag js-vX.Y.Z |
packages/vue (@mmpworks/rollingfiles-vue) |
npm + workspaces | npm (OIDC) | tag vue-vX.Y.Z |
packages/web (@mmpworks/rollingfiles-web) |
npm + Vite | GitHub Pages | every push to main |
The conformance harness is scaffolded but not wired yet —
spec/fixtures/ holds the cross-language contract once it lands,
and .github/workflows/conformance.yml runs the per-language
runners when they exist under tests/conformance/runner-*. A
contract change will require updating the fixtures and every
implementation in one PR.
# .NET (from packages/dotnet/)
dotnet build MMP.RollingFiles.sln
dotnet test MMP.RollingFiles.sln
# JS / Vue / Web (from repo root, workspaces wired)
npm install
npm run dev # serves the playground at http://localhost:5174
# Python (from packages/python/)
pip install -e .[dev]
pytest