rotatingFileOutput function
Returns an OutputFunction like fileOutput, but rotates the file once
it reaches maxSizeBytes: before a write that would exceed the limit,
the current file is renamed to <filePath>.0, any existing .0
becomes .1, and so on up to .{maxBackups - 1} — the oldest backup
beyond that is deleted. A fresh file at filePath is then created for
the new entry. Writing (and rotation) is synchronous; for a non-blocking
version see AsyncRotatingFileOutput.
The size is read from the file once, when the output is created, and counted in memory from then on (in bytes, as written): the file system is not asked on every write. A file shortened or removed by someone else in the meantime is not noticed until the next rotation — which then comes early or late by that much, and loses nothing.
StructlogConfiguration.configure(
output: rotatingFileOutput(
'logs/app.log',
maxSizeBytes: 1024 * 1024, // 1 MB per file
maxBackups: 5, // keep app.log.0 .. app.log.4
),
);
final log = getLogger();
for (var i = 0; i < 100000; i++) {
log.info('iteration', context: {'i': i});
}
// logs/app.log holds the newest entries; logs/app.log.0, .1, ...
// hold progressively older ones, oldest discarded past maxBackups.
Implementation
OutputFunction rotatingFileOutput(
String filePath, {
int maxSizeBytes = 10 * 1024 * 1024, // 10MB default
int maxBackups = 5,
}) {
final file = File(filePath);
final dir = file.parent;
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
void rotate() {
// Delete oldest backup
final oldest = File('$filePath.${maxBackups - 1}');
if (oldest.existsSync()) {
oldest.deleteSync();
}
// Shift backups
for (var i = maxBackups - 2; i >= 0; i--) {
final src = File('$filePath.$i');
final dst = File('$filePath.${i + 1}');
if (src.existsSync()) {
src.renameSync(dst.path);
}
}
// Move current to .0
if (file.existsSync()) {
file.renameSync('$filePath.0');
}
}
var size = file.existsSync() ? file.lengthSync() : 0;
return (Map<String, dynamic> entry, LogLevel level) {
if (size >= maxSizeBytes) {
rotate();
size = 0;
}
final bytes = utf8.encode('${encodeLogEntry(entry)}\n');
file.writeAsBytesSync(bytes, mode: FileMode.append);
size += bytes.length;
};
}