Przeglądaj źródła

Add backend for swift-log

Florian Friedrich 5 lat temu
rodzic
commit
4757611172

+ 16 - 0
Package.resolved

@@ -0,0 +1,16 @@
+{
+  "object": {
+    "pins": [
+      {
+        "package": "swift-log",
+        "repositoryURL": "https://github.com/apple/swift-log.git",
+        "state": {
+          "branch": null,
+          "revision": "173f567a2dfec11d74588eea82cecea555bdc0bc",
+          "version": "1.4.0"
+        }
+      }
+    ]
+  },
+  "version": 1
+}

+ 11 - 1
Package.swift

@@ -19,6 +19,12 @@ let package = Package(
         .library(
             name: "CocoaLumberjackSwift",
             targets: ["CocoaLumberjackSwift"]),
+        .library(
+            name: "CocoaLumberjackSwiftLogBackend",
+            targets: ["CocoaLumberjackSwiftLogBackend"]),
+    ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-log.git", from: "1.2.0"),
     ],
     targets: [
         // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -30,9 +36,13 @@ let package = Package(
         .target(name: "CocoaLumberjackSwift",
                 dependencies: ["CocoaLumberjack", "CocoaLumberjackSwiftSupport"],
                 exclude: ["Supporting Files"]),
+        .target(name: "CocoaLumberjackSwiftLogBackend",
+                dependencies: ["CocoaLumberjack", .product(name: "Logging", package: "swift-log")]),
         .testTarget(name: "CocoaLumberjackTests",
                     dependencies: ["CocoaLumberjack"]),
         .testTarget(name: "CocoaLumberjackSwiftTests",
-                    dependencies: ["CocoaLumberjackSwift"])
+                    dependencies: ["CocoaLumberjackSwift"]),
+        .testTarget(name: "CocoaLumberjackSwiftLogBackendTests",
+                    dependencies: ["CocoaLumberjackSwiftLogBackend"]),
     ]
 )

+ 13 - 1
Package@swift-5.0.swift

@@ -19,6 +19,12 @@ let package = Package(
         .library(
             name: "CocoaLumberjackSwift",
             targets: ["CocoaLumberjackSwift"]),
+        .library(
+            name: "CocoaLumberjackSwiftLogBackend",
+            targets: ["CocoaLumberjackSwiftLogBackend"]),
+    ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-log.git", from: "1.2.0"),
     ],
     targets: [
         // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -30,7 +36,13 @@ let package = Package(
         .target(name: "CocoaLumberjackSwift",
                 dependencies: ["CocoaLumberjack", "CocoaLumberjackSwiftSupport"],
                 exclude: ["Supporting Files"]),
+        .target(name: "CocoaLumberjackSwiftLogBackend",
+                dependencies: ["CocoaLumberjack", "Logging"]),
         .testTarget(name: "CocoaLumberjackTests",
-                    dependencies: ["CocoaLumberjack"])
+                    dependencies: ["CocoaLumberjack"]),
+        .testTarget(name: "CocoaLumberjackSwiftTests",
+                    dependencies: ["CocoaLumberjackSwift"]),
+        .testTarget(name: "CocoaLumberjackSwiftLogBackendTests",
+                    dependencies: ["CocoaLumberjackSwiftLogBackend"]),
     ]
 )

+ 13 - 1
Package@swift-5.1.swift

@@ -19,6 +19,12 @@ let package = Package(
         .library(
             name: "CocoaLumberjackSwift",
             targets: ["CocoaLumberjackSwift"]),
+        .library(
+            name: "CocoaLumberjackSwiftLogBackend",
+            targets: ["CocoaLumberjackSwiftLogBackend"]),
+    ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-log.git", from: "1.2.0"),
     ],
     targets: [
         // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -30,7 +36,13 @@ let package = Package(
         .target(name: "CocoaLumberjackSwift",
                 dependencies: ["CocoaLumberjack", "CocoaLumberjackSwiftSupport"],
                 exclude: ["Supporting Files"]),
+        .target(name: "CocoaLumberjackSwiftLogBackend",
+                dependencies: ["CocoaLumberjack", "Logging"]),
         .testTarget(name: "CocoaLumberjackTests",
-                    dependencies: ["CocoaLumberjack"])
+                    dependencies: ["CocoaLumberjack"]),
+        .testTarget(name: "CocoaLumberjackSwiftTests",
+                    dependencies: ["CocoaLumberjackSwift"]),
+        .testTarget(name: "CocoaLumberjackSwiftLogBackendTests",
+                    dependencies: ["CocoaLumberjackSwiftLogBackend"]),
     ]
 )

