-
Notifications
You must be signed in to change notification settings - Fork 392
/
Copy pathLogRotation.swift
98 lines (82 loc) · 3.07 KB
/
LogRotation.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//
// LogRotation.swift
// MullvadVPN
//
// Created by pronebird on 02/08/2020.
// Copyright © 2020 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadTypes
public enum LogRotation {
private struct LogData {
var path: URL
var size: UInt64
var creationDate: Date
}
public struct Options {
let storageSizeLimit: Int
let oldestAllowedDate: Date
/// Options for log rotation, defining how logs should be retained.
///
/// - Parameter storageSizeLimit: Storage size limit in bytes.
/// - Parameter oldestAllowedDate: Oldest allowed date.
public init(storageSizeLimit: Int, oldestAllowedDate: Date) {
self.storageSizeLimit = storageSizeLimit
self.oldestAllowedDate = oldestAllowedDate
}
}
public enum Error: LocalizedError, WrappingError {
case rotateLogFiles(Swift.Error)
public var errorDescription: String? {
switch self {
case .rotateLogFiles:
return "Failure to rotate logs"
}
}
public var underlyingError: Swift.Error? {
switch self {
case let .rotateLogFiles(error):
return error
}
}
}
public static func rotateLogs(logDirectory: URL, options: Options) throws {
let fileManager = FileManager.default
do {
// Filter out all log files in directory.
let logPaths: [URL] = (try fileManager.contentsOfDirectory(
atPath: logDirectory.relativePath
)).compactMap { file in
if file.split(separator: ".").last == "log" {
logDirectory.appendingPathComponent(file)
} else {
nil
}
}
// Convert logs into objects with necessary meta data.
let logs = try logPaths.map { logPath in
let attributes = try fileManager.attributesOfItem(atPath: logPath.relativePath)
let size = (attributes[.size] as? UInt64) ?? 0
let creationDate = (attributes[.creationDate] as? Date) ?? Date.distantPast
return LogData(path: logPath, size: size, creationDate: creationDate)
}.sorted { log1, log2 in
log1.creationDate > log2.creationDate
}
try deleteLogs(dateThreshold: options.oldestAllowedDate, sizeThreshold: options.storageSizeLimit, in: logs)
} catch {
throw Error.rotateLogFiles(error)
}
}
private static func deleteLogs(dateThreshold: Date, sizeThreshold: Int, in logs: [LogData]) throws {
let fileManager = FileManager.default
var fileSizes = UInt64.zero
for log in logs {
fileSizes += log.size
let logIsTooOld = log.creationDate < dateThreshold
let logCapacityIsExceeded = fileSizes > sizeThreshold
if logIsTooOld || logCapacityIsExceeded {
try fileManager.removeItem(at: log.path)
}
}
}
}