mirror of
https://github.com/wnagrodzki/SwiftLogger.git
synced 2025-04-06 12:31:51 +02:00
Merge branch 'feature/code_reorganization' into develop
This commit is contained in:
commit
3bc845ee28
9 changed files with 155 additions and 142 deletions
|
@ -15,14 +15,15 @@
|
|||
2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B482122AF53008E4117 /* ConsoleLogger.swift */; };
|
||||
2EBF4B4C2122AF53008E4117 /* NullLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B492122AF53008E4117 /* NullLogger.swift */; };
|
||||
2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B542122B598008E4117 /* DiskLogger.swift */; };
|
||||
2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* FileWriter.swift */; };
|
||||
2EBF4B592122B598008E4117 /* FileRotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* FileRotate.swift */; };
|
||||
2EBF4B582122B598008E4117 /* SizeLimitedFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* SizeLimitedFile.swift */; };
|
||||
2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; };
|
||||
2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; };
|
||||
2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; };
|
||||
2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; };
|
||||
2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E02135C61100EB3683 /* FileRotateTests.swift */; };
|
||||
2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */; };
|
||||
2ED077DB2132B0320058EEFC /* OSFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* OSFileManager.swift */; };
|
||||
2ED103E12135C61100EB3683 /* LogrotateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E02135C61100EB3683 /* LogrotateTests.swift */; };
|
||||
2ED103E32135D3FB00EB3683 /* SizeLimitedFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E22135D3FB00EB3683 /* SizeLimitedFileTests.swift */; };
|
||||
2ED103E52138553B00EB3683 /* DiskLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */; };
|
||||
2ED83781236A19A60008C01F /* OSFileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED83780236A19A60008C01F /* OSFileHandle.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -58,14 +59,15 @@
|
|||
2EBF4B482122AF53008E4117 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = "<group>"; };
|
||||
2EBF4B492122AF53008E4117 /* NullLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NullLogger.swift; sourceTree = "<group>"; };
|
||||
2EBF4B542122B598008E4117 /* DiskLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskLogger.swift; sourceTree = "<group>"; };
|
||||
2EBF4B552122B598008E4117 /* FileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWriter.swift; sourceTree = "<group>"; };
|
||||
2EBF4B562122B598008E4117 /* FileRotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileRotate.swift; sourceTree = "<group>"; };
|
||||
2EBF4B552122B598008E4117 /* SizeLimitedFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SizeLimitedFile.swift; sourceTree = "<group>"; };
|
||||
2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; };
|
||||
2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; };
|
||||
2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; };
|
||||
2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; };
|
||||
2ED103E02135C61100EB3683 /* FileRotateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRotateTests.swift; sourceTree = "<group>"; };
|
||||
2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWriterTests.swift; sourceTree = "<group>"; };
|
||||
2ED077DA2132B0320058EEFC /* OSFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSFileManager.swift; sourceTree = "<group>"; };
|
||||
2ED103E02135C61100EB3683 /* LogrotateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogrotateTests.swift; sourceTree = "<group>"; };
|
||||
2ED103E22135D3FB00EB3683 /* SizeLimitedFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeLimitedFileTests.swift; sourceTree = "<group>"; };
|
||||
2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskLoggerTests.swift; sourceTree = "<group>"; };
|
||||
2ED83780236A19A60008C01F /* OSFileHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSFileHandle.swift; sourceTree = "<group>"; };
|
||||
2EDA8AE8213ACCFF00FE5840 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -95,8 +97,8 @@
|
|||
2ED077D621329CA30058EEFC /* LoggetTests.swift */,
|
||||
2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */,
|
||||
2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */,
|
||||
2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */,
|
||||
2ED103E02135C61100EB3683 /* FileRotateTests.swift */,
|
||||
2ED103E22135D3FB00EB3683 /* SizeLimitedFileTests.swift */,
|
||||
2ED103E02135C61100EB3683 /* LogrotateTests.swift */,
|
||||
2E58D35E21316C3500BEF81A /* Info.plist */,
|
||||
);
|
||||
path = UnitTests;
|
||||
|
@ -146,9 +148,10 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
2EBF4B542122B598008E4117 /* DiskLogger.swift */,
|
||||
2EBF4B552122B598008E4117 /* FileWriter.swift */,
|
||||
2EBF4B562122B598008E4117 /* FileRotate.swift */,
|
||||
2ED077DA2132B0320058EEFC /* FileSystem.swift */,
|
||||
2EBF4B552122B598008E4117 /* SizeLimitedFile.swift */,
|
||||
2EBF4B562122B598008E4117 /* Logrotate.swift */,
|
||||
2ED077DA2132B0320058EEFC /* OSFileManager.swift */,
|
||||
2ED83780236A19A60008C01F /* OSFileHandle.swift */,
|
||||
);
|
||||
path = DiskLogger;
|
||||
sourceTree = "<group>";
|
||||
|
@ -246,10 +249,10 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */,
|
||||
2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */,
|
||||
2ED103E32135D3FB00EB3683 /* SizeLimitedFileTests.swift in Sources */,
|
||||
2ED103E52138553B00EB3683 /* DiskLoggerTests.swift in Sources */,
|
||||
2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */,
|
||||
2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */,
|
||||
2ED103E12135C61100EB3683 /* LogrotateTests.swift in Sources */,
|
||||
2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -258,14 +261,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */,
|
||||
2EBF4B582122B598008E4117 /* SizeLimitedFile.swift in Sources */,
|
||||
2ED83781236A19A60008C01F /* OSFileHandle.swift in Sources */,
|
||||
2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */,
|
||||
2EBF4B4C2122AF53008E4117 /* NullLogger.swift in Sources */,
|
||||
2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */,
|
||||
2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */,
|
||||
2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */,
|
||||
2ED077DB2132B0320058EEFC /* OSFileManager.swift in Sources */,
|
||||
2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */,
|
||||
2EBF4B592122B598008E4117 /* FileRotate.swift in Sources */,
|
||||
2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */,
|
||||
2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -24,23 +24,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// Write failed as allowed size limit would be exceeded.
|
||||
public struct SizeLimitedFileQuotaReached: Error {}
|
||||
|
||||
/// Allows writing to a file while respecting allowed size limit.
|
||||
protocol SizeLimitedFile {
|
||||
|
||||
/// Synchronously writes `data` at the end of the file.
|
||||
///
|
||||
/// - Parameter data: The data to be written.
|
||||
/// - Throws: Throws an error if no free space is left on the file system, or if any other writing error occurs.
|
||||
/// Throws `SizeLimitedFileQuotaReached` if allowed size limit would be exceeded.
|
||||
func write(_ data: Data) throws
|
||||
|
||||
/// Writes all in-memory data to permanent storage and closes the file.
|
||||
func synchronizeAndCloseFile()
|
||||
}
|
||||
|
||||
protocol SizeLimitedFileFactory {
|
||||
|
||||
/// Returns newly initialized SizeLimitedFile instance.
|
||||
|
@ -52,20 +35,6 @@ protocol SizeLimitedFileFactory {
|
|||
func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile
|
||||
}
|
||||
|
||||
/// Allows log files rotation.
|
||||
protocol Logrotate {
|
||||
|
||||
/// Rotates log files `rotations` number of times.
|
||||
///
|
||||
/// First deletes file at `<fileURL>.<rotations>`.
|
||||
/// Next moves files located at:
|
||||
///
|
||||
/// `<fileURL>, <fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations - 1>`
|
||||
///
|
||||
/// to `<fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations>`
|
||||
func rotate() throws
|
||||
}
|
||||
|
||||
protocol LogrotateFactory {
|
||||
|
||||
/// Returns newly initialized Logrotate instance.
|
||||
|
@ -82,7 +51,7 @@ public final class DiskLogger: Logger {
|
|||
private let fileURL: URL
|
||||
private let fileSizeLimit: UInt64
|
||||
private let rotations: Int
|
||||
private let fileSystem: FileSystem
|
||||
private let fileManager: OSFileManager
|
||||
private let sizeLimitedFileFactory: SizeLimitedFileFactory
|
||||
private let logrotateFactory: LogrotateFactory
|
||||
private let formatter: DateFormatter
|
||||
|
@ -97,14 +66,14 @@ public final class DiskLogger: Logger {
|
|||
/// - fileSizeLimit: Maximum size log file can reach in bytes. Attempt to exceeding that limit triggers log files rotation.
|
||||
/// - rotations: Number of times log files are rotated before being removed.
|
||||
public convenience init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int) {
|
||||
self.init(fileURL: fileURL, fileSizeLimit: fileSizeLimit, rotations: rotations, fileSystem: FileManager.default, sizeLimitedFileFactory: FileWriterFactory(), logrotateFactory: FileRotateFactory())
|
||||
self.init(fileURL: fileURL, fileSizeLimit: fileSizeLimit, rotations: rotations, fileManager: FileManager.default, sizeLimitedFileFactory: SizeLimitedFileFactoryImpl(), logrotateFactory: LogrotateFactoryImpl())
|
||||
}
|
||||
|
||||
init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem, sizeLimitedFileFactory: SizeLimitedFileFactory, logrotateFactory: LogrotateFactory) {
|
||||
init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileManager: OSFileManager, sizeLimitedFileFactory: SizeLimitedFileFactory, logrotateFactory: LogrotateFactory) {
|
||||
self.fileURL = fileURL
|
||||
self.fileSizeLimit = fileSizeLimit
|
||||
self.rotations = rotations
|
||||
self.fileSystem = fileSystem
|
||||
self.fileManager = fileManager
|
||||
self.sizeLimitedFileFactory = sizeLimitedFileFactory
|
||||
self.logrotateFactory = logrotateFactory
|
||||
formatter = DateFormatter()
|
||||
|
@ -144,8 +113,8 @@ public final class DiskLogger: Logger {
|
|||
|
||||
private func openSizeLimitedFile() throws {
|
||||
guard sizeLimitedFile == nil else { return }
|
||||
if fileSystem.itemExists(at: fileURL) == false {
|
||||
_ = fileSystem.createFile(at: fileURL)
|
||||
if fileManager.itemExists(at: fileURL) == false {
|
||||
_ = fileManager.createFile(at: fileURL)
|
||||
}
|
||||
sizeLimitedFile = try sizeLimitedFileFactory.makeInstance(fileURL: fileURL, fileSizeLimit: fileSizeLimit)
|
||||
}
|
||||
|
@ -166,27 +135,20 @@ public final class DiskLogger: Logger {
|
|||
}
|
||||
}
|
||||
|
||||
private class FileRotateFactory: LogrotateFactory {
|
||||
private class LogrotateFactoryImpl: LogrotateFactory {
|
||||
func makeInstance(fileURL: URL, rotations: Int) -> Logrotate {
|
||||
return FileRotate(fileURL: fileURL, rotations: rotations, fileSystem: FileManager.default)
|
||||
return LogrotateImpl(fileURL: fileURL, rotations: rotations, fileManager: FileManager.default)
|
||||
}
|
||||
}
|
||||
|
||||
private class FileWriterFactory: SizeLimitedFileFactory {
|
||||
private class SizeLimitedFileFactoryImpl: SizeLimitedFileFactory {
|
||||
func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile {
|
||||
return try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit, fileFactory: FileHandleFactory())
|
||||
return try SizeLimitedFileImpl(fileURL: fileURL, fileSizeLimit: fileSizeLimit, fileFactory: FileHandleFactoryImpl())
|
||||
}
|
||||
}
|
||||
|
||||
private class FileHandleFactory: FileFactory {
|
||||
func makeInstance(forWritingTo: URL) throws -> File {
|
||||
private class FileHandleFactoryImpl: FileHandleFactory {
|
||||
func makeInstance(forWritingTo: URL) throws -> OSFileHandle {
|
||||
return try FileHandle(forWritingTo: forWritingTo)
|
||||
}
|
||||
}
|
||||
|
||||
extension FileHandle: File {
|
||||
|
||||
func swift_write(_ data: Data) throws {
|
||||
try __write(data, error: ())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,26 +24,40 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
final class FileRotate {
|
||||
/// Allows log files rotation.
|
||||
protocol Logrotate {
|
||||
|
||||
/// Rotates log files `rotations` number of times.
|
||||
///
|
||||
/// First deletes file at `<fileURL>.<rotations>`.
|
||||
/// Next moves files located at:
|
||||
///
|
||||
/// `<fileURL>, <fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations - 1>`
|
||||
///
|
||||
/// to `<fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations>`
|
||||
func rotate() throws
|
||||
}
|
||||
|
||||
final class LogrotateImpl {
|
||||
|
||||
private let fileURL: URL
|
||||
private let rotations: Int
|
||||
private let fileSystem: FileSystem
|
||||
private let fileManager: OSFileManager
|
||||
|
||||
/// Initializes new Logrotate instance.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fileURL: URL of the log file.
|
||||
/// - rotations: Number of times log files are rotated before being removed.
|
||||
init(fileURL: URL, rotations: Int, fileSystem: FileSystem) {
|
||||
init(fileURL: URL, rotations: Int, fileManager: OSFileManager) {
|
||||
precondition(rotations > 0)
|
||||
self.fileURL = fileURL
|
||||
self.rotations = rotations
|
||||
self.fileSystem = fileSystem
|
||||
self.fileManager = fileManager
|
||||
}
|
||||
}
|
||||
|
||||
extension FileRotate: Logrotate {
|
||||
extension LogrotateImpl: Logrotate {
|
||||
|
||||
func rotate() throws {
|
||||
let range = 1...rotations
|
||||
|
@ -54,12 +68,12 @@ extension FileRotate: Logrotate {
|
|||
let toDelete = rotatedURLs.last!
|
||||
let toMove = zip(allURLs, rotatedURLs).reversed()
|
||||
|
||||
if fileSystem.itemExists(at: toDelete) {
|
||||
try fileSystem.removeItem(at: toDelete)
|
||||
if fileManager.itemExists(at: toDelete) {
|
||||
try fileManager.removeItem(at: toDelete)
|
||||
}
|
||||
for (oldURL, newURL) in toMove {
|
||||
guard fileSystem.itemExists(at: oldURL) else { continue }
|
||||
try fileSystem.moveItem(at: oldURL, to: newURL)
|
||||
guard fileManager.itemExists(at: oldURL) else { continue }
|
||||
try fileManager.moveItem(at: oldURL, to: newURL)
|
||||
}
|
||||
}
|
||||
}
|
23
Logger/Loggers/DiskLogger/OSFileHandle.swift
Normal file
23
Logger/Loggers/DiskLogger/OSFileHandle.swift
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// OSFileHandle.swift
|
||||
// Logger
|
||||
//
|
||||
// Created by Wojciech Nagrodzki on 30/10/2019.
|
||||
// Copyright © 2019 Wojciech Nagrodzki. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol OSFileHandle {
|
||||
func seekToEndOfFile() -> UInt64
|
||||
func swift_write(_ data: Data) throws
|
||||
func synchronizeFile()
|
||||
func closeFile()
|
||||
}
|
||||
|
||||
extension FileHandle: OSFileHandle {
|
||||
|
||||
func swift_write(_ data: Data) throws {
|
||||
try __write(data, error: ())
|
||||
}
|
||||
}
|
|
@ -24,14 +24,14 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public protocol FileSystem {
|
||||
public protocol OSFileManager {
|
||||
func itemExists(at URL: URL) -> Bool
|
||||
func removeItem(at URL: URL) throws
|
||||
func moveItem(at srcURL: URL, to dstURL: URL) throws
|
||||
func createFile(at URL: URL) -> Bool
|
||||
}
|
||||
|
||||
extension FileManager: FileSystem {
|
||||
extension FileManager: OSFileManager {
|
||||
|
||||
public func itemExists(at URL: URL) -> Bool {
|
||||
return fileExists(atPath: URL.path)
|
|
@ -24,38 +24,48 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
protocol File {
|
||||
func seekToEndOfFile() -> UInt64
|
||||
func swift_write(_ data: Data) throws
|
||||
func synchronizeFile()
|
||||
func closeFile()
|
||||
protocol FileHandleFactory {
|
||||
func makeInstance(forWritingTo: URL) throws -> OSFileHandle
|
||||
}
|
||||
|
||||
protocol FileFactory {
|
||||
func makeInstance(forWritingTo: URL) throws -> File
|
||||
/// Write failed as allowed size limit would be exceeded.
|
||||
public struct SizeLimitedFileQuotaReached: Error {}
|
||||
|
||||
/// Allows writing to a file while respecting allowed size limit.
|
||||
protocol SizeLimitedFile {
|
||||
|
||||
/// Synchronously writes `data` at the end of the file.
|
||||
///
|
||||
/// - Parameter data: The data to be written.
|
||||
/// - Throws: Throws an error if no free space is left on the file system, or if any other writing error occurs.
|
||||
/// Throws `SizeLimitedFileQuotaReached` if allowed size limit would be exceeded.
|
||||
func write(_ data: Data) throws
|
||||
|
||||
/// Writes all in-memory data to permanent storage and closes the file.
|
||||
func synchronizeAndCloseFile()
|
||||
}
|
||||
|
||||
/// Allows writing to a file while respecting allowed size limit.
|
||||
final class FileWriter {
|
||||
final class SizeLimitedFileImpl {
|
||||
|
||||
private let file: File
|
||||
private let file: OSFileHandle
|
||||
private let sizeLimit: UInt64
|
||||
private var currentSize: UInt64
|
||||
|
||||
/// Initializes new FileWriter instance.
|
||||
/// Initializes new SizeLimitedFileImpl instance.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fileURL: URL of the file.
|
||||
/// - fileSizeLimit: Maximum size the file can reach in bytes.
|
||||
/// - Throws: An error that may occur while the file is being opened for writing.
|
||||
init(fileURL: URL, fileSizeLimit: UInt64, fileFactory: FileFactory) throws {
|
||||
init(fileURL: URL, fileSizeLimit: UInt64, fileFactory: FileHandleFactory) throws {
|
||||
file = try fileFactory.makeInstance(forWritingTo: fileURL)
|
||||
self.sizeLimit = fileSizeLimit
|
||||
currentSize = file.seekToEndOfFile()
|
||||
}
|
||||
}
|
||||
|
||||
extension FileWriter: SizeLimitedFile {
|
||||
extension SizeLimitedFileImpl: SizeLimitedFile {
|
||||
|
||||
func write(_ data: Data) throws {
|
||||
let dataSize = UInt64(data.count)
|
|
@ -33,13 +33,13 @@ class DiskLoggerTests: XCTestCase {
|
|||
let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile")
|
||||
expectation.expectedFulfillmentCount = 1
|
||||
|
||||
let filesystem = FileSystemStub()
|
||||
let fimeManager = FileManagerStub()
|
||||
let sizeLimitedFileFactory = SizeLimitedFileMockFactory(writeCall: expectation)
|
||||
let logrotateFactory = LogrotateMockFactory()
|
||||
let logger = DiskLogger(fileURL: logURL,
|
||||
fileSizeLimit: 1024,
|
||||
rotations: 1,
|
||||
fileSystem: filesystem,
|
||||
fileManager: fimeManager,
|
||||
sizeLimitedFileFactory: sizeLimitedFileFactory,
|
||||
logrotateFactory: logrotateFactory)
|
||||
|
||||
|
@ -61,13 +61,13 @@ class DiskLoggerTests: XCTestCase {
|
|||
let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile")
|
||||
expectation.expectedFulfillmentCount = 2
|
||||
|
||||
let filesystem = FileSystemStub()
|
||||
let fimeManager = FileManagerStub()
|
||||
let sizeLimitedFileFactory = SizeLimitedFileMockFactory(writeCall: expectation)
|
||||
let logrotateFactory = LogrotateMockFactory()
|
||||
let logger = DiskLogger(fileURL: logURL,
|
||||
fileSizeLimit: 91,
|
||||
rotations: 1,
|
||||
fileSystem: filesystem,
|
||||
fileManager: fimeManager,
|
||||
sizeLimitedFileFactory: sizeLimitedFileFactory,
|
||||
logrotateFactory: logrotateFactory)
|
||||
|
||||
|
@ -90,13 +90,13 @@ class DiskLoggerTests: XCTestCase {
|
|||
let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile")
|
||||
expectation.expectedFulfillmentCount = 2
|
||||
|
||||
let filesystem = FileSystemStub()
|
||||
let fimeManager = FileManagerStub()
|
||||
let sizeLimitedFileFactory = UnwritableFileStubFactory(writeCall: expectation)
|
||||
let logrotateFactory = LogrotateMockFactory()
|
||||
let logger = DiskLogger(fileURL: logURL,
|
||||
fileSizeLimit: 91,
|
||||
rotations: 1,
|
||||
fileSystem: filesystem,
|
||||
fileManager: fimeManager,
|
||||
sizeLimitedFileFactory: sizeLimitedFileFactory,
|
||||
logrotateFactory: logrotateFactory)
|
||||
|
||||
|
@ -116,7 +116,7 @@ class DiskLoggerTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private class FileSystemStub: FileSystem {
|
||||
private class FileManagerStub: OSFileManager {
|
||||
func itemExists(at URL: URL) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
import XCTest
|
||||
@testable import Logger
|
||||
|
||||
class FileRotateTests: XCTestCase {
|
||||
class LogrotateTests: XCTestCase {
|
||||
|
||||
let logURL = URL(fileURLWithPath: "/var/log/application.log")
|
||||
let log1URL = URL(fileURLWithPath: "/var/log/application.log.1")
|
||||
|
@ -33,98 +33,98 @@ class FileRotateTests: XCTestCase {
|
|||
let log3URL = URL(fileURLWithPath: "/var/log/application.log.3")
|
||||
|
||||
func test_1rotation_0files() {
|
||||
let fileSystem = FileSystemMock(files: [])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 1, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>()
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_1rotation_1file() {
|
||||
let fileSystem = FileSystemMock(files: [logURL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 1, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_1rotation_2files() {
|
||||
let fileSystem = FileSystemMock(files: [logURL, log1URL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL, log1URL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 1, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_1rotation_3files() {
|
||||
let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL, log1URL, log2URL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 1, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL, log2URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_2rotations_0files() {
|
||||
let fileSystem = FileSystemMock(files: [])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 2, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>()
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_2rotations_1file() {
|
||||
let fileSystem = FileSystemMock(files: [logURL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 2, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_2rotations_2files() {
|
||||
let fileSystem = FileSystemMock(files: [logURL, log1URL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL, log1URL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 2, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL, log2URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_2rotations_3files() {
|
||||
let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL, log1URL, log2URL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 2, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL, log2URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func test_2rotations_4files() {
|
||||
let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL, log3URL])
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem)
|
||||
let fimeManager = FileManagerMock(files: [logURL, log1URL, log2URL, log3URL])
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 2, fileManager: fimeManager)
|
||||
try? logrotate.rotate()
|
||||
|
||||
let actual = fileSystem.files
|
||||
let actual = fimeManager.files
|
||||
let expected = Set<URL>([log1URL, log2URL, log3URL])
|
||||
XCTAssertEqual(actual, expected)
|
||||
}
|
||||
|
||||
func testErrorPropagation() {
|
||||
let fileSystem = BrokenFileSystem()
|
||||
let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem)
|
||||
let fimeManager = BrokenFileSystem()
|
||||
let logrotate = LogrotateImpl(fileURL: logURL, rotations: 1, fileManager: fimeManager)
|
||||
|
||||
XCTAssertThrowsError(try logrotate.rotate(), "An error when removing or moving an item") { (error) in
|
||||
XCTAssertTrue(error is BrokenFileSystem.IOError)
|
||||
|
@ -132,7 +132,7 @@ class FileRotateTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private class FileSystemMock: FileSystem {
|
||||
private class FileManagerMock: OSFileManager {
|
||||
|
||||
private(set) var files = Set<URL>()
|
||||
|
||||
|
@ -159,7 +159,7 @@ private class FileSystemMock: FileSystem {
|
|||
}
|
||||
}
|
||||
|
||||
private class BrokenFileSystem: FileSystem {
|
||||
private class BrokenFileSystem: OSFileManager {
|
||||
|
||||
struct IOError: Error { }
|
||||
|
|
@ -25,20 +25,20 @@
|
|||
import XCTest
|
||||
@testable import Logger
|
||||
|
||||
class FileWriterTests: XCTestCase {
|
||||
class SizeLimitedFileTests: XCTestCase {
|
||||
|
||||
let logURL = URL(fileURLWithPath: "/var/log/application.log")
|
||||
|
||||
func testFileOpeningFailure() {
|
||||
let factory = UnopenableFileFactory()
|
||||
XCTAssertThrowsError(try FileWriter(fileURL: logURL, fileSizeLimit: 0, fileFactory: factory), "file open failure") { (error) in
|
||||
XCTAssertThrowsError(try SizeLimitedFileImpl(fileURL: logURL, fileSizeLimit: 0, fileFactory: factory), "file open failure") { (error) in
|
||||
XCTAssertTrue(error is UnopenableFileFactory.OpenFileError)
|
||||
}
|
||||
}
|
||||
|
||||
func testKeepingFileSizeLimit() throws {
|
||||
let factory = FileMockFactory()
|
||||
let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
let writer = try SizeLimitedFileImpl(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
let data = Data([0])
|
||||
try writer.write(data)
|
||||
|
||||
|
@ -47,7 +47,7 @@ class FileWriterTests: XCTestCase {
|
|||
|
||||
func testExceedingFileSizeLimit() throws {
|
||||
let factory = FileMockFactory()
|
||||
let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
let writer = try SizeLimitedFileImpl(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
let data = Data([0, 0])
|
||||
|
||||
XCTAssertThrowsError(try writer.write(data), "file size limit exceeded") { (error) in
|
||||
|
@ -59,31 +59,31 @@ class FileWriterTests: XCTestCase {
|
|||
|
||||
func testSynchronizingAndClosingFile() throws {
|
||||
let factory = FileMockFactory()
|
||||
let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
let writer = try SizeLimitedFileImpl(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory)
|
||||
writer.synchronizeAndCloseFile()
|
||||
XCTAssertTrue(factory.mock.synchronizeFileCallCount == 1 && factory.mock.closeFileCallCount == 1)
|
||||
}
|
||||
}
|
||||
|
||||
private class UnopenableFileFactory: FileFactory {
|
||||
private class UnopenableFileFactory: FileHandleFactory {
|
||||
|
||||
struct OpenFileError: Error {}
|
||||
|
||||
func makeInstance(forWritingTo: URL) throws -> File {
|
||||
func makeInstance(forWritingTo: URL) throws -> OSFileHandle {
|
||||
throw OpenFileError()
|
||||
}
|
||||
}
|
||||
|
||||
private class FileMockFactory: FileFactory {
|
||||
private class FileMockFactory: FileHandleFactory {
|
||||
|
||||
let mock = FileMock()
|
||||
|
||||
func makeInstance(forWritingTo: URL) throws -> File {
|
||||
func makeInstance(forWritingTo: URL) throws -> OSFileHandle {
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
private class FileMock: File {
|
||||
private class FileMock: OSFileHandle {
|
||||
|
||||
private(set) var writtenData = Data()
|
||||
private(set) var synchronizeFileCallCount = 0
|
Loading…
Add table
Reference in a new issue