diff --git a/DiagnosticsTests/Reporters/LogsReporterTests.swift b/DiagnosticsTests/Reporters/LogsReporterTests.swift
index 1fe7d74..37c8d01 100644
--- a/DiagnosticsTests/Reporters/LogsReporterTests.swift
+++ b/DiagnosticsTests/Reporters/LogsReporterTests.swift
@@ -62,5 +62,4 @@ final class LogsReporterTests: XCTestCase {
let secondIndex = try XCTUnwrap(diagnostics.range(of: "second")?.lowerBound)
XCTAssertTrue(firstIndex > secondIndex)
}
-
}
diff --git a/DiagnosticsTests/Reporters/LogsTrimmerTests.swift b/DiagnosticsTests/Reporters/LogsTrimmerTests.swift
new file mode 100644
index 0000000..d875091
--- /dev/null
+++ b/DiagnosticsTests/Reporters/LogsTrimmerTests.swift
@@ -0,0 +1,60 @@
+//
+// LogsTrimmerTests.swift
+//
+//
+// Created by Antoine van der Lee on 01/03/2024.
+//
+// swiftlint:disable line_length
+
+@testable import Diagnostics
+import XCTest
+
+final class LogsTrimmerTests: XCTestCase {
+
+ /// It should trim the oldest line and skip session headers.
+ func testTrimmingSessionsSingleLine() {
+ let expectedOutput = """
+ Date: 2024-02-20 10:33:47 System: iOS 16.3 Locale: en-GB Version: 6.2.8 (17000)
2024-02-20 10:33:47 |
+ """ + + var input = expectedOutput + input += """ +2024-02-20 10:33:47 |
+ """ + + var inputData = Data(input.utf8) + let trimmer = LogsTrimmer( + numberOfLinesToTrim: 1 + ) + + trimmer.trim(data: &inputData) + + let outputString = String(data: inputData, encoding: .utf8) + XCTAssertEqual(outputString, expectedOutput) + } + + /// It should trim the oldest lines and skip session headers. + func testTrimmingSessionsMultipleLines() { + let expectedOutput = """ +Date: 2024-02-20 10:33:47
System: iOS 16.3
Locale: en-GB
Version: 6.2.8 (17000)
2024-02-20 10:33:47 |
+2024-02-20 10:33:47 |
+ """ + + var inputData = Data(input.utf8) + let trimmer = LogsTrimmer( + numberOfLinesToTrim: 10 + ) + + trimmer.trim(data: &inputData) + + let outputString = String(data: inputData, encoding: .utf8) + XCTAssertEqual(outputString, expectedOutput) + } +} +// swiftlint:enable line_length diff --git a/Sources/Logging/DiagnosticsLogger.swift b/Sources/Logging/DiagnosticsLogger.swift index a20e61e..59a1b63 100644 --- a/Sources/Logging/DiagnosticsLogger.swift +++ b/Sources/Logging/DiagnosticsLogger.swift @@ -223,19 +223,14 @@ extension DiagnosticsLogger { guard var data = try? Data(contentsOf: self.logFileLocation, options: .mappedIfSafe), - !data.isEmpty, - let newline = "\n".data(using: .utf8) else { - return assertionFailure("Trimming the current log file failed") + !data.isEmpty else { + return assertionFailure("Trimming the current log file failed") } - var position: Int = 0 - while (logSize - Int64(position)) > (maximumSize - trimSize) { - guard let range = data.firstRange(of: newline, in: position ..< data.count) else { break } - position = range.startIndex.advanced(by: 1) - } + let trimmer = LogsTrimmer(numberOfLinesToTrim: 10) + trimmer.trim(data: &data) - logSize -= Int64(position) - data.removeSubrange(0 ..< position) + logSize = Int64(data.count) guard (try? data.write(to: logFileLocation, options: .atomic)) != nil else { return assertionFailure("Could not write trimmed log to target file location: \(logFileLocation)") diff --git a/Sources/Logging/LogsTrimmer.swift b/Sources/Logging/LogsTrimmer.swift new file mode 100644 index 0000000..c28afee --- /dev/null +++ b/Sources/Logging/LogsTrimmer.swift @@ -0,0 +1,49 @@ +// +// LogsTrimmer.swift +// +// +// Created by Antoine van der Lee on 01/03/2024. +// + +import Foundation + +struct LogsTrimmer { + let numberOfLinesToTrim: Int + + func trim(data: inout Data) { + guard let logs = String(data: data, encoding: .utf8) else { + return + } + + // Define the regular expression pattern + let pattern = "(.*?)
" + + // Create a regular expression object + guard let regex = try? NSRegularExpression(pattern: pattern) else { + return + } + + // Find all matches in the input string + let matches = regex + .matches( + in: logs, + range: NSRange(location: 0, length: logs.utf16.count) + ) + .suffix(numberOfLinesToTrim) + + guard let firstMatch = matches.first, let lastMatch = matches.last else { + return + } + + let range = NSRange( + location: firstMatch.range.location, + length: lastMatch.range.upperBound - firstMatch.range.location + ) + guard let range = Range(range, in: logs) else { + return + } + + let trimmedLogs = logs.replacingCharacters(in: range, with: "") + data = Data(trimmedLogs.utf8) + } +}