瀏覽代碼

update swifty beaver

cyberta 4 年之前
父節點
當前提交
657b20bcd0

+ 2 - 2
Podfile.lock

@@ -10,7 +10,7 @@ PODS:
   - SDWebImage/Core (5.9.1)
   - SwiftFormat/CLI (0.40.9)
   - SwiftLint (0.32.0)
-  - SwiftyBeaver (1.7.0)
+  - SwiftyBeaver (1.9.4)
   - UICircularProgressRing (6.2.0)
 
 DEPENDENCIES:
@@ -44,7 +44,7 @@ SPEC CHECKSUMS:
   SDWebImage: a990c053fff71e388a10f3357edb0be17929c9c5
   SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
   SwiftLint: 009a898ef2a1c851f45e1b59349bf6ff2ddc990d
-  SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
+  SwiftyBeaver: 576177b2c5c94b3aedd5993914e91271a0524e88
   UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
 PODFILE CHECKSUM: 2588d4e56923eb871c0395db08c2ee0058acfefa

+ 2 - 2
Pods/Manifest.lock

@@ -10,7 +10,7 @@ PODS:
   - SDWebImage/Core (5.9.1)
   - SwiftFormat/CLI (0.40.9)
   - SwiftLint (0.32.0)
-  - SwiftyBeaver (1.7.0)
+  - SwiftyBeaver (1.9.4)
   - UICircularProgressRing (6.2.0)
 
 DEPENDENCIES:
@@ -44,7 +44,7 @@ SPEC CHECKSUMS:
   SDWebImage: a990c053fff71e388a10f3357edb0be17929c9c5
   SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
   SwiftLint: 009a898ef2a1c851f45e1b59349bf6ff2ddc990d
-  SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
+  SwiftyBeaver: 576177b2c5c94b3aedd5993914e91271a0524e88
   UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
 PODFILE CHECKSUM: 2588d4e56923eb871c0395db08c2ee0058acfefa

File diff suppressed because it is too large
+ 997 - 1003
Pods/Pods.xcodeproj/project.pbxproj


+ 56 - 17
Pods/SwiftyBeaver/README.md

@@ -1,6 +1,6 @@
-<p align="center"><a href="https://swiftybeaver.com"><img src="https://cloud.githubusercontent.com/assets/564725/19889302/73b1ee84-a034-11e6-8753-2d060502397c.jpg" style="width: 888px;" alt="SwiftyBeaver"></a><br/><b>Colorful</b>, flexible, <b>lightweight</b> logging for Swift 3, Swift 4 & <b>Swift 5</b>.<br/>Great for <b>development & release</b> with support for Console, File & cloud platforms.<br/>Log <b>during release</b> to the conveniently built-in SwiftyBeaver Platform, the <b>dedicated Mac App</b> & <b>Elasticsearch</b>!<br/><br/><a href="http://docs.swiftybeaver.com">Docs</a> | <a href="https://swiftybeaver.com">Website</a> | <a href="https://slack.swiftybeaver.com">Slack</a> | <a href="https://twitter.com/SwiftyBeaver">Twitter</a> | <a href="https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE">License</a><br/></p>
+<p align="center"><a href="https://swiftybeaver.com"><img src="https://cloud.githubusercontent.com/assets/564725/19889302/73b1ee84-a034-11e6-8753-2d060502397c.jpg" style="width: 888px;" alt="SwiftyBeaver"></a><br/><b>Colorful</b>, flexible, <b>lightweight</b> logging for Swift 3, Swift 4 & <b>Swift 5</b>.<br/>Great for <b>development & release</b> with support for Console, File & cloud platforms.<br/>Log <b>during release</b> to the conveniently built-in SwiftyBeaver Platform, the <b>dedicated Mac App</b> & <b>Elasticsearch</b>!<br/><br/><a href="http://docs.swiftybeaver.com">Docs</a> | <a href="https://swiftybeaver.com">Website</a> | <a href="https://twitter.com/SwiftyBeaver">Twitter</a> | <a href="#privacy">Privacy</a> | <a href="https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE">License</a><br/></p>
 
-<p align="center"><a href="https://swift.org" target="_blank"><img src="https://img.shields.io/badge/Language-Swift%203,%204%20&%205-orange.svg" alt="Language Swift 2, 3, 4 & 5"></a> <a href="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver" target="_blank"><img src="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver/tree/master.svg?style=shield" alt="CircleCI"/></a> <a href="https://slack.swiftybeaver.com" target="_blank"><img src="https://img.shields.io/badge/Join-Our%20Slack%20Chat-blue.svg" alt="Slack Status"/></a><br/><p>
+<p align="center"><a href="https://swift.org" target="_blank"><img src="https://img.shields.io/badge/Language-Swift%203,%204%20&%205-orange.svg" alt="Language Swift 2, 3, 4 & 5"></a> <a href="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver" target="_blank"><img src="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver/tree/master.svg?style=shield" alt="CircleCI"/></a><br/><p>
 
 ----
 
