FileDestination.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. //
  2. // FileDestination.swift
  3. // SwiftyBeaver
  4. //
  5. // Created by Sebastian Kreutzberger on 05.12.15.
  6. // Copyright © 2015 Sebastian Kreutzberger
  7. // Some rights reserved: http://opensource.org/licenses/MIT
  8. //
  9. import Foundation
  10. public class FileDestination: BaseDestination {
  11. public var logFileURL: URL?
  12. public var syncAfterEachWrite: Bool = false
  13. override public var defaultHashValue: Int {return 2}
  14. let fileManager = FileManager.default
  15. var fileHandle: FileHandle?
  16. public override init() {
  17. // platform-dependent logfile directory default
  18. var baseURL: URL?
  19. #if os(OSX)
  20. if let url = fileManager.urls(for:.cachesDirectory, in: .userDomainMask).first {
  21. baseURL = url
  22. // try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
  23. if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
  24. do {
  25. if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
  26. try fileManager.createDirectory(at: appURL,
  27. withIntermediateDirectories: true, attributes: nil)
  28. baseURL = appURL
  29. }
  30. } catch {
  31. print("Warning! Could not create folder /Library/Caches/\(appName)")
  32. }
  33. }
  34. }
  35. #else
  36. #if os(Linux)
  37. baseURL = URL(fileURLWithPath: "/var/cache")
  38. #else
  39. // iOS, watchOS, etc. are using the caches directory
  40. if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
  41. baseURL = url
  42. }
  43. #endif
  44. #endif
  45. if let baseURL = baseURL {
  46. logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
  47. }
  48. super.init()
  49. // bash font color, first value is intensity, second is color
  50. // see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
  51. // uses the 256-color table from http://bit.ly/1W1qJuH
  52. reset = "\u{001b}[0m"
  53. escape = "\u{001b}[38;5;"
  54. levelColor.verbose = "251m" // silver
  55. levelColor.debug = "35m" // green
  56. levelColor.info = "38m" // blue
  57. levelColor.warning = "178m" // yellow
  58. levelColor.error = "197m" // red
  59. }
  60. // append to file. uses full base class functionality
  61. override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
  62. file: String, function: String, line: Int, context: Any? = nil) -> String? {
  63. let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
  64. if let str = formattedString {
  65. _ = saveToFile(str: str)
  66. }
  67. return formattedString
  68. }
  69. deinit {
  70. // close file handle if set
  71. if let fileHandle = fileHandle {
  72. fileHandle.closeFile()
  73. }
  74. }
  75. /// appends a string as line to a file.
  76. /// returns boolean about success
  77. func saveToFile(str: String) -> Bool {
  78. guard let url = logFileURL else { return false }
  79. do {
  80. if fileManager.fileExists(atPath: url.path) == false {
  81. // create file if not existing
  82. let line = str + "\n"
  83. try line.write(to: url, atomically: true, encoding: .utf8)
  84. #if os(iOS) || os(watchOS)
  85. if #available(iOS 10.0, watchOS 3.0, *) {
  86. var attributes = try fileManager.attributesOfItem(atPath: url.path)
  87. attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
  88. try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
  89. }
  90. #endif
  91. } else {
  92. // append to end of file
  93. if fileHandle == nil {
  94. // initial setting of file handle
  95. fileHandle = try FileHandle(forWritingTo: url as URL)
  96. }
  97. if let fileHandle = fileHandle {
  98. _ = fileHandle.seekToEndOfFile()
  99. let line = str + "\n"
  100. if let data = line.data(using: String.Encoding.utf8) {
  101. fileHandle.write(data)
  102. if syncAfterEachWrite {
  103. fileHandle.synchronizeFile()
  104. }
  105. }
  106. }
  107. }
  108. return true
  109. } catch {
  110. print("SwiftyBeaver File Destination could not write to file \(url).")
  111. return false
  112. }
  113. }
  114. /// deletes log file.
  115. /// returns true if file was removed or does not exist, false otherwise
  116. public func deleteLogFile() -> Bool {
  117. guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
  118. do {
  119. try fileManager.removeItem(at: url)
  120. fileHandle = nil
  121. return true
  122. } catch {
  123. print("SwiftyBeaver File Destination could not remove file \(url).")
  124. return false
  125. }
  126. }
  127. }