iOS日志框架CocoaLumberjack


iOS日志框架CocoaLumberjack

========================
我使用了如下代码进行配置,只需在AppDelegate或SceneDelegate调用configDDLog即可(在其他某些文件配置也是可以的)

//
//  configDDLog.swift
//  RunInto
//
//  Created by 张赛东(手机:15674119605) on 2021/4/10.
//  Copyright © 2021 adong666666. All rights reserved.
//

import Foundation
import CocoaLumberjack
/**
 配置DDLog相关参数
 */
func configDDLog() {
//    //让日志只在debug时输出
//    #if DEBUG
//        defaultDebugLevel = .Verbose
//    #else
//    dynamicLogLevel = .off
//    #endif
    //添加发送日志语句到苹果的日志系统
    DDLog.add(DDOSLogger.sharedInstance)
    //添加发送日志语句到Xcode控制台
    //DDLog.add(DDTTYLogger.sharedInstance!)
    
    //MARK: - 日志将会被保存到沙盒/Library/Caches/Logs,这意味着可以上传至服务器
    let fileLogger: DDFileLogger = DDFileLogger() // File Logger
    fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7
    DDLog.add(fileLogger)
    log(fileLogger.currentLogFileInfo?.filePath)
    
//    //允许控制台带颜色
//    DDTTYLogger.sharedInstance?.colorsEnabled = true
//    //设置Info下为蓝色
//    DDTTYLogger.sharedInstance?.setForegroundColor(UIColor.blue, backgroundColor: UIColor.white, for: .info)
}

/**
 得到输出的字符串的格式

 - parameter message:  日志消息的主题
 - parameter file:     日志消息所在的文件,方便调试定位用
 - parameter function: 日志消息所在的方法,方便调试定位用
 - parameter line:     日志消息所在的方法中的行数,方便调试定位用

 - returns: 返回输出的日志字符串
 */
private func getMessage(message: String, file: StaticString, function: StaticString, line: UInt ) -> String {
    //初始化需要返回的字符串
    var returnMessage: String = ""
    //通过file获取文件的名称
    if let className = file.description.components(separatedBy: CharacterSet.init(charactersIn: "/")).last {
        //拼接字符串
        returnMessage = "\n" +
                  "className:\(className)\n" +
                  " function:\(function)\n" +
                  "      ine:\(line)\n" +
                  "  message:\(message)"
    } else {
        //拼接字符串
        returnMessage = "\n" +
            " function:\(function)\n" +
            "      ine:\(line)\n" +
            "  message:\(message)"
    }
    return returnMessage
}

/**
   输出Info等级的日志消息
 */
public func logInfo(_ message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
    DDLogInfo(getMessage(message: message, file: file, function: function, line: line))
}

/**
 输出Error等级的日志消息
 */
public func logError(_ message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
    DDLogError(getMessage(message: message, file: file, function: function, line: line))
}

/**
 输出Debug等级的日志消息
 */
public func logDebug(_ message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
    DDLogDebug(getMessage(message: message, file: file, function: function, line: line))
}

/**
 输出Warn等级的日志消息
 */
public func logWarn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
    DDLogWarn(getMessage(message: message, file: file, function: function, line: line))
}

public func logVerbose(_ message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
    DDLogVerbose(getMessage(message: message, file: file, function: function, line: line))
}

此外,我还引用了官方demo的一些代码

// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, 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.

#if SWIFT_PACKAGE
import CocoaLumberjack
import CocoaLumberjackSwiftSupport
#endif

/**
 * Replacement for Swift's `assert` function that will output a log message even when assertions
 * are disabled.
 *
 * - Parameters:
 *   - condition: The condition to test. Unlike `Swift.assert`, `condition` is always evaluated,
 *     even when assertions are disabled.
 *   - message: A string to log (using `DDLogError`) if `condition` evaluates to `false`.
 *     The default is an empty string.
 */
@inlinable
public func DDAssert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = "", level: DDLogLevel = .all, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) {
    if !condition() {
        DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
        Swift.assertionFailure(message(), file: file, line: line)
    }
}

/**
 * Replacement for Swift's `assertionFailure` function that will output a log message even
 * when assertions are disabled.
 *
 * - Parameters:
 *   - message: A string to log (using `DDLogError`). The default is an empty string.
 */
@inlinable
public func DDAssertionFailure(_ message: @autoclosure () -> String = "", level: DDLogLevel = .all, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) {
    DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
    Swift.assertionFailure(message(), file: file, line: line)
}
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, 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.