+ 11 - 1
Package@swift-5.2.swift

@@ -19,6 +19,12 @@ let package = Package(
         .library(
             name: "CocoaLumberjackSwift",
             targets: ["CocoaLumberjackSwift"]),
+        .library(
+            name: "CocoaLumberjackSwiftLogBackend",
+            targets: ["CocoaLumberjackSwiftLogBackend"]),
+    ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-log.git", from: "1.2.0"),
     ],
     targets: [
         // Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -30,9 +36,13 @@ let package = Package(
         .target(name: "CocoaLumberjackSwift",
                 dependencies: ["CocoaLumberjack", "CocoaLumberjackSwiftSupport"],
                 exclude: ["Supporting Files"]),
+        .target(name: "CocoaLumberjackSwiftLogBackend",
+                dependencies: ["CocoaLumberjack", .product(name: "Logging", package: "swift-log")]),
         .testTarget(name: "CocoaLumberjackTests",
                     dependencies: ["CocoaLumberjack"]),
         .testTarget(name: "CocoaLumberjackSwiftTests",
-                    dependencies: ["CocoaLumberjackSwift"])
+                    dependencies: ["CocoaLumberjackSwift"]),
+        .testTarget(name: "CocoaLumberjackSwiftLogBackendTests",
+                    dependencies: ["CocoaLumberjackSwiftLogBackend"]),
     ]
 )

+ 42 - 0
Sources/CocoaLumberjack/DDLog.m

@@ -1071,6 +1071,48 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
     return self;
 }
 
