Unity 调试日志与异常处理速查

这篇按排查问题来查:怎么打日志、日志写什么、构建后日志在哪、异常怎么捕获、排查记录怎么留。


快速索引

我想做什么 常用方法
打普通日志 Debug.Log
打警告 Debug.LogWarning
打错误 Debug.LogError
带对象定位 Debug.Log("msg", this)
开发期断言 Debug.Assert
条件编译日志 [Conditional("UNITY_EDITOR")]
捕获异常 try/catch
async 捕获异常 async 入口自己 try/catch
查构建后日志 Player.log
看完整堆栈 Console 第一条错误

0. Debug API

1
2
3
4
Debug.Log("普通信息");                 // 普通流程日志
Debug.LogWarning("警告信息"); // 可恢复但需要注意
Debug.LogError("错误信息"); // 功能无法继续
Debug.Assert(condition, "断言失败"); // 开发期检查

带上下文对象:

1
Debug.LogWarning("Player 引用为空", this); // Console 点击可定位对象

1. 日志格式

推荐:

1
[模块] 行为 | 关键参数 | 对象/场景

示例:

1
2
3
Debug.Log($"[Inventory] AddItem id={itemId}, count={count}", this);
Debug.LogWarning($"[UI] Button missing binding: {buttonName}", this);
Debug.LogError($"[Config] Load failed path={path}, reason={reason}");

少写:

1
Debug.Log("出错了");                   // 没有模块、参数、对象,难查

2. 条件日志

1
2
3
4
5
6
7
8
9
10
11
12
using System.Diagnostics;
using UnityEngine;

public static class GameLog
{
[Conditional("UNITY_EDITOR")]
[Conditional("DEVELOPMENT_BUILD")]
public static void Info(string message)
{
Debug.Log(message); // 只在编辑器或开发包输出
}
}

调用:

1
GameLog.Info("[Battle] Spawn enemy");   // Release 可被编译移除

3. 模块开关日志

1
2
3
4
5
6
7
8
9
10
public static class LogSwitch
{
public static bool Battle = true;
public static bool UI = false;
}

if (LogSwitch.Battle)
{
Debug.Log("[Battle] Attack hit");
}

适合:

1
2
3
4
战斗日志
网络日志
资源加载日志
UI 打开关闭日志

4. try / catch

推荐:

1
2
3
4
5
6
7
8
try
{
LoadConfig(path);
}
catch (IOException ex)
{
Debug.LogError($"[Config] Load failed path={path}\n{ex}");
}

只捕获能处理的异常:

1
2
3
catch (FormatException ex)              // 配置格式错误
catch (IOException ex) // 文件读写错误
catch (OperationCanceledException) // async 任务取消

不建议:

1
2
3
4
catch
{
// 空 catch 会吞掉问题
}

5. async 异常

1
2
3
4
5
6
7
8
9
10
11
private async void OnClick()
{
try
{
await LoadAsync();
}
catch (Exception ex)
{
Debug.LogError($"[UI] Load button failed\n{ex}", this);
}
}

async void 只适合:

1
2
3
按钮事件
UnityEvent 回调
生命周期入口里转 async

普通异步方法优先:

1
2
3
4
private async Task LoadAsync()
{
await Task.Delay(1000);
}

6. 断点调试

适合断点:

1
2
3
4
5
复杂 if 分支
数据状态不对
事件触发顺序不对
异步 await 前后
对象池取出回收

断点时重点看:

1
2
3
4
5
当前对象是否为 null
Inspector 引用是否存在
集合 Count 是否符合预期
事件是否触发多次
当前场景和对象层级

7. 构建后日志位置

Windows:

1
%USERPROFILE%\AppData\LocalLow\CompanyName\ProductName\Player.log

macOS:

1
~/Library/Logs/CompanyName/ProductName/Player.log

Linux:

1
~/.config/unity3d/CompanyName/ProductName/Player.log

Android:

1
2
adb logcat -s Unity                    # 查看 Unity 日志
adb logcat | grep Unity # 过滤 Unity

iOS:

1
Xcode Devices and Simulators -> Open Console

8. 发布包建议记录

1
2
3
4
5
6
7
8
9
游戏版本
构建号
平台
设备型号
当前场景
账号 / 存档 id
关键配置版本
资源版本
最近操作

9. 引用防御

1
2
3
4
5
6
7
8
9
private void Awake()
{
if (player == null)
{
Debug.LogError("[Battle] player is null", this);
enabled = false; // 核心引用缺失时禁用脚本
return;
}
}

10. 排查记录模板

1
2
3
4
5
6
7
8
9
10
11
12
问题现象:
复现步骤:
复现概率:
平台:
Unity 版本:
构建版本:
第一条错误:
完整堆栈:
最近改动:
定位过程:
修复方式:
验证结果:

11. 常见坑速查

1
2
3
4
5
6
7
只写“出错了”                         # 没上下文
只看 Console 最后一条 # 第一条通常才是根因
catch 后不打印 ex # 堆栈丢失
每帧 Debug.Log # 严重卡顿
发布包完全没有日志 # 线上问题难查
async void 不 catch # 异常难定位
日志没有版本/平台/场景 # 无法复现

系列导航