跳到主要内容
教程目录

iOS 真机日志怎么看?用 Logger 打日志并过滤导出真机日志

最后更新

iOS 真机日志的看法是:代码里用 Logger 按 subsystem 和 category 打日志,调试时在 VS Code「输出 → KXBuild Console」里看,不调试时用 kxdevice ostrace 按子系统、级别、进程过滤,再导出到文件,和崩溃报告一起分析。日常调试用 print 就够;要在不连调试器时也能查、能按模块过滤、能区分严重程度,改用 Logger(iOS 14 起可用)。Logger 的规则以 Apple 官方文档为准,截至 2026 年 10 月。

你需要一台已经能用 KXBuild 安装调试的 iPhone,连接和信任的步骤见设备准备。

三种写法都能在调试时看到输出,区别在于不调试时还能不能看到,以及能不能过滤。

写法 写到哪里 调试时(KXBuild Console) 不调试时(kxdevice ostrace) 按模块和级别过滤
print 标准输出 能看到 看不到 不能
NSLog 系统日志 能看到 能看到 只能按消息文字
Logger / os_log 系统日志,带子系统、分类、级别 能看到 能看到 能

print 只写到标准输出,不进系统日志,App 从主屏幕启动、没有调试器时,这些输出就丢了。Logger 写进 Apple 的统一日志系统,每条都带子系统、分类和级别,系统按级别决定只放内存还是写入磁盘,并以压缩的二进制格式存储,查看时才转换成文字。

用法上的建议是:临时排查、看完就删的输出用 print;要留在代码里、给测试同事复现问题时看的日志用 Logger。

怎么用 Logger 写日志?

Logger 在 os 模块里,从 iOS 14 起可用。

创建 Logger:subsystem 和 category

每个 Logger 带两个字符串,用来在查日志时把无关的消息筛掉。

  • subsystem。标识 App 里一大块功能,Apple 建议用反向域名,通常直接用 Bundle ID,例如 com.example.myapp。
  • category。标识这个子系统里的某个模块,例如 network、database、ui,命名方式自己定。

把 Logger 集中定义在一个文件里,各处直接引用,名字就不会写乱。

1import os
2
3extension Logger {
4    private static let subsystem = Bundle.main.bundleIdentifier ?? "com.example.myapp"
5
6    static let network = Logger(subsystem: subsystem, category: "network")
7    static let database = Logger(subsystem: subsystem, category: "database")
8    static let ui = Logger(subsystem: subsystem, category: "ui")
9}

用的时候调用对应级别的方法。

1func loadFeed() async {
2    Logger.network.info("开始请求首页数据")
3    do {
4        let items = try await api.fetchFeed()
5        Logger.network.notice("首页数据返回 \(items.count) 条")
6    } catch {
7        Logger.network.error("首页请求失败:\(error.localizedDescription, privacy: .public)")
8    }
9}

五个级别的区别

Apple 的Generating log messages from your code说明,系统先把所有消息放在内存里,级别越严重的越会写到磁盘。下表按严重程度从低到高排列,「是否写入磁盘」一列是 Apple 原文的意思。

Logger 方法 级别 是否写入磁盘 用途
debug Debug 否 只在开发调试时有用的详细信息
info Info 只有用 log 工具收集时才写 有帮助但不是必需的排查信息
notice、log Notice(Default) 是,有存储上限 排查问题必需的信息,例如可能导致失败的情况
error Error 是,有存储上限 代码执行中遇到的错误
fault Fault 是,有存储上限 代码里的缺陷和不该发生的状态

几点要记住。

  • 磁盘上的日志超过预设大小后,系统会清掉旧消息,所以不要指望很久以前的日志一直都在。
  • debug 级别开销很低,可以多写;fault 和 error 会让系统额外收集信息,开销更大,不要拿它们打普通流程。
  • Logger 还有 trace、warning、critical 三个方法,按 Apple 文档分别等同于 debug、error、fault。

隐私修饰 privacy

Apple 文档写明,插值里的整数、浮点数和布尔值默认显示,动态字符串和复杂对象默认会被隐去,防止用户的账号、手机号之类的信息出现在日志文件里。

