iOS开发:关于日志框架

在移动端开发中,我们为什么需要日志打印框架?

在 iOS 移动端开发中,我们需要日志打印框架的原因主要有以下几点:

  1. 便于调试和排查问题
    日志可以帮助开发者快速定位代码执行流程和异常发生的位置,提升调试效率。
  2. 记录关键运行信息
    日志能记录用户操作、网络请求、错误信息等,有助于分析用户行为和应用运行状况。
  3. 方便线上问题追踪
    在正式环境中,日志可以帮助开发者远程收集崩溃、异常等信息,及时发现和修复线上 bug。
  4. 分级管理日志信息
    日志框架通常支持不同级别(如 debug、info、warning、error),可以灵活控制输出内容,避免信息过载。
  5. 统一日志格式,便于维护
    日志框架可以统一日志输出格式,方便团队协作和后续维护。
  6. 支持日志持久化和上传
    一些日志框架支持将日志保存到本地或上传到服务器,便于后续分析和追踪。

小结

日志打印框架是 iOS 开发中不可或缺的工具,能大大提升开发、测试和运维的效率与质量。

我的实践经历--日志帮我甩锅

咳咳,上面一本正经的说了那么多理由和好处,你或许会说,好像和我们这种小米旮旯没啥事,没错我曾经也是这么认为的,直到我遇到了这样一个项目:

App中,集成了多个不同业务厂家的SDK,而且每个SDK都是闭源的,负责不同的业务,然后在某个版本,App崩溃率飙升!

接着,作为App的主工程的开发者,我被领导各种"问候"。

于是我这个壳App拉着各路SDK开发商一起开了线上会议:

SDK1厂商:我们SDK质量杠杠的!

SDK2厂商:我们SDK没毛病!

SDK3厂商:不是我说,在座的各位都是...

我:要不然,我们都依赖同一个日志输出模块吧,看看打底啥情况?

SDK1厂商、SDK2厂商、SDK3厂商:行,就这么干!

几日后,SDK3厂商:大哥们,我错了,是我们的问题。

小结

有的时候,使用日志框架,目的并没有那么简单,特别是像这种多方集成时候,自证自己清白往往比解决bug更为重要,因为不能保证每个开发都有基本素养!

所以用哪个框架呢?

推荐使用CocoaLumberjack

GitHub - CocoaLumberjack/CocoaLumberjack: A fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS

说起这个库,那真的是绝对老牌,绝对好用,绝对实力!

下面是 CocoaLumberjack 在 iOS 项目中的详细使用示例,包括集成、基本用法和常见配置。

1. 集成 CocoaLumberjack

使用 CocoaPods:

在你的 Podfile 中添加:

ruby 复制代码
pod 'CocoaLumberjack/Swift'

然后执行:

shell 复制代码
pod install

2. 基本配置

AppDelegate.swift 中进行初始化配置:

swift 复制代码
import UIKit
import CocoaLumberjackSwift

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 添加控制台日志输出
        DDLog.add(DDOSLogger.sharedInstance) // 使用系统 os_log

        // 添加文件日志输出
        let fileLogger: DDFileLogger = DDFileLogger() // 创建文件日志
        fileLogger.rollingFrequency = TimeInterval(60*60*24) // 24小时滚动一次
        fileLogger.logFileManager.maximumNumberOfLogFiles = 7 // 最多保存7个日志文件
        DDLog.add(fileLogger)

        return true
    }
}

3. 日志打印示例

在你的代码中直接使用如下方式打印不同级别的日志:

swift 复制代码
import CocoaLumberjackSwift

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        DDLogVerbose("这是 Verbose 日志")
        DDLogDebug("这是 Debug 日志")
        DDLogInfo("这是 Info 日志")
        DDLogWarn("这是 Warning 日志")
        DDLogError("这是 Error 日志")
    }
}

4. 日志级别控制

你可以通过设置全局日志级别来控制输出:

swift 复制代码
// 只输出 Info 及以上级别的日志
dynamicLogLevel = .info

5. 自定义日志格式(可选)

你可以自定义日志输出格式:

swift 复制代码
class CustomLogFormatter: NSObject, DDLogFormatter {
    func format(message logMessage: DDLogMessage) -> String? {
        return "\(logMessage.timestamp) [\(logMessage.level)] \(logMessage.message)"
    }
}

// 在初始化时添加
let formatter = CustomLogFormatter()
DDOSLogger.sharedInstance.logFormatter = formatter

6. 查看日志

  • 控制台日志会直接输出到 Xcode 控制台。
  • 文件日志默认保存在 Library/Caches/Logs 目录下,可以通过 fileLogger.logFileManager.logsDirectory 获取路径。

小结:

CocoaLumberjack 功能强大,配置灵活,适合中大型项目的日志管理需求。你可以根据实际需要进一步扩展和定制。

