import Foundation /// One current file and one bounded archive. The owning process serializes writes. struct BoundedLog { let url: URL var maxBytes = 5 * 1024 * 1024 func append(_ line: String) throws { let files = FileManager.default try files.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true) if !files.fileExists(atPath: url.path) { guard files.createFile(atPath: url.path, contents: nil, attributes: [.posixPermissions: 0o600]) else { throw CocoaError(.fileWriteUnknown) } } try files.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path) let handle = try FileHandle(forUpdating: url) defer { try? handle.close() } let size = try handle.seekToEnd() let data = Data(line.utf8).suffix(maxBytes) let archive = url.appendingPathExtension("1") var archiveSize: UInt64 = 0 if files.fileExists(atPath: archive.path) { try files.setAttributes([.posixPermissions: 0o600], ofItemAtPath: archive.path) archiveSize = (try files.attributesOfItem(atPath: archive.path)[.size] as? NSNumber)?.uint64Value ?? 0 } if size + UInt64(data.count) > UInt64(maxBytes) || archiveSize > UInt64(maxBytes) { try handle.seek(toOffset: size > UInt64(maxBytes) ? size - UInt64(maxBytes) : 0) let tail = try handle.read(upToCount: maxBytes) ?? Data() guard files.createFile(atPath: archive.path, contents: tail, attributes: [.posixPermissions: 0o600]) else { throw CocoaError(.fileWriteUnknown) } try handle.truncate(atOffset: 0) try handle.seek(toOffset: 0) } try handle.write(contentsOf: data) } }