1let account = "user@example.com"
2let screen = "设置页"
3
4Logger.ui.notice("进入页面 \(screen, privacy: .public)")        // 显示原文
5Logger.ui.notice("当前账号 \(account)")                          // 默认隐去
6Logger.ui.notice("当前账号 \(account, privacy: .private(mask: .hash))")  // 隐去,但同一个值的哈希相同
  • 确定不敏感的字符串,例如页面名、错误描述、接口路径,加 privacy: .public,否则在设备日志里看不到内容。
  • 账号、令牌、位置这类数据保持默认,或者明确写 .private。
  • 想知道几条日志是不是同一个用户,又不想暴露原值时,用 .private(mask: .hash)。

可选项的完整列表见 OSLogPrivacy。

Objective-C 里用 os_log

Objective-C 用 os_log_create 创建日志对象,按级别调用对应的函数,字符串参数用 %{public}@ 标成公开。

 1#import <os/log.h>
 2
 3static os_log_t NetworkLog(void) {
 4    static os_log_t log;
 5    static dispatch_once_t once;
 6    dispatch_once(&once, ^{
 7        log = os_log_create("com.example.myapp", "network");
 8    });
 9    return log;
10}
11
12os_log_info(NetworkLog(), "开始请求 %{public}@", url.path);
13os_log_error(NetworkLog(), "请求失败,状态码 %d", statusCode);

调试时 iOS 真机日志在哪看?

按 F5 构建并运行后,App 里 print、NSLog、os_log 的输出显示在 VS Code「输出」面板的 KXBuild Console 通道,构建日志在 KXBuild 通道。

  1. 按 Ctrl+Shift+U 打开「输出」面板。
  2. 在面板右上角的下拉框里选 KXBuild Console。
  3. 用面板右上角的筛选框输入关键字,只显示包含它的行。

调试结束会退出手机上的 App,KXBuild Console 也就不再有新输出。要看 App 不在调试时的日志,或者要按级别、子系统筛选,用下一节的 kxdevice。

App 不在调试时,kxdevice ostrace 怎么过滤?

kxdevice ostrace 实时输出手机上的 os_log,NSLog 和 Logger 都经过它,比 syslog 干净,支持过滤。它不需要 App 在调试中,从主屏幕点开 App 也能看到。

1kxdevice ostrace --subsystem com.example.myapp

按 Ctrl-C 停止。可用的过滤参数如下,以 kxdevice 命令参考为准。

参数 作用
--levels default,info,debug,error,fault 只输出这些级别,逗号分隔
--pid N 只看这个进程,-1 为全部
--subsystem S 子系统名包含 S
--match S 消息必须包含 S
--exclude S 消息不能包含 S

--levels 里的 default 对应 Logger 的 notice 和 log,没有单独的 notice 写法。接了多台手机时,在命令前面加全局参数 --udid 指定设备。

按 App 过滤

日志用了 Bundle ID 做 subsystem 时,--subsystem 最直接。只想看错误时加上级别。

1kxdevice ostrace --subsystem com.example.myapp --levels error,fault

第三方库和 NSLog 的日志通常不带你的 subsystem,要连它们一起看,就按进程过滤。先用 kxdevice 启动 App,它会打印 pid,App 已在运行时用 apps ps 查。

1kxdevice apps launch com.example.myapp
2kxdevice apps ps --apps
3kxdevice ostrace --pid 1234

--subsystem 是「包含」匹配,填 com.example 会同时匹配 App 和它的扩展,例如小组件。

按分类和关键字过滤

ostrace 没有单独的分类参数。每行输出里都带着「子系统/分类」,用 findstr 或 grep 按分类筛。

1kxdevice ostrace --subsystem com.example.myapp | findstr "com.example.myapp/network"
1kxdevice ostrace --subsystem com.example.myapp | grep "com.example.myapp/network"

--match 和 --exclude 只看消息文字,适合按业务关键字筛,或者去掉刷屏的心跳日志。

1kxdevice ostrace --subsystem com.example.myapp --match 订单 --exclude heartbeat

syslog:整机系统日志

要看系统服务怎么处理你的 App,例如安装失败、权限被拒、App 被系统结束,用 kxdevice syslog。它输出整机日志,量很大,一定要配合过滤。

1kxdevice syslog | findstr MyApp

导出到文件

ostrace 和 syslog 都是实时输出,不会回放已经过去的日志。所以顺序是先开始记录,再复现问题。

在 PowerShell 里把输出存成 UTF-8 文件,中文不会乱码。

1kxdevice ostrace --subsystem com.example.myapp | Out-File -Encoding utf8 .\myapp-log.txt

