在移动端开发中,我们为什么需要日志打印框架?
在 iOS 移动端开发中,我们需要日志打印框架的原因主要有以下几点:
- 便于调试和排查问题
日志可以帮助开发者快速定位代码执行流程和异常发生的位置,提升调试效率。 - 记录关键运行信息
日志能记录用户操作、网络请求、错误信息等,有助于分析用户行为和应用运行状况。 - 方便线上问题追踪
在正式环境中,日志可以帮助开发者远程收集崩溃、异常等信息,及时发现和修复线上 bug。 - 分级管理日志信息
日志框架通常支持不同级别(如 debug、info、warning、error),可以灵活控制输出内容,避免信息过载。 - 统一日志格式,便于维护
日志框架可以统一日志输出格式,方便团队协作和后续维护。 - 支持日志持久化和上传
一些日志框架支持将日志保存到本地或上传到服务器,便于后续分析和追踪。
小结 :
日志打印框架是 iOS 开发中不可或缺的工具,能大大提升开发、测试和运维的效率与质量。
我的实践经历--日志帮我甩锅
咳咳,上面一本正经的说了那么多理由和好处,你或许会说,好像和我们这种小米旮旯没啥事,没错我曾经也是这么认为的,直到我遇到了这样一个项目:

App中,集成了多个不同业务厂家的SDK,而且每个SDK都是闭源的,负责不同的业务,然后在某个版本,App崩溃率飙升!
接着,作为App的主工程的开发者,我被领导各种"问候"。
于是我这个壳App拉着各路SDK开发商一起开了线上会议:
SDK1厂商:我们SDK质量杠杠的!
SDK2厂商:我们SDK没毛病!
SDK3厂商:不是我说,在座的各位都是...
我:要不然,我们都依赖同一个日志输出模块吧,看看打底啥情况?
SDK1厂商、SDK2厂商、SDK3厂商:行,就这么干!
几日后,SDK3厂商:大哥们,我错了,是我们的问题。
小结:
有的时候,使用日志框架,目的并没有那么简单,特别是像这种多方集成时候,自证自己清白往往比解决bug更为重要,因为不能保证每个开发都有基本素养!
所以用哪个框架呢?
推荐使用CocoaLumberjack
说起这个库,那真的是绝对老牌,绝对好用,绝对实力!


下面是 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会闪退,运维人员让客户在触发意见反馈页面,进而触发日志上传,结果就在意见反馈页面崩溃了......
一定要保证日志上传的业务场景不会崩溃,同时也不要影响用户体验!
注意:
- 请根据你的服务器接口实际调整
baseURL
和path
。 - 日志文件较大时,建议在后台线程处理压缩和上传。
- 上传完成后可根据需要删除本地 zip 文件。
总结
- 日志框架绝对要纳入到App开发中的基建建设中;
- 日志有的时候并不是需要找到自己的问题,而有可能是需要自证自己;
- CocoaLumberjack是我用的比较多的日志框架,推荐给大家;
- 日志不仅需要沙盒保存,需要上合适的时机,被动或者主动上传到服务器,以便于远程分析问题。