@@ -41,11 +41,11 @@ Conveniently access your logs during development & release with our [free Mac Ap
 <br/>
 
 
-### On Release: Enterprise-ready Logging to Your Elasticsearch & Kibana (on-premise)
+### On Release: Enterprise-ready Logging to Your Private and Public Cloud
 
 <img src="https://user-images.githubusercontent.com/564725/34486363-dc501aec-efcf-11e7-92b2-1163cca9e7aa.jpg" width="700">
 
-[Learn more](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker) about **legally compliant**, end-to-end encrypted logging to Elasticsearch with **SwiftyBeaver Enterprise**. Install via Docker or manual, fully-featured free trial included!
+[Learn more](https://swiftybeaver.com/enterprise.html) about **legally compliant**, end-to-end encrypted logging your own cloud with **SwiftyBeaver Enterprise**. Install via Docker or manual, fully-featured free trial included!
 
 <br/>
 
@@ -62,7 +62,8 @@ You can fully customize your log format, turn it into JSON, or create your own d
 
 ## Installation
 
-- For **Swift 3, 4 & 5** install the latest SwiftyBeaver version
+- For **Swift 4 & 5** install the latest SwiftyBeaver version
+- For **Swift 3** install SwiftyBeaver 1.8.4
 - For **Swift 2** install SwiftyBeaver 0.7.0
 
 <br/>
@@ -71,11 +72,17 @@ You can fully customize your log format, turn it into JSON, or create your own d
 
 You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftyBeaver by adding that to your Cartfile:
 
-Swift 3, 4 & 5:
+Swift 4 & 5:
 ``` Swift
 github "SwiftyBeaver/SwiftyBeaver"
 ```
 
+
+Swift 3:
+``` Swift
+github "SwiftyBeaver/SwiftyBeaver" ~> 1.8.4
+```
+
 Swift 2:
 ``` Swift
 github "SwiftyBeaver/SwiftyBeaver" ~> 0.7
@@ -85,25 +92,33 @@ github "SwiftyBeaver/SwiftyBeaver" ~> 0.7
 
 ### Swift Package Manager
 
-For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 3, 4 & 5 are supported:
+For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 4 & 5 are supported:
 
 ``` Swift
-.Package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", majorVersion: 1)
+.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .upToNextMajor(from: "1.9.0")),
 ```
 
 <br/>
 
 ### CocoaPods
 
-We plan to discontinue the support for CocoaPods due to its issues with latest Xcode and Swift versions latest at the end of 2018. Please use Carthage or SPM instead to stay on top of new SwiftyBeaver releases.
-
 To use [CocoaPods](https://cocoapods.org) just add this to your Podfile:
 
-Swift 3, 4 & 5:
+Swift 4 & 5:
 ``` Swift
 pod 'SwiftyBeaver'
 ```
 
+Swift 3:
+``` Ruby
+target 'MyProject' do
+  use_frameworks!
+
+  # Pods for MyProject
+  pod 'SwiftyBeaver', '~> 1.8.4'
+end
+```
+
 Swift 2:
 ``` Ruby
 target 'MyProject' do
@@ -134,7 +149,7 @@ let log = SwiftyBeaver.self
 
 ```
 
-At the the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com//article/20-custom-format) and then you can already do the following log level calls globally:
+At the the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com/article/20-custom-format) and then you can already do the following log level calls globally:
 
 ``` Swift
 // add log destinations. at least one is needed!
@@ -167,7 +182,7 @@ log.error(["name": "Mr Beaver", "address": "7 Beaver Lodge"])
 
 // optionally add context to a log message
 console.format = "$L: $M $X"
-log.debug("age", 123)  // "DEBUG: age 123"
+log.debug("age", context: 123)  // "DEBUG: age 123"
 log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]"
 
 ```
@@ -177,11 +192,11 @@ log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]"
 
 ## Server-side Swift
 
-We ❤️ server-side Swift 3, 4 & 5 and SwiftyBeaver supports it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your the project folder on macOS or Ubuntu and type:
+We ❤️ server-side Swift 4 & 5 and SwiftyBeaver supports it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your the project folder on macOS or Ubuntu and type:
 
 ```shell
 # create docker image, build SwiftyBeaver and run unit tests
-swift build --clean && docker build --rm -t swiftybeaver .
+docker run --rm -it -v $PWD:/app swiftybeaver /bin/bash -c "cd /app ; swift build ; swift test"
 
 # optionally log into container to run Swift CLI and do more stuff
 docker run --rm -it --privileged=true -v $PWD:/app swiftybeaver