@_exported import CocoaLumberjack
#if SWIFT_PACKAGE
import CocoaLumberjackSwiftSupport
#endif

extension DDLogFlag {
    public static func from(_ logLevel: DDLogLevel) -> DDLogFlag {
        return DDLogFlag(rawValue: logLevel.rawValue)
    }

    public init(_ logLevel: DDLogLevel) {
        self = DDLogFlag(rawValue: logLevel.rawValue)
    }

    /// Returns the log level, or the lowest equivalent.
    public func toLogLevel() -> DDLogLevel {
        if let ourValid = DDLogLevel(rawValue: rawValue) {
            return ourValid
        } else {
            if contains(.verbose) {
                return .verbose
            } else if contains(.debug) {
                return .debug
            } else if contains(.info) {
                return .info
            } else if contains(.warning) {
                return .warning
            } else if contains(.error) {
                return .error
            } else {
                return .off
            }
        }
    }
}

/// The log level that can dynamically limit log messages (vs. the static DDDefaultLogLevel). This log level will only be checked, if the message passes the `DDDefaultLogLevel`.
public var dynamicLogLevel = DDLogLevel.all

/// Resets the `dynamicLogLevel` to `.all`.
/// - SeeAlso: `dynamicLogLevel`
@inlinable
public func resetDynamicLogLevel() {
    dynamicLogLevel = .all
}

@available(*, deprecated, message: "Please use dynamicLogLevel", renamed: "dynamicLogLevel")
public var defaultDebugLevel: DDLogLevel {
    get {
        return dynamicLogLevel
    }
    set {
        dynamicLogLevel = newValue
    }
}

@available(*, deprecated, message: "Please use resetDynamicLogLevel", renamed: "resetDynamicLogLevel")
public func resetDefaultDebugLevel() {
    resetDynamicLogLevel()
}

/// If `true`, all logs (except errors) are logged asynchronously by default.
public var asyncLoggingEnabled = true

@inlinable
public func _DDLogMessage(_ message: @autoclosure () -> Any,
                          level: DDLogLevel,
                          flag: DDLogFlag,
                          context: Int,
                          file: StaticString,
                          function: StaticString,
                          line: UInt,
                          tag: Any?,
                          asynchronous: Bool,
                          ddlog: DDLog) {
    // The `dynamicLogLevel` will always be checked here (instead of being passed in).
    // We cannot "mix" it with the `DDDefaultLogLevel`, because otherwise the compiler won't strip strings that are not logged.
    if level.rawValue & flag.rawValue != 0 && dynamicLogLevel.rawValue & flag.rawValue != 0 {
        // Tell the DDLogMessage constructor to copy the C strings that get passed to it.
        let logMessage = DDLogMessage(message: String(describing: message()),
                                      level: level,
                                      flag: flag,
                                      context: context,
                                      file: String(describing: file),
                                      function: String(describing: function),
                                      line: line,
                                      tag: tag,
                                      options: [.copyFile, .copyFunction],
                                      timestamp: nil)
        ddlog.log(asynchronous: asynchronous, message: logMessage)
    }
}

