前言
上一章(https://blog.csdn.net/cyf649669121/article/details/105637064)说了关于Trello这个插件的使用和用户自定义上报的部分,不过还需要实现的一个就是错误抓取的功能。这个就和手机上用的Bugly就很像了,就是抓取Unity控制台输出的报错和异常,然后分类上报。
不过这个插件没有自己实现,需要我们自己来实现他。
设想
根据现有的一些功能,我想的是当接收到一次报错之后,是先记录下来。因为Unity经常有一个报错报很多次的情况,如果每次都记录下来上报未免太消耗了。而且我想在报错的瞬间抓取一下截图,上传的时候上传下当前的日志。这个是当前插件自带的功能需要整合一下。
此外个人建议是,收集之后不要直接上报。因为在报错收集的监听里是不能打Log的。所以收集到就报错,万一有新的报错岂不是很尴尬?
实现
报错收集
Unity里面收集报错需要监听一个函数:
//这里是两个函数二选一使用。
Application.logMessageReceivedThreaded += OnReceiveLog;
Application.logMessageReceived += OnReceiveLog;
Application.logMessageReceivedThreaded 和 Application.logMessageReceived 这两个函数二选一即可。两个的区别,我从网上看下来的话是 logMessageReceivedThreaded 是可以接受多线程的,而 logMessageReceived 是只能接受主线程的。我在主线程测试了一下两个函数,两个都能收到报错,多线程的没测试过。
我觉得可能区别不太大,我就监听 logMessageReceivedThreaded 了。
报错的监听函数如下:
/// <summary>
/// 一个打印物件;
/// </summary>
struct LogItem
{
public string condition;
public string stackTrace;
public LogItem(string con, string sta)
{
condition = con;
stackTrace = sta;
}
}
/// <summary>
/// 已经生成过的报错;
/// </summary>
Dictionary<LogItem, int> dictLogIttems = new Dictionary<LogItem, int>();
/// <summary>
/// 等待上传的LogItem
/// </summary>
Dictionary<LogItem, Texture2D> dictUploadItem = new Dictionary<LogItem, Texture2D>();
/// <summary>
/// 报错信息的处理;
/// </summary>
/// <param name="condition"></param>
/// <param name="stackTrace">堆栈</param>
/// <param name="type">打印类型</param>
private void OnReceiveLog(string condition, string stackTrace, LogType type)
{
//编辑器下报错不需要上传,除非是在调试状态;
#if UNITY_EDITOR
//return;
#endif
if (type == LogType.Error || type == LogType.Exception)
{
LogItem newItem = new LogItem(condition, stackTrace);
//去重;
if (dictLogIttems.ContainsKey(newItem))
dictLogIttems[newItem]++;
else
{
//记录;
dictLogIttems.Add(newItem, 1);
//这里使用协程是因为截图需要在协程中完成;
StartCoroutine(GetScreenShotForLog(newItem));
}
}
}
IEnumerator GetScreenShotForLog(LogItem newLog)
{
yield return new WaitForEndOfFrame();
//第一次产生的还需要截图;
var tex2D = ScreenshotTool.TakeScreenshot();
dictUploadItem.Add(newLog, tex2D);
}
这里就把报错和异常滤出来进行处理,先存起来。不过这时候的截图确实是要实时的,不过也是只存第一次的截图即可。
报错上传
这里我对上传的函数做了一些改造,大概就是只传一张图,日志可传可不传之类的。考虑到可能会在同一时间上报多次错误,所有做了一些优化。
我这里设定的是,上传是每个1s就上传一次。具体方法如下:
bool IsUploadEnd = true;
/// <summary>
/// 上传所有的Log;
/// </summary>
IEnumerator UploadAll()
{
IsUploadEnd = false;
yield return null;
if (dictUploadItem.Count > 0)
{
//截图需要实时截图,但是Log日志可以在上传时传最终的那个即可;
string logPath = GetLogCopy();
//上传每个错误;
foreach (var item in dictUploadItem)
{
LogItem log = item.Key;
Texture2D tex = item.Value;
TrelloCard card = trello.NewCard(log.condition, log.stackTrace, ListName_AutoReport);
yield return StartCoroutine(SendReportRoutine(card, tex, logPath));
}
}
dictUploadItem.Clear();
IsUploadEnd = true;
}
这样就可以实现报错上传了,上面这个协程1s触发一次,没有上传完成不触发第二次。
int uploadTime = 1;
float lastUploadTime = 0;
private void Update()
{
//分批地上传;
if (IsUploadEnd)
{
//每隔一段时间上报一次
lastUploadTime += Time.deltaTime;
if (lastUploadTime > uploadTime)
{
lastUploadTime = 0;
StartCoroutine(UploadAll());
}
}
}
然后测试一下,随便手动打印个报错。
最后看到已经在网站上有当前的报错上报了:
不过这个有个缺点就是我的屏幕设置成1920*1080的,确实上传有点满了。
后续需要考虑把图片缩小一些,日志也弄小一些。(其实如果有报错堆栈的话,日志有没有其实没什么所谓)。
这样我们就实现了Unity的PC端错误上报。
后记
我后面把商店上的插件做了一些改造:https://download.csdn.net/download/cyf649669121/12346924。
上传的版本没有例子文件和绘图工具(没有那么多花里胡哨的~),新增了我自己写的BugReport类,可以直接移植到别的项目中。另外我还升级了API,把以前的一些过时的API升级到Unity最新的了。
如果这两篇介绍与上传的包有冲突,以最新的为准~~(目前是包是最新的)。