+- (BOOL)isEqual:(id)other {
+    if (other == self) {
+        return YES;
+    } else if (![super isEqual:other] || ![other isKindOfClass:[self class]]) {
+        return NO;
+    } else {
+        __auto_type otherMsg = (DDLogMessage *)other;
+        return [otherMsg->_message isEqualToString:_message]
+        && otherMsg->_level == _level
+        && otherMsg->_flag == _flag
+        && otherMsg->_context == _context
+        && [otherMsg->_file isEqualToString:_file]
+        && [otherMsg->_fileName isEqualToString:_fileName]
+        && [otherMsg->_function isEqualToString:_function]
+        && otherMsg->_line == _line
+        && (([otherMsg->_tag respondsToSelector:@selector(isEqual:)] && [otherMsg->_tag isEqual:_tag]) || otherMsg->_tag == _tag)
+        && otherMsg->_options == _options
+        && [otherMsg->_timestamp isEqualToDate:_timestamp]
+        && [otherMsg->_threadID isEqualToString:_threadID] // If the thread ID is the same, the name will likely be the same as well.
+        && [otherMsg->_queueLabel isEqualToString:_queueLabel]
+        && otherMsg->_qos == _qos;
+    }
+}
+
+- (NSUInteger)hash {
+    return [super hash]
+    ^ _message.hash
+    ^ _level
+    ^ _flag
+    ^ _context
+    ^ _file.hash
+    ^ _fileName.hash
+    ^ _function.hash
+    ^ _line
+    ^ ([_tag respondsToSelector:@selector(hash)] ? [_tag hash] : 0)
+    ^ _options
+    ^ _timestamp.hash
+    ^ _threadID.hash
+    ^ _queueLabel.hash
+    ^ _qos;
+}
+
 - (id)copyWithZone:(NSZone * __attribute__((unused)))zone {
     DDLogMessage *newMessage = [DDLogMessage new];
 

+ 205 - 0
Sources/CocoaLumberjackSwiftLogBackend/DDLogHandler.swift

@@ -0,0 +1,205 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2020, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+import CocoaLumberjack
+import Logging
+
+extension Logger.Level {
+    @inlinable
+    var ddLogLevelAndFlag: (DDLogLevel, DDLogFlag) {
+        switch self {
+        case .trace: return (.verbose, .verbose)
+        case .debug: return (.debug, .debug)
+        case .info, .notice: return (.info, .info)
+        case .warning: return (.warning, .warning)
+        case .error, .critical: return (.error, .error)
+        }
+    }
+}
+
+extension DDLogMessage {
+    /// Contains the swift-log details of a given log message.
+    public struct SwiftLogInformation: Equatable {
+        /// Contains information about the swift-log logger that logged this message.
+        public struct LoggerInformation: Equatable {
+            /// The label of the swift-log logger that logged this message.
+            public let label: String
+            /// The metadata of the swift-log logger that logged this message.
+            public let metadata: Logger.Metadata
+        }
+
+        /// Contains information about the swift-log message thas was logged.
+        public struct MessageInformation: Equatable {
+            /// The original swift-log message.
+            public let message: Logger.Message
+            /// The original swift-log level of the message. This could be more fine-grained than `DDLogMessage.level` & `DDLogMessage.flag`.
+            public let level: Logger.Level
+            /// The original swift-log metadata of the message.
+            public let metadata: Logger.Metadata?
+            /// The original swift-log source of the message.
+            public let source: String
+        }
+
+        /// The information about the swift-log logger that logged this message.
+        public let logger: LoggerInformation
+        /// The information about the swift-log message that was logged.
+        public let message: MessageInformation
+    }
+
+    /// The swift-log information of this log message. This only exists for messages logged via swift-log.
+    /// - SeeAlso: `DDLogMessage.SwiftLogInformation`
+    @inlinable
+    public var swiftLogInfo: SwiftLogInformation? {
+        return (self as? SwiftLogMessage)?._swiftLogInfo
+    }
+}
+
+/// This class (intentionally internal) is basically only an "encapsulation" layer above `DDLogMessage`.
+/// It's basically an implementation detail of `DDLogMessage.swiftLogInfo`.
+@usableFromInline
+final class SwiftLogMessage: DDLogMessage {
+    @usableFromInline
+    let _swiftLogInfo: SwiftLogInformation
+
+    @usableFromInline
+    init(loggerLabel: String,
+         loggerMetadata: Logger.Metadata,
+         message: Logger.Message,
+         level: Logger.Level,
+         metadata: Logger.Metadata?,
+         source: String,
+         file: String,
+         function: String,
+         line: UInt) {
+        _swiftLogInfo = .init(logger: .init(label: loggerLabel, metadata: loggerMetadata),
+                              message: .init(message: message,
+                                             level: level,
+                                             metadata: metadata,
+                                             source: source))
+        let (ddLogLevel, ddLogFlag) = level.ddLogLevelAndFlag
+        super.init(message: String(describing: message),
+                   level: ddLogLevel,
+                   flag: ddLogFlag,
+                   context: 0,
+                   file: file,
+                   function: function,
+                   line: line,
+                   tag: nil,
+                   options: .dontCopyMessage, // Swift will bridge to NSString. No need to make an additional copy.
+                   timestamp: nil) // Passing nil will make DDLogMessage create the timestamp which saves us the bridging between Date and NSDate.
+    }
+
+    override func isEqual(_ object: Any?) -> Bool {
+        return super.isEqual(object) && (object as? SwiftLogMessage)?._swiftLogInfo == _swiftLogInfo
+    }
+}
+
+/// A swift-log `LogHandler` implenentation that forwards messages to a given `DDLog` instance.
+public struct DDLogHandler: LogHandler {
+    @usableFromInline
+    struct Configuration {
+        @usableFromInline
+        let log: DDLog
+        @usableFromInline
+        let syncLoggingTresholdLevel: Logger.Level
+    }
+
+    @usableFromInline
+    struct LoggerInfo {
+        @usableFromInline
+        let label: String
+        @usableFromInline
+        var logLevel: Logger.Level
+        @usableFromInline
+        var metadata: Logger.Metadata = [:]
+    }
+
+    @usableFromInline
+    let config: Configuration
+    @usableFromInline
+    var loggerInfo: LoggerInfo
+
+    public var logLevel: Logger.Level {
+        get { loggerInfo.logLevel }
+        set { loggerInfo.logLevel = newValue }
+    }
+    public var metadata: Logger.Metadata {
+        get { loggerInfo.metadata }
+        set { loggerInfo.metadata = newValue }
+    }
+
+    @inlinable
+    public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
+        get { return metadata[metadataKey] }
+        set { metadata[metadataKey] = newValue }
+    }
+
+    private init(config: Configuration, loggerInfo: LoggerInfo) {
+        self.config = config
+        self.loggerInfo = loggerInfo
+    }
+
+    @inlinable
+    public func log(level: Logger.Level,
+                    message: Logger.Message,
+                    metadata: Logger.Metadata?,
+                    source: String,
+                    file: String,
+                    function: String,
+                    line: UInt) {
+        let slMessage = SwiftLogMessage(loggerLabel: loggerInfo.label,
+                                        loggerMetadata: loggerInfo.metadata,
+                                        message: message,
+                                        level: level,
+                                        metadata: metadata,
+                                        source: source,
+                                        file: file,
+                                        function: function,
+                                        line: line)
+        config.log.log(asynchronous: level < config.syncLoggingTresholdLevel,
+                       message: slMessage)
+    }
+}
+
+extension DDLogHandler {
+    /// Creates a new `LogHandler` factory using `DDLogHandler` with the given parameters.
+    /// - Parameters:
+    ///   - log: The `DDLog` instance to use for logging. Defaults to `DDLog.sharedInstance`.
+    ///   - defaultLogLevel: The default log level for new loggers. Defaults to `.info`.
+    ///   - syncLoggingTreshold: The level as of which log messages should be logged synchronously instead of asynchronously. Defaults to `.error`.
+    /// - Returns: A new `LogHandler` factory using `DDLogHandler` that can be passed to `LoggingSystem.bootstrap`.
+    /// - SeeAlso: `DDLog`, `LoggingSystem.boostrap`
+    public static func handlerFactory(for log: DDLog = .sharedInstance,
+                                      defaultLogLevel: Logger.Level = .info,
+                                      loggingSynchronousAsOf syncLoggingTreshold: Logger.Level = .error) -> (String) -> LogHandler {
+        let config = DDLogHandler.Configuration(log: log, syncLoggingTresholdLevel: syncLoggingTreshold)
+        return { DDLogHandler(config: config, loggerInfo: .init(label: $0, logLevel: defaultLogLevel)) }
+    }
+}
+
+extension LoggingSystem {
+    /// Bootraps the logging system with a new `LogHandler` factory using `DDLogHandler`.
+    /// - Parameters:
+    ///   - log: The `DDLog` instance to use for logging. Defaults to `DDLog.sharedInstance`.
+    ///   - defaultLogLevel: The default log level for new loggers. Defaults to `.info`.
+    ///   - syncLoggingTreshold: The level as of which log messages should be logged synchronously instead of asynchronously. Defaults to `.error`.
+    /// - SeeAlso: `DDLogHandler.handlerFactory`, `LoggingSystem.bootstrap`
+    @inlinable
+    public static func bootstrapWithCocoaLumberjack(for log: DDLog = .sharedInstance,
+                                                    defaultLogLevel: Logger.Level = .info,
+                                                    loggingSynchronousAsOf syncLoggingTreshold: Logger.Level = .error) {
+        bootstrap(DDLogHandler.handlerFactory(for: log, defaultLogLevel: defaultLogLevel, loggingSynchronousAsOf: syncLoggingTreshold))
+    }
+}