注意:

  • 除了自定义日志格式,我们其实还可以自定义log文件管理器类,以便于更好的自定义路径,以及排序进行将日志进行分类。
  • DDLogVerbose("这是 Verbose 日志")使用固然方便,但是每个使用的位置都必须进行import CocoaLumberjack操作,非常繁琐,建议做一层简单的二次封装,使用起来更加简单。

日志上传到服务器

日志输出到本地,我们需要定期或者在有需求的时候,静默或者让客户主动上传日志,便于我们通过日志进行问题分析,下面是打包上传的几个步骤:

下面是一个结合 SSZipArchive、Moya,将 CocoaLumberjack 生成的日志文件压缩并上传到服务器的完整示例流程。


1. 获取 CocoaLumberjack 日志文件路径

swift 复制代码
import CocoaLumberjackSwift

func getLogFilePaths() -> [String] {
    if let fileLogger = DDLog.allLoggers.compactMap({ $0 as? DDFileLogger }).first {
        return fileLogger.logFileManager.sortedLogFilePaths
    }
    return []
}

2. 使用 SSZipArchive 压缩日志文件

swift 复制代码
import SSZipArchive

func zipLogFiles(logFilePaths: [String], zipPath: String) -> Bool {
    return SSZipArchive.createZipFile(atPath: zipPath, withFilesAtPaths: logFilePaths)
}

3. 使用 Moya 上传压缩包

定义 Moya Target

swift 复制代码
import Moya

enum LogUploadAPI {
    case uploadLog(zipFileURL: URL)
}

extension LogUploadAPI: TargetType {
    var baseURL: URL { URL(string: "https://your.server.com")! }
    var path: String { "/api/upload/log" }
    var method: Moya.Method { .post }
    var sampleData: Data { Data() }
    var task: Task {
        switch self {
        case .uploadLog(let zipFileURL):
            let formData = MultipartFormData(provider: .file(zipFileURL), name: "file", fileName: "logs.zip", mimeType: "application/zip")
            return .uploadMultipart([formData])
        }
    }
    var headers: [String : String]? {
        ["Content-type": "multipart/form-data"]
    }
}

4. 组合流程:压缩并上传

swift 复制代码
func uploadLogs() {
    // 1. 获取日志文件路径
    let logFilePaths = getLogFilePaths()
    guard !logFilePaths.isEmpty else { return }

    // 2. 压缩日志文件
    let zipPath = NSTemporaryDirectory().appending("logs.zip")
    let zipSuccess = zipLogFiles(logFilePaths: logFilePaths, zipPath: zipPath)
    guard zipSuccess else { return }

    // 3. 上传压缩包
    let provider = MoyaProvider<LogUploadAPI>()
    let zipFileURL = URL(fileURLWithPath: zipPath)
    provider.request(.uploadLog(zipFileURL: zipFileURL)) { result in
        switch result {
        case .success(let response):
            print("上传成功: \(response.statusCode)")
        case .failure(let error):
            print("上传失败: \(error)")
        }
    }
}

5. 调用时机与上传

你可以在需要上传日志的地方调用 uploadLogs() 方法即可。

这里需要注意上传时机与场景非常重要:

我之前遇到过这样一件啼笑皆非的事情,用户反馈App会闪退,运维人员让客户在触发意见反馈页面,进而触发日志上传,结果就在意见反馈页面崩溃了......

一定要保证日志上传的业务场景不会崩溃,同时也不要影响用户体验!


注意:

  • 请根据你的服务器接口实际调整 baseURLpath
  • 日志文件较大时,建议在后台线程处理压缩和上传。
  • 上传完成后可根据需要删除本地 zip 文件。

总结

  • 日志框架绝对要纳入到App开发中的基建建设中;
  • 日志有的时候并不是需要找到自己的问题,而有可能是需要自证自己;
  • CocoaLumberjack是我用的比较多的日志框架,推荐给大家;
  • 日志不仅需要沙盒保存,需要上合适的时机,被动或者主动上传到服务器,以便于远程分析问题。
相关推荐
互联网搬砖老肖8 小时前
Web 架构相关文章目录(持续更新中)
架构
计算机毕设定制辅导-无忧学长8 小时前
Kafka 核心架构与消息模型深度解析(二)
架构·kafka·linq
计算机毕设定制辅导-无忧学长8 小时前
Kafka 核心架构与消息模型深度解析(一)
分布式·架构·kafka
二流小码农12 小时前
鸿蒙开发:实现一个标题栏吸顶
android·ios·harmonyos
shepherd11112 小时前
一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
架构·消息队列·rocketmq
小马爱记录12 小时前
Sentinel微服务保护
spring cloud·微服务·架构·sentinel
程序员老刘13 小时前
20%的选择决定80%的成败
flutter·架构·客户端
渔夫Lee14 小时前
OLTP分库分表数据CDC到Doris的架构设计
架构
梦想画家15 小时前
Apache Druid 架构深度解析:构建高性能分布式数据存储系统
架构·druid·数据工程