Initial commit
This commit is contained in:
240
Pods/SwiftyBeaver/Sources/FileDestination.swift
generated
Normal file
240
Pods/SwiftyBeaver/Sources/FileDestination.swift
generated
Normal file
@ -0,0 +1,240 @@
|
||||
//
|
||||
// FileDestination.swift
|
||||
// SwiftyBeaver
|
||||
//
|
||||
// Created by Sebastian Kreutzberger on 05.12.15.
|
||||
// Copyright © 2015 Sebastian Kreutzberger
|
||||
// Some rights reserved: http://opensource.org/licenses/MIT
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class FileDestination: BaseDestination {
|
||||
|
||||
public var logFileURL: URL?
|
||||
public var syncAfterEachWrite: Bool = false
|
||||
public var colored: Bool = false {
|
||||
didSet {
|
||||
if colored {
|
||||
// bash font color, first value is intensity, second is color
|
||||
// see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
|
||||
// uses the 256-color table from http://bit.ly/1W1qJuH
|
||||
reset = "\u{001b}[0m"
|
||||
escape = "\u{001b}[38;5;"
|
||||
levelColor.verbose = "251m" // silver
|
||||
levelColor.debug = "35m" // green
|
||||
levelColor.info = "38m" // blue
|
||||
levelColor.warning = "178m" // yellow
|
||||
levelColor.error = "197m" // red
|
||||
} else {
|
||||
reset = ""
|
||||
escape = ""
|
||||
levelColor.verbose = ""
|
||||
levelColor.debug = ""
|
||||
levelColor.info = ""
|
||||
levelColor.warning = ""
|
||||
levelColor.error = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOGFILE ROTATION
|
||||
// ho many bytes should a logfile have until it is rotated?
|
||||
// default is 5 MB. Just is used if logFileAmount > 1
|
||||
public var logFileMaxSize = (5 * 1024 * 1024)
|
||||
// Number of log files used in rotation, default is 1 which deactivates file rotation
|
||||
public var logFileAmount = 1
|
||||
|
||||
override public var defaultHashValue: Int {return 2}
|
||||
let fileManager = FileManager.default
|
||||
|
||||
|
||||
public init(logFileURL: URL? = nil) {
|
||||
if let logFileURL = logFileURL {
|
||||
self.logFileURL = logFileURL
|
||||
super.init()
|
||||
return
|
||||
}
|
||||
|
||||
// platform-dependent logfile directory default
|
||||
var baseURL: URL?
|
||||
#if os(OSX)
|
||||
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
// try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
|
||||
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
|
||||
do {
|
||||
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
|
||||
try fileManager.createDirectory(at: appURL,
|
||||
withIntermediateDirectories: true, attributes: nil)
|
||||
baseURL = appURL
|
||||
}
|
||||
} catch {
|
||||
print("Warning! Could not create folder /Library/Caches/\(appName)")
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if os(Linux)
|
||||
baseURL = URL(fileURLWithPath: "/var/cache")
|
||||
#else
|
||||
// iOS, watchOS, etc. are using the caches directory
|
||||
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
|
||||
baseURL = url
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if let baseURL = baseURL {
|
||||
self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
// append to file. uses full base class functionality
|
||||
override open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
|
||||
file: String, function: String, line: Int, context: Any? = nil) -> String? {
|
||||
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
|
||||
|
||||
if let str = formattedString {
|
||||
_ = validateSaveFile(str: str)
|
||||
}
|
||||
return formattedString
|
||||
}
|
||||
|
||||
// check if filesize is bigger than wanted and if yes then rotate them
|
||||
func validateSaveFile(str: String) -> Bool {
|
||||
if self.logFileAmount > 1 {
|
||||
guard let url = logFileURL else { return false }
|
||||
let filePath = url.path
|
||||
if FileManager.default.fileExists(atPath: filePath) == true {
|
||||
do {
|
||||
// Get file size
|
||||
let attr = try FileManager.default.attributesOfItem(atPath: filePath)
|
||||
let fileSize = attr[FileAttributeKey.size] as! UInt64
|
||||
// Do file rotation
|
||||
if fileSize > logFileMaxSize {
|
||||
rotateFile(url)
|
||||
}
|
||||
} catch {
|
||||
print("validateSaveFile error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
return saveToFile(str: str)
|
||||
}
|
||||
|
||||
private func rotateFile(_ fileUrl: URL) {
|
||||
let filePath = fileUrl.path
|
||||
let lastIndex = (logFileAmount-1)
|
||||
let firstIndex = 1
|
||||
do {
|
||||
for index in stride(from: lastIndex, through: firstIndex, by: -1) {
|
||||
let oldFile = makeRotatedFileUrl(fileUrl, index: index).path
|
||||
|
||||
if FileManager.default.fileExists(atPath: oldFile) {
|
||||
if index == lastIndex {
|
||||
// Delete the last file
|
||||
try FileManager.default.removeItem(atPath: oldFile)
|
||||
} else {
|
||||
// Move the current file to next index
|
||||
let newFile = makeRotatedFileUrl(fileUrl, index: index + 1).path
|
||||
try FileManager.default.moveItem(atPath: oldFile, toPath: newFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, move the current file
|
||||
let newFile = makeRotatedFileUrl(fileUrl, index: firstIndex).path
|
||||
try FileManager.default.moveItem(atPath: filePath, toPath: newFile)
|
||||
} catch {
|
||||
print("rotateFile error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func makeRotatedFileUrl(_ fileUrl: URL, index: Int) -> URL {
|
||||
// The index is appended to the file name, to preserve the original extension.
|
||||
fileUrl.deletingPathExtension()
|
||||
.appendingPathExtension("\(index).\(fileUrl.pathExtension)")
|
||||
}
|
||||
|
||||
/// appends a string as line to a file.
|
||||
/// returns boolean about success
|
||||
func saveToFile(str: String) -> Bool {
|
||||
guard let url = logFileURL else { return false }
|
||||
|
||||
let line = str + "\n"
|
||||
guard let data = line.data(using: String.Encoding.utf8) else { return false }
|
||||
|
||||
return write(data: data, to: url)
|
||||
}
|
||||
|
||||
private func write(data: Data, to url: URL) -> Bool {
|
||||
|
||||
#if os(Linux)
|
||||
return true
|
||||
#else
|
||||
var success = false
|
||||
let coordinator = NSFileCoordinator(filePresenter: nil)
|
||||
var error: NSError?
|
||||
coordinator.coordinate(writingItemAt: url, error: &error) { url in
|
||||
do {
|
||||
if fileManager.fileExists(atPath: url.path) == false {
|
||||
|
||||
let directoryURL = url.deletingLastPathComponent()
|
||||
if fileManager.fileExists(atPath: directoryURL.path) == false {
|
||||
try fileManager.createDirectory(
|
||||
at: directoryURL,
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
}
|
||||
fileManager.createFile(atPath: url.path, contents: nil)
|
||||
|
||||
#if os(iOS) || os(watchOS)
|
||||
if #available(iOS 10.0, watchOS 3.0, *) {
|
||||
var attributes = try fileManager.attributesOfItem(atPath: url.path)
|
||||
attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
|
||||
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
let fileHandle = try FileHandle(forWritingTo: url)
|
||||
fileHandle.seekToEndOfFile()
|
||||
if #available(iOS 13.4, watchOS 6.2, tvOS 13.4, macOS 10.15.4, *) {
|
||||
try fileHandle.write(contentsOf: data)
|
||||
} else {
|
||||
fileHandle.write(data)
|
||||
}
|
||||
if syncAfterEachWrite {
|
||||
fileHandle.synchronizeFile()
|
||||
}
|
||||
fileHandle.closeFile()
|
||||
success = true
|
||||
} catch {
|
||||
print("SwiftyBeaver File Destination could not write to file \(url).")
|
||||
}
|
||||
}
|
||||
|
||||
if let error = error {
|
||||
print("Failed writing file with error: \(String(describing: error))")
|
||||
return false
|
||||
}
|
||||
|
||||
return success
|
||||
#endif
|
||||
}
|
||||
|
||||
/// deletes log file.
|
||||
/// returns true if file was removed or does not exist, false otherwise
|
||||
public func deleteLogFile() -> Bool {
|
||||
guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
|
||||
do {
|
||||
try fileManager.removeItem(at: url)
|
||||
return true
|
||||
} catch {
|
||||
print("SwiftyBeaver File Destination could not remove file \(url).")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user