在 Linux 或 WSL 里用 tee,屏幕上照常显示,同时写进文件。

1kxdevice ostrace --subsystem com.example.myapp | tee myapp-log.txt

复现完按 Ctrl-C 停止,文件里就是这段时间的日志。发给同事或附到问题单时,说明手机型号、iOS 版本和复现步骤。

不想记命令时,执行 kxdevice web,用浏览器打开 http://localhost:8780,系统日志、崩溃报告、进程列表都能在网页上查看。

怎么结合崩溃报告分析日志?

App 崩溃后,崩溃报告告诉你崩在哪一行,日志告诉你崩溃之前发生了什么。两者对着看,比只看调用栈快得多。

  1. 开一个终端,把 ostrace 导出到文件。
  2. 在手机上复现崩溃。
  3. 停止记录,拉取崩溃报告。
1kxdevice crash list MyApp*
2kxdevice crash pull .\crashes MyApp*
  1. 打开 .ips 文件,记下崩溃时间,在日志文件里找这个时间点之前的最后几条。
  2. 看最后一条正常日志之后、崩溃之前做了什么操作,缩小可疑代码的范围。

为了让这一步有用,在关键流程的入口和出口各打一条 notice,异常分支打 error。崩溃报告的读法见 Windows 上真机调试 iOS App的崩溃一节。

Flutter 和 React Native 工程的日志在哪看?

跨平台工程里有两层日志,分别在不同的地方看。

日志来源 在哪看
ios 目录下 Swift、Objective-C 代码的 print、NSLog、Logger 调试时在「输出 → KXBuild Console」,不调试时用 kxdevice ostrace
Flutter 的 Dart 日志(print、debugPrint、dart:developer 的 log) 按 Flutter 官方说明,在 DevTools 的 Logging 视图里看
React Native 的 JS 日志(console.log 等) React Native DevTools 的 Console 面板,在运行 Metro 的终端里按 j 打开
  • Flutter。原生插件、MethodChannel 处理函数里的日志用 Logger 写,和纯原生 App 一样过滤。Dart 一侧用 developer.log 时,name 参数起的作用类似 category,方便在 DevTools 里分类,见 Flutter 文档的 Add logging to your application。调试步骤见 Flutter 真机调试。
  • React Native。原生模块的日志在 KXBuild Console,JS 日志和 JS 断点用 React Native 自带的调试工具,见 React Native 开发 iOS App。

日志级别与用途对照

写日志前对一下这张表,选好级别,查的时候 --levels 就能一次筛准。

场景 Logger 方法 ostrace --levels 写法
打印变量、循环里的中间值,只在开发时看 debug debug
记录流程进展,例如开始请求、读取缓存 info info
关键节点,例如登录成功、支付回调、首屏显示 notice default
可恢复的错误,例如请求失败、解析失败 error error
不该发生的状态,例如数据不一致、断言之外的异常分支 fault fault
只看出问题的部分 — error,fault

按这张表定好级别,iOS 真机日志在 KXBuild Console 和 ostrace 里都能一次筛到要看的那几条。

常见问题

iPhone 上 App 没在调试时怎么看日志?

用 kxdevice ostrace 实时读取手机上的 os_log,加 –subsystem 填你的 Bundle ID 就只剩自己 App 的日志。代码里要用 Logger、os_log 或 NSLog,print 的输出不会出现在这里。

Logger 打的字符串为什么显示不出来?

动态字符串默认被系统当作隐私数据隐去。确定不敏感的值在插值里加 privacy: .public,例如 (screen, privacy: .public)。

Logger 的 debug 和 info 日志为什么过后找不到?

按 Apple 文档,debug 级别不写入磁盘,info 只有用 log 工具收集时才写入。要事后还能查到的日志用 notice 或更高级别,并且在复现前就开始用 ostrace 记录。

kxdevice ostrace 能按 category 过滤吗?

没有单独的分类参数。每行输出都带有子系统和分类名,用 –subsystem 先筛出自己的 App,再用 findstr 或 grep 按「子系统/分类」筛选。

怎么把 iPhone 日志导出成文件发给别人?

先开始记录再复现问题。PowerShell 里用 kxdevice ostrace –subsystem 你的BundleID | Out-File -Encoding utf8 .\log.txt,Linux 里用 tee,复现完按 Ctrl-C 停止。