@inlinable
public func DDLogDebug(_ message: @autoclosure () -> Any,
                       level: DDLogLevel = .debug,
                       context: Int = 0,
                       file: StaticString = #file,
                       function: StaticString = #function,
                       line: UInt = #line,
                       tag: Any? = nil,
                       asynchronous async: Bool = asyncLoggingEnabled,
                       ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: level, flag: .debug, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

@inlinable
public func DDLogInfo(_ message: @autoclosure () -> Any,
                      level: DDLogLevel = .info,
                      context: Int = 0,
                      file: StaticString = #file,
                      function: StaticString = #function,
                      line: UInt = #line,
                      tag: Any? = nil,
                      asynchronous async: Bool = asyncLoggingEnabled,
                      ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: level, flag: .info, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

@inlinable
public func DDLogWarn(_ message: @autoclosure () -> Any,
                      level: DDLogLevel = DDLogLevel.warning,
                      context: Int = 0,
                      file: StaticString = #file,
                      function: StaticString = #function,
                      line: UInt = #line,
                      tag: Any? = nil,
                      asynchronous async: Bool = asyncLoggingEnabled,
                      ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: level, flag: .warning, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

@inlinable
public func DDLogVerbose(_ message: @autoclosure () -> Any,
                         level: DDLogLevel = .verbose,
                         context: Int = 0,
                         file: StaticString = #file,
                         function: StaticString = #function,
                         line: UInt = #line,
                         tag: Any? = nil,
                         asynchronous async: Bool = asyncLoggingEnabled,
                         ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: level, flag: .verbose, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

@inlinable
public func DDLogError(_ message: @autoclosure () -> Any,
                       level: DDLogLevel = .error,
                       context: Int = 0,
                       file: StaticString = #file,
                       function: StaticString = #function,
                       line: UInt = #line,
                       tag: Any? = nil,
                       asynchronous async: Bool = false,
                       ddlog: DDLog = .sharedInstance) {
    _DDLogMessage(message(), level: level, flag: .error, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}

/// Returns a String of the current filename, without full path or extension.
///
/// Analogous to the C preprocessor macro `THIS_FILE`.
public func currentFileName(_ fileName: StaticString = #file) -> String {
    var str = String(describing: fileName)
    if let idx = str.range(of: "/", options: .backwards)?.upperBound {
        str = String(str[idx...])
    }
    if let idx = str.range(of: ".", options: .backwards)?.lowerBound {
        str = String(str[..<idx])
    }
    return str
}

// swiftlint:disable identifier_name
// swiftlint doesn't like func names that begin with a capital letter - deprecated
@available(*, deprecated, message: "Please use currentFileName", renamed: "currentFileName")
public func CurrentFileName(_ fileName: StaticString = #file) -> String {
    return currentFileName(fileName)
}
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, 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.

#if canImport(Combine)

import Combine

#if SWIFT_PACKAGE
import CocoaLumberjack
import CocoaLumberjackSwiftSupport
#endif

@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension DDLog {

    /**
     * Creates a message publisher.
     *
     * The publisher will add and remove loggers as subscriptions are added and removed.
     *
     * The level that you provide here is a preemptive filter (for performance).
     * That is, the level specified here will be used to filter out logMessages so that
     * the logger is never even invoked for the messages.
     *
     * More information:
     *    See -[DDLog addLogger:with:]
     *
     * - Parameter logLevel: preemptive filter of the message returned by the publisher. All levels are sent by default
     * - Returns: A MessagePublisher that emits LogMessages filtered by the specified logLevel
     **/
    public func messagePublisher(with logLevel: DDLogLevel = .all) -> MessagePublisher {
        return MessagePublisher(log: self, with: logLevel)
    }

    // MARK: - MessagePublisher
    
    @available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
    public struct MessagePublisher: Combine.Publisher {

        public typealias Output = DDLogMessage
        public typealias Failure = Never

        private let log: DDLog
        private let logLevel: DDLogLevel

        public init(log: DDLog, with logLevel: DDLogLevel) {
            self.log = log
            self.logLevel = logLevel
        }

        public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {

            let subscription = Subscription(log: self.log, with: logLevel, subscriber: subscriber)
            subscriber.receive(subscription: subscription)
        }
    }

    // MARK: - Subscription

    private final class Subscription<S: Subscriber>: NSObject, DDLogger, Combine.Subscription
        where S.Input == DDLogMessage {
        private var subscriber: S?
        private weak var log: DDLog?

        //Not used but DDLogger requires it. The preferred way to achieve this is to use the `map()` Combine operator of the publisher.
        //ie:
        // DDLog.sharedInstance.messagePublisher()
        //     .map { [format log message] }
        //     .sink(receiveValue: { [process log message] })
        //
        var logFormatter: DDLogFormatter? = nil

        let combineIdentifier = CombineIdentifier()

        init(log: DDLog, with logLevel: DDLogLevel, subscriber: S) {

            self.subscriber = subscriber
            self.log = log

            super.init()

            log.add(self, with: logLevel)
        }

        func request(_ demand: Subscribers.Demand) {
            //The log messages are endless until canceled, so we won't do any demand management.
            //Combine operators can be used to deal with it as needed.
        }

        func cancel() {
            self.log?.remove(self)
            self.subscriber = nil
        }

        func log(message logMessage: DDLogMessage) {
            _ = self.subscriber?.receive(logMessage)
        }
    }
}

@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension Publisher where Output == DDLogMessage {

    public func formatted(with formatter: DDLogFormatter) -> Publishers.CompactMap<Self, String> {
        return compactMap { formatter.format(message: $0) }
    }
}

#endif

文章作者: 张赛东
文章链接: https://zsd.name
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 张赛东 !
评论
  目录