背景交代
在实际开发的过程中,为了使得项目的log能更加的有规范和控制性,通常我们都会对unity自身的Log进行二次包装!
实现
由于unity的Console Log在双击后都是打开某个资源的,所以我们需要监听Unity的Console的双击事件:
双击回调
Unity提供了一系列的回调APi
OnOpenAsset
是Unity提供的一个打开资源前的回调;
/// <summary>
/// 双击Unity日志
/// </summary>
/// <param name="instanceId">instance id</param>
/// <param name="line">行数</param>
/// <returns>false:Unity继续默认打开;true:需要自行打开</returns>
[OnOpenAsset(0)]
public static bool DoubleClickLog(int instanceId, int line)
{
return true;
}
添加以上代码后,我们发现无论是在project中双击资源还是在Console Log中双击资源,都会执行该方法。
因此我们需要在双击的时候检查一下双击的目标是不是在Console Log中:
信息获取
/// <summary>
/// 获取Console Log中的信息
/// </summary>
/// <returns>信息</returns>
private static string GetStackTrace()
{
var editorWindowAssembly = typeof(EditorWindow).Assembly;
var consoleWindowType =editorWindowAssembly.GetType("UnityEditor.ConsoleWindow");
var consoleWindowFieldInfo = consoleWindowType.GetField(
"ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
var consoleWindow = consoleWindowFieldInfo.GetValue(null) as EditorWindow;
if (consoleWindow != EditorWindow.focusedWindow)
{
return null;
}
var activeTextFieldInfo = consoleWindowType.GetField(
"m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
return (string)activeTextFieldInfo.GetValue(consoleWindow);
}
使用反射的方式获取Unity当前正在使用的Console Log窗口,利用EditorWindow.focusedWindow
来判定用户当前是在哪个Window
下双击的。
自定义规则
在实际项目中,经常有这样的log
WDFramework.AddressableBridge==>Addressables.InitializeAsync() OnComplete==>Succeeded
UnityEngine.Debug:LogError(Object)
WDFramework.WDLog:Error(Object) (at Packages/framework/Runtime/Log/WDLog.cs:35)
WDFramework.AddressableBridge:OnComplete(AsyncOperationHandle`1) (at Packages/framework/Runtime/ResourceSystem/AddressableBridge.cs:56)
DelegateList`1:Invoke(AsyncOperationHandle`1) (at Library/PackageCache/com.unity.addressables@1.16.1/Runtime/ResourceManager/Util/DelegateList.cs:69)
UnityEngine.ResourceManagement.ResourceManager:Update(Single)
MonoBehaviourCallbackHooks:Update() (at Library/PackageCache/com.unity.addressables@1.16.1/Runtime/ResourceManager/Util/MonoBehaviourCallbackHooks.cs:26)
当我们需要查看该日志输出的位置的时候,直接双击都是跳转到WDLog这个文件下,那么我们古河过滤掉自己写的包装Log文件?
public static bool DoubleClickLog(int instanceId, int line)
{
var trackInfo = GetStackTrace();
// 不是在Log界面点击的!
if (string.IsNullOrEmpty(trackInfo))
return false;
return true;
}
通过上文说的,如何不是在log界面点击的,我们先过滤掉。
然后过滤不是自定义log包装的日志!
/// <summary>
/// 需要过滤的cs文件
/// </summary>
private static string keyCS = "WDLog.cs";
DoubleClickLog方法内...
var trackInfo = GetStackTrace();
// 不是在Log界面点击的!
if (string.IsNullOrEmpty(trackInfo))
return false;
// 不是wdlog的输出
if (!trackInfo.Contains(keyCS))
return false;
...
然后我们拦截掉自定的输出,取出上一级调用堆栈的文件及其行号,然后通过unity打开就可以了!
/// <summary>
/// 匹配
/// </summary>
private static readonly Regex LogRegex = new Regex(@" \(at (.+)\:(\d+)\)\r?\n");
DoubleClickLog方法内...
// 不是wdlog的输出
if (!trackInfo.Contains(keyCS))
return false;
var match = LogRegex.Match(trackInfo);
if (!match.Success) return false;
match = match.NextMatch();
if (!match.Success) return false;
var file = match.Groups[1].Value;
var lineId = int.Parse(match.Groups[2].Value);
// 上一级目录
var projectRootPath = Path.GetFullPath(Path.Combine(Application.dataPath, "../"));
InternalEditorUtility.OpenFileAtLineExternal(
Path.Combine(projectRootPath, file),lineId);
return true;
到此已经完成了?
简单测试了一下,发现和网上其他人写的blog一样,完全覆盖了unity自身的单个文件跳转的问题,所有我们需要小小的修复一下!
补丁
经过一轮测试后,难发现,如果使用者是通过点击具体的栈信息跳转指定文件的,那么DoubleClickLog
中的行instanceId
就一定和WDLog
的id不一致!!
那么需要如何才能获取到WDLog
的instanceId呢?
- instanceId查找gameobject
unity提供了一个EditorUtility.InstanceIDToObject
的api可以找到点击的是哪个文件,所以只需要在打开的文件里识别一下是否是打开的log文件就能正确了
...
// 不是wdlog的输出
if (!trackInfo.Contain(keyCS))
return false;
var className =EditorUtilityInstanceIDToObjec(instanceId).name;
if (className != "WDLog")
{
return false;
}
...
测试了一下,完全正确!