39 lines
1.8 KiB
Swift
39 lines
1.8 KiB
Swift
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)
|
|
}
|
|
}
|