+ 146 - 0
Tests/CocoaLumberjackSwiftLogBackendTests/DDLogHandlerTests.swift

@@ -0,0 +1,146 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2020, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+import XCTest
+import CocoaLumberjack
+@testable import Logging
+@testable import CocoaLumberjackSwiftLogBackend
+
+fileprivate final class MockDDLog: DDLog {
+    struct LoggedMessage: Equatable {
+        let async: Bool
+        let message: DDLogMessage
+    }
+
+    private(set) var loggedMessages = Array<LoggedMessage>()
+
+    override func log(asynchronous: Bool, message logMessage: DDLogMessage) {
+        super.log(asynchronous: asynchronous, message: logMessage)
+        loggedMessages.append(LoggedMessage(async: asynchronous, message: logMessage))
+    }
+}
+
+final class DDLogHandlerTests: XCTestCase {
+    private var mockDDLog: MockDDLog!
+
+    private var logSource: String { "CocoaLumberjackSwiftLogBackendTests" }
+
+    override func setUp() {
+        super.setUp()
+        mockDDLog = MockDDLog()
+    }
+
+    override func tearDown() {
+        mockDDLog = nil
+        super.tearDown()
+    }
+
+    func testBootstrappingWithConvenienceMethod() throws {
+        // It is important that this is the only test using the convenience method,
+        // since another use of it will fail the precondition (multiple bootstrap calls)
+        // All other tests must use `LoggingSystem.bootstrapInternal`.
+        LoggingSystem.bootstrapWithCocoaLumberjack(for: mockDDLog)
+        let logger = Logger(label: "TestLogger")
+        let msg: Logger.Message = "test message"
+        let logLine: UInt = #line + 1
+        logger.info(msg)
+        XCTAssertEqual(mockDDLog.loggedMessages.count, 1)
+        let loggedMsg = try XCTUnwrap(mockDDLog.loggedMessages.first)
+        XCTAssertTrue(loggedMsg.async)
+        XCTAssertEqual(loggedMsg.message.message, String(describing: msg))
+        XCTAssertEqual(loggedMsg.message.level, .info)
+        XCTAssertEqual(loggedMsg.message.flag, .info)
+        XCTAssertEqual(loggedMsg.message.file, #file)
+        XCTAssertEqual(loggedMsg.message.function, #function)
+        XCTAssertEqual(loggedMsg.message.line, logLine)
+        XCTAssertNotNil(loggedMsg.message.swiftLogInfo)
+        XCTAssertEqual(loggedMsg.message.swiftLogInfo, .init(logger: .init(label: logger.label,
+                                                                           metadata: logger.handler.metadata),
+                                                             message: .init(message: msg,
+                                                                            level: .info,
+                                                                            metadata: nil,
+                                                                            source: logSource)))
+    }
+
+    func testBootstrappingWithExplicitMethod() throws {
+        LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory(for: mockDDLog))
+        let logger = Logger(label: "TestLogger")
+        let msg: Logger.Message = "test message"
+        let logLine: UInt = #line + 1
+        logger.info(msg)
+        XCTAssertEqual(mockDDLog.loggedMessages.count, 1)
+        let loggedMsg = try XCTUnwrap(mockDDLog.loggedMessages.first)
+        XCTAssertTrue(loggedMsg.async)
+        XCTAssertEqual(loggedMsg.message.message, String(describing: msg))
+        XCTAssertEqual(loggedMsg.message.level, .info)
+        XCTAssertEqual(loggedMsg.message.flag, .info)
+        XCTAssertEqual(loggedMsg.message.file, #file)
+        XCTAssertEqual(loggedMsg.message.function, #function)
+        XCTAssertEqual(loggedMsg.message.line, logLine)
+        XCTAssertNotNil(loggedMsg.message.swiftLogInfo)
+        XCTAssertEqual(loggedMsg.message.swiftLogInfo, .init(logger: .init(label: logger.label,
+                                                                           metadata: logger.handler.metadata),
+                                                             message: .init(message: msg,
+                                                                            level: .info,
+                                                                            metadata: nil,
+                                                                            source: logSource)))
+    }
+
+    func testDefaults() throws {
+        LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory())
+        let logger = Logger(label: "TestLogger")
+        XCTAssertEqual(logger.logLevel, .info)
+        XCTAssertTrue(logger.handler is DDLogHandler)
+        let ddLogHandler = try XCTUnwrap(logger.handler as? DDLogHandler)
+        XCTAssertEqual(ddLogHandler.loggerInfo.label, logger.label)
+        XCTAssertTrue(ddLogHandler.loggerInfo.metadata.isEmpty)
+        XCTAssertTrue(ddLogHandler.config.log === DDLog.sharedInstance)
+        XCTAssertEqual(ddLogHandler.config.syncLoggingTresholdLevel, .error)
+    }
+
+    func testLoggingAllLevels() throws {
+        let syncTresholdLevel = Logger.Level.warning
+        LoggingSystem.bootstrapInternal(DDLogHandler.handlerFactory(for: mockDDLog, loggingSynchronousAsOf: syncTresholdLevel))
+        var logger = Logger(label: "TestLogger")
+        logger.logLevel = .trace // enable all logs
+        logger[metadataKey: "test-data"] = "test-value"
+        XCTAssertEqual(logger.logLevel, .trace)
+        XCTAssertEqual(logger[metadataKey: "test-data"], "test-value")
+        let messageMeta: Logger.Metadata = ["msg-data": "msg-value"]
+        let logLine: UInt = #line + 2
+        for level in Logger.Level.allCases {
+            logger.log(level: level, "\(level)-msg", metadata: messageMeta)
+        }
+        XCTAssertEqual(mockDDLog.loggedMessages.count, Logger.Level.allCases.count)
+        guard mockDDLog.loggedMessages.count >= Logger.Level.allCases.count else { return } // prevent test crashes
+        for (idx, level) in Logger.Level.allCases.enumerated() {
+            let loggedMsg = mockDDLog.loggedMessages[idx]
+            XCTAssertEqual(loggedMsg.async, level < syncTresholdLevel)
+            XCTAssertEqual(loggedMsg.message.message, "\(level)-msg")
+            XCTAssertEqual(loggedMsg.message.level, level.ddLogLevelAndFlag.0)
+            XCTAssertEqual(loggedMsg.message.flag, level.ddLogLevelAndFlag.1)
+            XCTAssertEqual(loggedMsg.message.file, #file)
+            XCTAssertEqual(loggedMsg.message.function, #function)
+            XCTAssertEqual(loggedMsg.message.line, logLine)
+            XCTAssertNotNil(loggedMsg.message.swiftLogInfo)
+            XCTAssertEqual(loggedMsg.message.swiftLogInfo, .init(logger: .init(label: logger.label,
+                                                                               metadata: logger.handler.metadata),
+                                                                 message: .init(message: "\(level)-msg",
+                                                                                level: level,
+                                                                                metadata: messageMeta,
+                                                                                source: logSource)))
+        }
+    }
+}

+ 14 - 21
Tests/CocoaLumberjackSwiftTests/DDLogCombineTests.swift

@@ -20,20 +20,9 @@ import Combine
 import XCTest
 
 @available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
-class DDLogCombineTests: XCTestCase {
+final class DDLogCombineTests: XCTestCase {
 
-    var subscriptions = Set<AnyCancellable>()
-
-    override func setUp() {
-        super.setUp()
-        DDLog.removeAllLoggers()
-    }
-
-    override func tearDown() {
-        self.subscriptions.removeAll()
-        DDLog.removeAllLoggers()
-        super.tearDown()
-    }
+    private var subscriptions = Set<AnyCancellable>()
 
     private var logFormatter: DDLogFileFormatterDefault {
         //let's return a formatter that doesn't change based where the
@@ -46,8 +35,18 @@ class DDLogCombineTests: XCTestCase {
         return DDLogFileFormatterDefault(dateFormatter: formatter)
     }
 
-    func testMessagePublisherWithDDLogLevelAll() {
+    override func setUp() {
+        super.setUp()
+        DDLog.removeAllLoggers()
+    }
+
+    override func tearDown() {
+        DDLog.removeAllLoggers()
+        self.subscriptions.removeAll()
+        super.tearDown()
+    }
 
+    func testMessagePublisherWithDDLogLevelAll() {
         DDLog.sharedInstance.messagePublisher()
             .sink(receiveValue: { _ in })
             .store(in: &self.subscriptions)
@@ -58,7 +57,6 @@ class DDLogCombineTests: XCTestCase {
     }
 
     func testMessagePublisherWithSpecifiedLevelMask() {
-
         DDLog.sharedInstance.messagePublisher(with: .error)
             .sink(receiveValue: { _ in })
             .store(in: &self.subscriptions)
@@ -69,7 +67,6 @@ class DDLogCombineTests: XCTestCase {
     }
 
     func testMessagePublisherRemovedWhenSubscriptionIsCanceled() {
-
         let sub = DDLog.sharedInstance.messagePublisher()
             .sink(receiveValue: { _ in })
 
@@ -84,7 +81,6 @@ class DDLogCombineTests: XCTestCase {
     }
 
     func testReceivedValuesWithDDLogLevelAll() {
-
         var receivedValue = [DDLogMessage]()
 
         DDLog.sharedInstance.messagePublisher()
@@ -115,7 +111,6 @@ class DDLogCombineTests: XCTestCase {
     }
 
     func testReceivedValuesWithDDLogLevelWarning() {
-
         var receivedValue = [DDLogMessage]()
 
         DDLog.sharedInstance.messagePublisher(with: .warning)
@@ -134,12 +129,10 @@ class DDLogCombineTests: XCTestCase {
         XCTAssertEqual(messages, ["Error", "Warn"])
 
         let levels = receivedValue.map { $0.flag }
-        XCTAssertEqual(levels, [.error,
-                                .warning])
+        XCTAssertEqual(levels, [.error, .warning])
     }
 
     func testFormatted() {
-
         let subject = PassthroughSubject<DDLogMessage, Never>()
 
         var receivedValue = [String]()

+ 3 - 0
Tests/CocoaLumberjackTests/DDOSLoggingTests.m

@@ -39,10 +39,13 @@
         XCTAssertEqualObjects(logger.loggerName, DDLoggerNameOS);
         XCTAssertEqualObjects(logger, DDLog.allLoggers.firstObject);
     } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         __auto_type logger = [DDASLLogger new];
         [DDLog addLogger:logger];
         XCTAssertEqualObjects(logger.loggerName, DDLoggerNameASL);
         XCTAssertEqualObjects(logger, DDLog.allLoggers.firstObject);
+#pragma clang diagnostic pop
     }
 }