Skip to content

Commit

Permalink
Add optional file protection + backup exclusion to FileLoggerable
Browse files Browse the repository at this point in the history
  • Loading branch information
richardhenry committed Feb 7, 2024
1 parent 510741f commit 68dd1bb
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 2 deletions.
13 changes: 12 additions & 1 deletion Sources/Puppy/FileLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ public struct FileLogger: FileLoggerable {

public let fileURL: URL
public let filePermission: String
public let fileProtectionType: FileProtectionType?
public let isExcludedFromBackup: Bool

public let flushMode: FlushMode
public let writeMode: FileWritingErrorHandlingMode

public init(_ label: String, logLevel: LogLevel = .trace, logFormat: LogFormattable? = nil, fileURL: URL, filePermission: String = "640", flushMode: FlushMode = .always, writeMode: FileWritingErrorHandlingMode = .force) throws {
public init(_ label: String,
logLevel: LogLevel = .trace,
logFormat: LogFormattable? = nil,
fileURL: URL, filePermission: String = "640",
fileProtectionType: FileProtectionType? = nil,
isExcludedFromBackup: Bool = false,
flushMode: FlushMode = .always,
writeMode: FileWritingErrorHandlingMode = .force) throws {
self.label = label
self.queue = DispatchQueue(label: label)
self.logLevel = logLevel
Expand All @@ -22,6 +31,8 @@ public struct FileLogger: FileLoggerable {
self.fileURL = fileURL
puppyDebug("initialized, fileURL: \(fileURL)")
self.filePermission = filePermission
self.fileProtectionType = fileProtectionType
self.isExcludedFromBackup = isExcludedFromBackup

self.flushMode = flushMode
self.writeMode = writeMode
Expand Down
14 changes: 14 additions & 0 deletions Sources/Puppy/FileLoggerable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public enum FileWritingErrorHandlingMode: Sendable {
public protocol FileLoggerable: Loggerable, Sendable {
var fileURL: URL { get }
var filePermission: String { get }
var fileProtectionType: FileProtectionType? { get }
var isExcludedFromBackup: Bool { get }
}

extension FileLoggerable {
Expand Down Expand Up @@ -107,6 +109,18 @@ extension FileLoggerable {

if !FileManager.default.fileExists(atPath: fileURL.path) {
let successful = FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: [FileAttributeKey.posixPermissions: uintPermission])

if let fileProtectionType = fileProtectionType {
try FileManager.default.setAttributes([.protectionKey: fileProtectionType], ofItemAtPath: fileURL.path)
}

if isExcludedFromBackup {
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
var fileURL = fileURL
try fileURL.setResourceValues(resourceValues)
}

if successful {
puppyDebug("succeeded in creating filePath")
} else {
Expand Down
14 changes: 13 additions & 1 deletion Sources/Puppy/FileRotationLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ public struct FileRotationLogger: FileLoggerable {

public let fileURL: URL
public let filePermission: String
public var fileProtectionType: FileProtectionType?
public var isExcludedFromBackup: Bool

let rotationConfig: RotationConfig
private weak var delegate: FileRotationLoggerDelegate?

private var dateFormat: DateFormatter

public init(_ label: String, logLevel: LogLevel = .trace, logFormat: LogFormattable? = nil, fileURL: URL, filePermission: String = "640", rotationConfig: RotationConfig, delegate: FileRotationLoggerDelegate? = nil) throws {
public init(_ label: String,
logLevel: LogLevel = .trace,
logFormat: LogFormattable? = nil,
fileURL: URL,
filePermission: String = "640",
fileProtectionType: FileProtectionType? = nil,
isExcludedFromBackup: Bool = false,
rotationConfig: RotationConfig,
delegate: FileRotationLoggerDelegate? = nil) throws {
self.label = label
self.queue = DispatchQueue(label: label)
self.logLevel = logLevel
Expand All @@ -29,6 +39,8 @@ public struct FileRotationLogger: FileLoggerable {
self.fileURL = fileURL
puppyDebug("initialized, fileURL: \(fileURL)")
self.filePermission = filePermission
self.fileProtectionType = fileProtectionType
self.isExcludedFromBackup = isExcludedFromBackup

self.rotationConfig = rotationConfig
self.delegate = delegate
Expand Down
40 changes: 40 additions & 0 deletions Tests/PuppyTests/FileLoggerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,46 @@ final class FileLoggerTests: XCTestCase {
log.remove(fileLogger)
}

func testFileProtection() throws {
#if os(iOS) || os(macOS)
let fileURL = URL(fileURLWithPath: "./protected.log").absoluteURL
let fileLogger: FileLogger = try .init("com.example.yourapp.filelogger.protected", fileURL: fileURL, fileProtectionType: .completeUntilFirstUserAuthentication)
var log = Puppy()
log.add(fileLogger)
log.trace("protection, TRACE message using FileLogger")
log.verbose("protection, VERBOSE message using FileLogger")
fileLogger.flush(fileURL)

let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path)
// swiftlint:disable force_cast
let protection = attributes[FileAttributeKey.protectionKey] as! FileProtectionType
// swiftlint:enable force_cast

XCTAssertEqual(protection, FileProtectionType.completeUntilFirstUserAuthentication)

_ = fileLogger.delete(fileURL)
log.remove(fileLogger)
#endif
}

func testExcludeFromBackup() throws {
#if os(iOS) || os(macOS)
let fileURL = URL(fileURLWithPath: "./exclude-from-backup.log").absoluteURL
let fileLogger: FileLogger = try .init("com.example.yourapp.filelogger.exclude-from-backup", fileURL: fileURL, isExcludedFromBackup: true)
var log = Puppy()
log.add(fileLogger)
log.trace("exclude from backup, TRACE message using FileLogger")
log.verbose("exclude from backup, VERBOSE message using FileLogger")
fileLogger.flush(fileURL)

let resourceValues = try fileURL.resourceValues(forKeys: [.isExcludedFromBackupKey])
XCTAssertTrue(resourceValues.isExcludedFromBackup == true)

_ = fileLogger.delete(fileURL)
log.remove(fileLogger)
#endif
}

func testFilePermissionError() throws {
let permission800FileURL = URL(fileURLWithPath: "./permission800.log").absoluteURL
let filePermission800 = "800"
Expand Down

0 comments on commit 68dd1bb

Please sign in to comment.