@@ -216,13 +231,37 @@ Best: for the popular server-side Swift web framework [Vapor](https://github.com
 **Stay Informed:**
 
 - [Official Website](https://swiftybeaver.com)
-- [Medium Blog](https://medium.com/swiftybeaver-blog)
 - [On Twitter](https://twitter.com/SwiftyBeaver)
 
+<br/>
+<br/>
+
+## Privacy
+
+**SwiftyBeaver is not collecting any data without you as a developer knowing about it**. That's why it is **open-source** and developed in a simple way to be easy to inspect and check what it is actually doing under the hood.
+
+The only sending to servers is done if you use the `SBPlatformDestination`. That destination is meant for production logging and on default it sends your logs plus additional device information **end-to-end encrypted** to our cloud service. Our cloud service **can not decrypt the data**.
+
+Instead, you install our Mac App and that Mac App downloads the encrypted logs from the cloud and decrypts and shows them to you. Additionally, the Mac App stores all data that it downloads in a local SQLite database file on your computer so that you actually "physically" own your data.
+
+The business model of the SwiftyBeaver cloud service is to provide the most secure logging solution in the market. On purpose we do not provide a web UI for you because it would require us to store your encryption key on our servers.
+
+**Only you can see the logging and device data** which is sent from your users' devices. Our servers just see encrypted data and do not know your decryption key.
+
+SwiftyBeaver is **fully GDPR compliant** due to its focus on encryption and transparency in what data is collected and also meets **Apple’s latest requirements** on the privacy of 3rd party frameworks.
+
+Our Enterprise offering is an even more secure solution where you are not using anymore our cloud service and Mac App but you send your end-to-end encrypted logs directly to your own servers and you store them in your Elasticsearch cluster. The **Enterprise offering is used by health tech** and governmental institutions which require the highest level of privacy and security.
+
+<br/>
+<br/>
+
+## End-to-End Encryption
 
-More destination & system documentation is coming soon! <br/>Get support via Github Issues, email and <a href="https://slack.swiftybeaver.com">public Slack channel</a>.
+SwiftyBeaver is using symmetric AES256CBC encryption in the `SBPlatformDestination` destination. No other officially supported destination uses encryption. 
 
+The encryption used in the `SBPlatformDestination` destination is end-to-end. The open-source SwiftyBeaver logging framework symmetrically encrypts all logging data on your client's device inside your app (iPhone, iPad, ...) before it is sent to the SwiftyBeaver Crypto Cloud. The decryption is done on your Mac which has the SwiftyBeaver Mac App installed. All logging data stays encrypted in the SwiftyBeaver Crypto Cloud due to the lack of the password.
 
+You are using the encryption at your own risk. SwiftyBeaver’s authors and contributors do not take over any guarantee about the absence of potential security or cryptopgraphy issues, weaknesses, etc.; please also read the LICENSE file for details. Also if you are interested in cryptography in general, please have a look at the file AES256CBC.swift to learn more about the cryptographical implementation.
 <br/>
 <br/>
 

+ 42 - 118
Pods/SwiftyBeaver/Sources/AES256CBC.swift

@@ -8,32 +8,13 @@
 
 import Foundation
 
-/// cross-platform random numbers generator
-fileprivate struct Random {
-    #if os(Linux)
-    static var initialized = false
-    #endif
-
-    public static func generate(_ upperBound: Int) -> Int {
-        #if os(Linux)
-            if !Random.initialized {
-                srandom(UInt32(time(nil)))
-                Random.initialized = true
-            }
-            return Int(random() % upperBound)
-        #else
-            return Int(arc4random_uniform(UInt32(upperBound)))
-        #endif
-    }
-}
-
 final class AES256CBC {
 
     /// returns optional encrypted string via AES-256CBC
     /// automatically generates and puts a random IV at first 16 chars
     /// the password must be exactly 32 chars long for AES-256
     class func encryptString(_ str: String, password: String) -> String? {
-        if !str.isEmpty && password.length == 32 {
+        if !str.isEmpty && Data(password.utf8).count == 32 {
             let iv = randomText(16)
             let key = password
 
@@ -49,16 +30,12 @@ final class AES256CBC {
     /// returns optional decrypted string via AES-256CBC
     /// IV need to be at first 16 chars, password must be 32 chars long
     class func decryptString(_ str: String, password: String) -> String? {
-        if str.length > 16 && password.length == 32 {
+        if Data(str.utf8).count > 16 && Data(password.utf8).count == 32 {
             // get AES initialization vector from first 16 chars
-            #if swift(>=4.0)
-            let iv = String(str[..<str.index(str.startIndex, offsetBy: 16)])
-            #else
-            let iv = str.substring(to: str.index(str.startIndex, offsetBy: 16))
-            #endif
-
+            let iv = String(str.prefix(16))
             let encryptedString = str.replacingOccurrences(of: iv, with: "",
-                                                           options: String.CompareOptions.literal, range: nil) // remove IV
+                                      options: String.CompareOptions.literal,
+                                      range: nil) // remove IV
 
             guard let decryptedString = try? aesDecrypt(encryptedString, key: password, iv: iv) else {
                 print("an error occured while decrypting")
@@ -68,54 +45,17 @@ final class AES256CBC {
         }
         return nil
     }
-
+    
     /// returns random string (uppercase & lowercase, no spaces) of 32 characters length
-    /// which can be used as SHA-256 compatbile password
-    class func generatePassword() -> String {
-        return randomText(32)
-    }
-
-    /// returns random text of a defined length.
-    /// Optional bool parameter justLowerCase to just generate random lowercase text and
-    /// whitespace to exclude the whitespace character
-    class func randomText(_ length: Int, justLowerCase: Bool = false, whitespace: Bool = false) -> String {
-        var chars = [UInt8]()
-
-        while chars.count < length {
-            let char = CharType.random(justLowerCase, whitespace).randomCharacter()
-            if char == 32 && (chars.last ?? 0) == char {
-                // do not allow two consecutive spaces
-                continue
-            }
-            chars.append(char)
-        }
-        return String(bytes: chars, encoding: .ascii)!
-    }
-
-    /// Used for random text generation
-    fileprivate enum CharType: Int {
-        case LowerCase, UpperCase, Digit, Space
-
-        func randomCharacter() -> UInt8 {
-            switch self {
-            case .LowerCase:
-                return UInt8(Random.generate(26)) + 97
-            case .UpperCase:
-                return UInt8(Random.generate(26)) + 65
-            case .Digit:
-                return UInt8(Random.generate(10)) + 48
-            case .Space:
-                return 32
-            }
-        }
+     /// which can be used as SHA-256 compatbile password
+     class func generatePassword() -> String {
+         return randomText(32)
+     }
 
-        static func random(_ justLowerCase: Bool, _ allowWhitespace: Bool) -> CharType {
-            if justLowerCase {
-                return .LowerCase
-            } else {
-                return CharType(rawValue: Int(Random.generate(allowWhitespace ? 4 : 3)))!
-            }
-        }
+    /// returns random text of a defined length
+    public class func randomText(_ length: Int) -> String {
+        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+        return String((0..<length).map { _ in letters.randomElement()!})
     }
 
     /// returns encrypted string, IV must be 16 chars long
@@ -125,35 +65,33 @@ final class AES256CBC {
         let data = str.data(using: String.Encoding.utf8)!
         #if swift(>=5)
         let enc = try Data(AESCipher(key: keyData.bytes,
-                                            iv: ivData.bytes).encrypt(bytes: data.bytes))
+                                     iv: ivData.bytes).encrypt(bytes: data.bytes))
         #else
         let enc = try Data(bytes: AESCipher(key: keyData.bytes,
                                             iv: ivData.bytes).encrypt(bytes: data.bytes))
         #endif
-        // Swift 3.1.x has a bug with base64encoding under Linux, so we are using our own
-        #if os(Linux)
-            return Base64.encode([UInt8](enc))
-        #else
-            return enc.base64EncodedString(options: [])
-        #endif
+        return enc.base64EncodedString(options: [])
     }
 
     /// returns decrypted string, IV must be 16 chars long
     fileprivate class func aesDecrypt(_ str: String, key: String, iv: String) throws -> String {
         let keyData = key.data(using: String.Encoding.utf8)!
         let ivData = iv.data(using: String.Encoding.utf8)!
-        let data = Data(base64Encoded: str)!
-        #if swift(>=5)
-        let dec = try Data(AESCipher(key: keyData.bytes,
-                                            iv: ivData.bytes).decrypt(bytes: data.bytes))
-        #else
-        let dec = try Data(bytes: AESCipher(key: keyData.bytes,
-                                            iv: ivData.bytes).decrypt(bytes: data.bytes))
-        #endif
-        guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
-            throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
+        if let data = Data(base64Encoded: str) {
+            #if swift(>=5)
+            let dec = try Data(AESCipher(key: keyData.bytes,
+                                         iv: ivData.bytes).decrypt(bytes: data.bytes))
+            #else
+            let dec = try Data(bytes: AESCipher(key: keyData.bytes,
+                                                iv: ivData.bytes).decrypt(bytes: data.bytes))
+            #endif
+            guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
+                throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
+            }
+            return decryptStr
+        } else {
+            return "error"
         }
-        return decryptStr
     }
 
 }
@@ -179,7 +117,7 @@ final class AES256CBC {
 
 // MARK: - AESCipher
 
-fileprivate typealias Key = Array<UInt8>
+private typealias Key = Array<UInt8>
 
 final private class AESCipher {
 
@@ -269,12 +207,6 @@ final private class AESCipher {
         self.iv = iv
     }
 
-    convenience init(key: [UInt8]) throws {
-        // default IV is all 0x00...
-        let defaultIV = [UInt8](repeating: 0, count: AESCipher.blockSize)
-        try self.init(key: key, iv: defaultIV)
-    }
-
     /**
      Encrypt message. If padding is necessary, then PKCS7 padding is added and needs to be removed after decryption.
 
@@ -668,16 +600,9 @@ private struct PKCS7 {
     func add(bytes: [UInt8], blockSize: Int) -> [UInt8] {
         let padding = UInt8(blockSize - (bytes.count % blockSize))
         var withPadding = bytes
-        if padding == 0 {
-            // If the original data is a multiple of N bytes, then an extra block of bytes with value N is added.
-            for _ in 0..<blockSize {
-                withPadding.append(contentsOf: [UInt8(blockSize)])
-            }
-        } else {
-            // The value of each added byte is the number of bytes that are added
-            for _ in 0..<padding {
-                withPadding.append(contentsOf: [UInt8(padding)])
-            }
+        // The value of each added byte is the number of bytes that are added
+        for _ in 0..<padding {
+            withPadding.append(contentsOf: [UInt8(padding)])
         }
         return withPadding
     }
@@ -700,7 +625,7 @@ private struct PKCS7 {
 
 // MARK: - Utils
 
-fileprivate func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
+private func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
     var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
     for i in 0..<xored.count {
         xored[i] = a[i] ^ b[i]
@@ -708,26 +633,26 @@ fileprivate func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
     return xored
 }
 
-fileprivate func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
+private func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
     return ((value << by) & 0xFF) | (value >> (8 - by))
 }
 
-fileprivate func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
+private func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
     return ((value << by) & 0xFFFFFFFF) | (value >> (32 - by))
 }
 
-fileprivate protocol BitshiftOperationsType {
+private protocol BitshiftOperationsType {
     static func << (lhs: Self, rhs: Self) -> Self
 }
 
-fileprivate protocol ByteConvertible {
+private protocol ByteConvertible {
     init(_ value: UInt8)
     init(truncatingBitPattern: UInt64)
 }
 
 #if swift(>=4.0)
 #else
-    extension UInt32 : BitshiftOperationsType, ByteConvertible { }
+    extension UInt32: BitshiftOperationsType, ByteConvertible { }
 #endif
 
 fileprivate extension UInt32 {
@@ -740,7 +665,7 @@ fileprivate extension UInt32 {
     }
 }
 
-fileprivate func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
+private func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
     var result = Array<UInt32>()
     result.reserveCapacity(16)
     for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
@@ -757,7 +682,7 @@ fileprivate func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
 
 /// Array of bytes, little-endian representation. Don't use if not necessary.
 /// I found this method slow
-fileprivate func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
+private func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
     let totalBytes = length ?? MemoryLayout<T>.size
 
     let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
@@ -854,4 +779,3 @@ fileprivate extension Data {
         return Array(self)
     }
 }
-

+ 42 - 112
Pods/SwiftyBeaver/Sources/BaseDestination.swift

@@ -85,7 +85,6 @@ open class BaseDestination: Hashable, Equatable {
 
     open var defaultHashValue: Int {return 0}
 
-
     // each destination instance must have an own serial queue to ensure serial output
     // GCD gives it a prioritization between User Initiated and Utility
     var queue: DispatchQueue? //dispatch_queue_t?
@@ -123,7 +122,7 @@ open class BaseDestination: Hashable, Equatable {
             queue.async(execute: block)
         }
     }
-    
+
     public func executeSynchronously<T>(block: @escaping () throws -> T) rethrows -> T {
         guard let queue = queue else {
             fatalError("Queue not set")
@@ -136,8 +135,7 @@ open class BaseDestination: Hashable, Equatable {
     ////////////////////////////////
 
     /// returns (padding length value, offset in string after padding info)
-    private func parsePadding(_ text: String) -> (Int, Int)
-    {
+    private func parsePadding(_ text: String) -> (Int, Int) {
         // look for digits followed by a alpha character
         var s: String!
         var sign: Int = 1
@@ -147,14 +145,14 @@ open class BaseDestination: Hashable, Equatable {
         } else {
             s = text
         }
-        let numStr = s.prefix { $0 >= "0" && $0 <= "9" }
-        if let num = Int(String(numStr)) {
+        let numStr = String(s.prefix { $0 >= "0" && $0 <= "9" })
+        if let num = Int(numStr) {
             return (sign * num, (sign == -1 ? 1 : 0) + numStr.count)
         } else {
             return (0, 0)
         }
     }
-    
+
     private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String {
         if toLength > 0 {
             // Pad to the left of the string
@@ -172,7 +170,7 @@ open class BaseDestination: Hashable, Equatable {
             return text
         }
     }
-    
+
     /// returns the log message based on the format pattern
     func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String,
         file: String, function: String, line: Int, context: Any? = nil) -> String {
@@ -188,7 +186,7 @@ open class BaseDestination: Hashable, Equatable {
             let formatChar = phrase[formatCharIndex]
             let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..<phrase.endIndex
             let remainingPhrase = phrase[rangeAfterFormatChar]
-            
+
             switch formatChar {
             case "I":  // ignore
                 text += remainingPhrase
@@ -347,16 +345,16 @@ open class BaseDestination: Hashable, Equatable {
         let dateStr = formatter.string(from: Date())
         return dateStr
     }
-    
+
     /// returns a uptime string
     func uptime() -> String {
         let interval = Date().timeIntervalSince(startDate)
-        
+
         let hours = Int(interval) / 3600
         let minutes = Int(interval / 60) - Int(hours * 60)
         let seconds = Int(interval) - (Int(interval / 60) * 60)
         let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
-        
+
         return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds])
     }
 
@@ -437,51 +435,58 @@ open class BaseDestination: Hashable, Equatable {
         if filters.isEmpty {
             if level.rawValue >= minLevel.rawValue {
                 if debugPrint {
-                    print("filters is empty and level >= minLevel")
+                    print("filters are empty and level >= minLevel")
                 }
                 return true
             } else {
                 if debugPrint {
-                    print("filters is empty and level < minLevel")
+                    print("filters are empty and level < minLevel")
                 }
                 return false
             }
         }
 
-        let (matchedExclude, allExclude) = passedExcludedFilters(level, path: path,
-                                                                 function: function, message: message)
-        if allExclude > 0 && matchedExclude != allExclude {
+        let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message))
+
+        // Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method)
+        switch filterCheckResult[.excluded] {
+        case .some(.someFiltersMatch):
+            // Exclusion filters are present and at least one of them matches the log entry
             if debugPrint {
-                print("filters is not empty and message was excluded")
+                print("filters are not empty and message was excluded")
             }
             return false
+        case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break
         }
 
-        let (matchedRequired, allRequired) = passedRequiredFilters(level, path: path,
-                                                                   function: function, message: message)
-        let (matchedNonRequired, allNonRequired) = passedNonRequiredFilters(level, path: path,
-                                                                    function: function, message: message)
-
         // If required filters exist, we should validate or invalidate the log if all of them pass or not
-        if allRequired > 0 {
-            return matchedRequired == allRequired
+        switch filterCheckResult[.required] {
+        case .some(.allFiltersMatch): return true
+        case .some(.someFiltersMatch): return false
+        case .some(.noFiltersMatchingType), .none: break
         }
 
-        // If a non-required filter matches, the log is validated
-		if allNonRequired > 0 {  // Non-required filters exist
-
-			if matchedNonRequired > 0 { return true }  // At least one non-required filter matched
-			else { return false }  // No non-required filters matched
-		}
+        let checkLogLevel: () -> Bool = {
+            // Check if the log message's level matches or exceeds the minLevel of the destination
+            return level.rawValue >= self.minLevel.rawValue
+        }
 
-        if level.rawValue < minLevel.rawValue {
-            if debugPrint {
-                print("filters is not empty and level < minLevel")
+        // Non-required filters should only be applied if the log entry matches the filter condition (e.g. path)
+        switch filterCheckResult[.nonRequired] {
+        case .some(.allFiltersMatch): return true
+        case .some(.noFiltersMatchingType), .none: return checkLogLevel()
+        case .some(.someFiltersMatch(let partialMatchData)):
+            if partialMatchData.fullMatchCount > 0 {
+                // The log entry matches at least one filter condition and the destination's log level
+                return true
+            } else if partialMatchData.conditionMatchCount > 0 {
+                // The log entry matches at least one filter condition, but does not match or exceed the destination's log level
+                return false
+            } else {
+                // There is no filter with a matching filter condition. Check the destination's log level
+                return checkLogLevel()
             }
-            return false
         }
-
-        return true
     }
 
     func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] {
@@ -490,81 +495,6 @@ open class BaseDestination: Hashable, Equatable {
         }
     }
 
-    /// returns a tuple of matched and all filters
-    func passedRequiredFilters(_ level: SwiftyBeaver.Level, path: String,
-                               function: String, message: String?) -> (Int, Int) {
-        let requiredFilters = self.filters.filter { filter in
-            return filter.isRequired() && !filter.isExcluded()
-        }
-
-        let matchingFilters = applyFilters(requiredFilters, level: level, path: path,
-                                           function: function, message: message)
-        if debugPrint {
-            print("matched \(matchingFilters) of \(requiredFilters.count) required filters")
-        }
-
-        return (matchingFilters, requiredFilters.count)
-    }
-
-    /// returns a tuple of matched and all filters
-    func passedNonRequiredFilters(_ level: SwiftyBeaver.Level,
-                                  path: String, function: String, message: String?) -> (Int, Int) {
-        let nonRequiredFilters = self.filters.filter { filter in
-            return !filter.isRequired() && !filter.isExcluded()
-        }
-
-        let matchingFilters = applyFilters(nonRequiredFilters, level: level,
-                                           path: path, function: function, message: message)
-        if debugPrint {
-            print("matched \(matchingFilters) of \(nonRequiredFilters.count) non-required filters")
-        }
-        return (matchingFilters, nonRequiredFilters.count)
-    }
-
-    /// returns a tuple of matched and all exclude filters
-    func passedExcludedFilters(_ level: SwiftyBeaver.Level,
-                               path: String, function: String, message: String?) -> (Int, Int) {
-        let excludeFilters = self.filters.filter { filter in
-            return filter.isExcluded()
-        }
-
-        let matchingFilters = applyFilters(excludeFilters, level: level,
-                                           path: path, function: function, message: message)
-        if debugPrint {
-            print("matched \(matchingFilters) of \(excludeFilters.count) exclude filters")
-        }
-        return (matchingFilters, excludeFilters.count)
-    }
-
-    func applyFilters(_ targetFilters: [FilterType], level: SwiftyBeaver.Level,
-                      path: String, function: String, message: String?) -> Int {
-        return targetFilters.filter { filter in
-
-            let passes: Bool
-
-            if !filter.reachedMinLevel(level) {
-                return false
-            }
-
-            switch filter.getTarget() {
-            case .Path(_):
-                passes = filter.apply(path)
-
-            case .Function(_):
-                passes = filter.apply(function)
-
-            case .Message(_):
-                guard let message = message else {
-                    return false
-                }
-
-                passes = filter.apply(message)
-            }
-
-            return passes
-            }.count
-    }
-
   /**
     Triggered by main flush() method on each destination. Runs in background thread.
    Use for destinations that buffer log items, implement this function to flush those

+ 4 - 5
Pods/SwiftyBeaver/Sources/Extensions.swift

@@ -13,19 +13,19 @@ extension String {
     var length: Int {
         return self.count
     }
-    
+
     /// cross-Swift-compatible first character
     var firstChar: Character? {
         return self.first
     }
-    
+
     /// cross-Swift-compatible last character
     var lastChar: Character? {
         return self.last
     }
-    
+
     /// cross-Swift-compatible index
-    func find(_ char: Character) ->  Index? {
+    func find(_ char: Character) -> Index? {
         #if swift(>=5)
             return self.firstIndex(of: char)
         #else
@@ -33,4 +33,3 @@ extension String {
         #endif
     }
 }
-

+ 142 - 53
Pods/SwiftyBeaver/Sources/FileDestination.swift

@@ -9,20 +9,57 @@
 
 import Foundation
 
-public class FileDestination: BaseDestination {
+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
-    var fileHandle: FileHandle?
 
-    public override init() {
+
+    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 {
+            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 {
@@ -49,20 +86,9 @@ public class FileDestination: BaseDestination {
         #endif
 
         if let baseURL = baseURL {
-            logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
+            self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
         }
         super.init()
-
-        // 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
     }
 
     // append to file. uses full base class functionality
@@ -71,57 +97,121 @@ public class FileDestination: BaseDestination {
         let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
 
         if let str = formattedString {
-            _ = saveToFile(str: str)
+            _ = validateSaveFile(str: str)
         }
         return formattedString
     }
-
-    deinit {
-        // close file handle if set
-        if let fileHandle = fileHandle {
-            fileHandle.closeFile()
+    
+    // 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(filePath)
+                    }
+                } catch {
+                    print("validateSaveFile error: \(error)")
+                }
+            }
         }
+        return saveToFile(str: str)
+    }
+    
+    private func rotateFile(_ filePath: String) {
+       let lastIndex = (logFileAmount-1)
+       let firstIndex = 1
+       do {
+           for index in stride(from: lastIndex, to: firstIndex, by: -1) {
+               let oldFile = String.init(format: "%@.%d", filePath, index)
+
+               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 = String.init(format: "%@.%d", filePath, index+1)
+                       try FileManager.default.moveItem(atPath: oldFile, toPath: newFile)
+                   }
+               }
+           }
+        
+           // Finally, move the current file
+           let newFile = String.init(format: "%@.%d", filePath, firstIndex)
+           try FileManager.default.moveItem(atPath: filePath, toPath: newFile)
+       } catch {
+           print("rotateFile error: \(error)")
+       }
     }
 
     /// appends a string as line to a file.
     /// returns boolean about success
     func saveToFile(str: String) -> Bool {
         guard let url = logFileURL else { return false }
-        do {
-            if fileManager.fileExists(atPath: url.path) == false {
-                // create file if not existing
-                let line = str + "\n"
-                try line.write(to: url, atomically: true, encoding: .utf8)
-                
-                #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
-            } else {
-                // append to end of file
-                if fileHandle == nil {
-                    // initial setting of file handle
-                    fileHandle = try FileHandle(forWritingTo: url as URL)
-                }
-                if let fileHandle = fileHandle {
-                    _ = fileHandle.seekToEndOfFile()
-                    let line = str + "\n"
-                    if let data = line.data(using: String.Encoding.utf8) {
-                        fileHandle.write(data)
-                        if syncAfterEachWrite {
-                            fileHandle.synchronizeFile()
-                        }
+
+        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()
+                fileHandle.write(data)
+                if syncAfterEachWrite {
+                    fileHandle.synchronizeFile()
                 }
+                fileHandle.closeFile()
+                success = true
+            } catch {
+                print("SwiftyBeaver File Destination could not write to file \(url).")
             }
-            return 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.
@@ -130,7 +220,6 @@ public class FileDestination: BaseDestination {
         guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
         do {
             try fileManager.removeItem(at: url)
-            fileHandle = nil
             return true
         } catch {
             print("SwiftyBeaver File Destination could not remove file \(url).")

+ 10 - 11
Pods/SwiftyBeaver/Sources/Filter.swift

@@ -16,10 +16,9 @@ import Foundation
 ///
 /// A filter must contain a target, which identifies what it filters against
 /// A filter can be required meaning that all required filters against a specific
-/// target must pass in order for the message to be logged. At least one non-required
-/// filter must pass in order for the message to be logged
-public protocol FilterType : class {
-    func apply(_ value: Any) -> Bool
+/// target must pass in order for the message to be logged.
+public protocol FilterType : AnyObject {
+    func apply(_ value: String?) -> Bool
     func getTarget() -> Filter.TargetType
     func isRequired() -> Bool
     func isExcluded() -> Bool
@@ -106,8 +105,8 @@ public class CompareFilter: Filter, FilterType {
         self.filterComparisonType = comparisonType
     }
 
-    public func apply(_ value: Any) -> Bool {
-        guard let value = value as? String else {
+    public func apply(_ value: String?) -> Bool {
+        guard let value = value else {
             return false
         }
 
@@ -157,7 +156,7 @@ public class CompareFilter: Filter, FilterType {
         guard let filterComparisonType = self.filterComparisonType else { return false }
 
         switch filterComparisonType {
-        case .Excludes(_, _):
+        case .Excludes:
             return true
         default:
             return false
@@ -261,7 +260,7 @@ public class PathFilterFactory {
     }
 }
 
-extension Filter.TargetType : Equatable {
+extension Filter.TargetType: Equatable {
 }
 
 // The == does not compare associated values for each enum. Instead == evaluates to true
@@ -269,13 +268,13 @@ extension Filter.TargetType : Equatable {
 public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
     switch (lhs, rhs) {
 
-    case (.Path(_), .Path(_)):
+    case (.Path, .Path):
         return true
 
-    case (.Function(_), .Function(_)):
+    case (.Function, .Function):
         return true
 
-    case (.Message(_), .Message(_)):
+    case (.Message, .Message):
         return true
 
     default:

+ 129 - 0
Pods/SwiftyBeaver/Sources/FilterValidator.swift

@@ -0,0 +1,129 @@
+//
+//  FilterValidator.swift
+//  SwiftyBeaver (iOS)
+//
+//  Created by Felix Lisczyk on 07.07.19.
+//  Copyright © 2019 Sebastian Kreutzberger. All rights reserved.
+//
+
+import Foundation
+
+/// FilterValidator is a utility class used by BaseDestination.
+/// It encapsulates the filtering logic for excluded, required
+/// and non-required filters.
+///
+/// FilterValidator evaluates a set of filters for a single log
+/// entry. It determines if these filters apply to the log entry
+/// based on their condition (path, function, message) and their
+/// minimum log level.
+
+struct FilterValidator {
+
+    // These are the different filter types that the user can set
+    enum ValidationType: CaseIterable {
+        case excluded
+        case required
+        case nonRequired
+
+        func apply(to filters: [FilterType]) -> [FilterType] {
+            switch self {
+            case .excluded:
+                return filters.filter { $0.isExcluded() }
+            case .required:
+                return filters.filter { $0.isRequired() && !$0.isExcluded() }
+            case .nonRequired:
+                return filters.filter { !$0.isRequired() && !$0.isExcluded() }
+            }
+        }
+    }
+
+    // Wrapper object for input parameters
+    struct Input {
+        let filters: [FilterType]
+        let level: SwiftyBeaver.Level
+        let path: String
+        let function: String
+        let message: String?
+    }
+
+    // Result wrapper object
+    enum Result {
+        case allFiltersMatch                            // All filters fully match the log entry (condition + minimum log level)
+        case someFiltersMatch(PartialMatchData)         // Only some filters fully match the log entry (condition + minimum log level)
+        case noFiltersMatchingType                      // There are no filters set for a particular type (excluded, required, nonRequired)
+
+        struct PartialMatchData {
+            let fullMatchCount: Int                     // Number of filters that match both the condition and the minimum log level of the log entry
+            let conditionMatchCount: Int                // Number of filters that match ONLY the condition of the log entry (path, function, message)
+            let logLevelMatchCount: Int                 // Number of filters that match ONLY the minimum log level of the log entry
+        }
+    }
+
+    static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] {
+        var results = [ValidationType: Result]()
+        for type in types {
+            let filtersToValidate = type.apply(to: input.filters)
+
+            if filtersToValidate.isEmpty {
+                // There are no filters set for this particular type
+                results[type] = .noFiltersMatchingType
+            } else {
+                var fullMatchCount: Int = 0
+                var conditionMatchCount: Int = 0
+                var logLevelMatchCount: Int = 0
+
+                for filter in filtersToValidate {
+                    let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message)
+                    let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level)
+
+                    switch (filterMatchesCondition, filterMatchesMinLogLevel) {
+                    // Filter matches both the condition and the minimum log level
+                    case (true, true): fullMatchCount += 1
+                    // Filter matches only the condition (path, function, message)
+                    case (true, false): conditionMatchCount += 1
+                    // Filter matches only the minimum log level
+                    case (false, true): logLevelMatchCount += 1
+                    // Filter does not match the condition nor the minimum log level
+                    case (false, false): break
+                    }
+                }
+
+                if filtersToValidate.count == fullMatchCount {
+                    // All filters fully match the log entry
+                    results[type] = .allFiltersMatch
+                } else {
+                    // Only some filters match the log entry
+                    results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount))
+                }
+            }
+        }
+
+        return results
+    }
+
+    private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level,
+                                                path: String, function: String, message: String?) -> Bool {
+            let passes: Bool
+
+            switch filter.getTarget() {
+            case .Path(_):
+                passes = filter.apply(path)
+
+            case .Function(_):
+                passes = filter.apply(function)
+
+            case .Message(_):
+                guard let message = message else {
+                    return false
+                }
+
+                passes = filter.apply(message)
+            }
+
+            return passes
+    }
+
+    private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool {
+        return filter.reachedMinLevel(level)
+    }
+}

+ 18 - 11
Pods/SwiftyBeaver/Sources/SBPlatformDestination.swift

@@ -9,6 +9,10 @@
 
 import Foundation
 
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
 // platform-dependent import frameworks to get device details
 // valid values for os(): OSX, iOS, watchOS, tvOS, Linux
 // in Swift 3 the following were added: FreeBSD, Windows, Android
@@ -305,10 +309,14 @@ public class SBPlatformDestination: BaseDestination {
 
             // POST parameters
             let params = ["payload": payload]
-            do {
-                request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
-            } catch {
-                toNSLog("Error! Could not create JSON for server payload.")
+            if(JSONSerialization.isValidJSONObject(params)){
+                do {
+                    request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
+                } catch {
+                    toNSLog("Error! Could not create JSON for server payload.")
+                    return complete(false, 0)
+                }
+            }else{
                 return complete(false, 0)
             }
             toNSLog("sending params: \(params)")
@@ -406,7 +414,7 @@ public class SBPlatformDestination: BaseDestination {
     }
 
     /// returns optional array of log dicts from a file which has 1 json string per line
-    func logsFromFile(_ url: URL) -> [[String:Any]]? {
+    func logsFromFile(_ url: URL) -> [[String: Any]]? {
         var lines = 0
         do {
             // try to read file, decode every JSON line and put dict from each line in array
@@ -420,7 +428,7 @@ public class SBPlatformDestination: BaseDestination {
                     if let data = lineJSON.data(using: .utf8) {
                         do {
                             if let dict = try JSONSerialization.jsonObject(with: data,
-                                options: .mutableContainers) as? [String:Any] {
+                                options: .mutableContainers) as? [String: Any] {
                                 if !dict.isEmpty {
                                     dicts.append(dict)
                                 }
@@ -470,10 +478,9 @@ public class SBPlatformDestination: BaseDestination {
         osVersionStr += "." + String(osVersion.minorVersion)
         osVersionStr += "." + String(osVersion.patchVersion)
         details["osVersion"] = osVersionStr
-        details["hostName"] = ProcessInfo.processInfo.hostName
         details["deviceName"] = ""
         details["deviceModel"] = ""
-
+        details["hostName"] = ""
         if DEVICE_NAME != "" {
             details["deviceName"] = DEVICE_NAME
         }
@@ -484,7 +491,7 @@ public class SBPlatformDestination: BaseDestination {
     }
 
     /// returns (updated) analytics dict, optionally loaded from file.
-    func analytics(_ url: URL, update: Bool = false) -> [String:Any] {
+    func analytics(_ url: URL, update: Bool = false) -> [String: Any] {
 
         var dict = [String: Any]()
         let now = NSDate().timeIntervalSince1970
@@ -560,12 +567,12 @@ public class SBPlatformDestination: BaseDestination {
     }
 
     /// returns optional dict from a json encoded file
-    func dictFromFile(_ url: URL) -> [String:Any]? {
+    func dictFromFile(_ url: URL) -> [String: Any]? {
         do {
             let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
             if let data = fileContent.data(using: .utf8) {
                 return try JSONSerialization.jsonObject(with: data,
-                                    options: .mutableContainers) as? [String:Any]
+                                    options: .mutableContainers) as? [String: Any]
             }
         } catch {
             toNSLog("SwiftyBeaver Platform Destination could not read file \(url)")

+ 5 - 9
Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift

@@ -12,9 +12,9 @@ import Foundation
 open class SwiftyBeaver {
 
     /// version string of framework
-    public static let version = "1.7.0"  // UPDATE ON RELEASE!
+    public static let version = "1.9.4"  // UPDATE ON RELEASE!
     /// build number of framework
-    public static let build = 1700 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
+    public static let build = 1940 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
 
     public enum Level: Int {
         case verbose = 0
@@ -71,12 +71,8 @@ open class SwiftyBeaver {
             if Thread.isMainThread {
                 return ""
             } else {
-                let threadName = Thread.current.name
-                if let threadName = threadName, !threadName.isEmpty {
-                    return threadName
-                } else {
-                    return String(format: "%p", Thread.current)
-                }
+                let name = __dispatch_queue_get_label(nil)
+                return String(cString: name, encoding: .utf8) ?? Thread.current.description
             }
         #endif
     }
@@ -134,7 +130,7 @@ open class SwiftyBeaver {
     }
 
     /// custom logging to manually adjust values, should just be used by other frameworks
-    public class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
+    open class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
                              file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
         #if swift(>=5)
         dispatch_send(level: level, message: message(), thread: threadName(),

+ 1 - 1
Pods/Target Support Files/SwiftyBeaver/SwiftyBeaver-Info.plist

@@ -15,7 +15,7 @@
   <key>CFBundlePackageType</key>
   <string>FMWK</string>
   <key>CFBundleShortVersionString</key>
-  <string>1.7.0</string>
+  <string>1.9.4</string>
   <key>CFBundleSignature</key>
   <string>????</string>
   <key>CFBundleVersion</key>

Some files were not shown because too many files changed in this diff