1. 程式人生 > >Unity日誌工具——封裝,跳轉

Unity日誌工具——封裝,跳轉

//轉自 By D.S.Qiu

應該所有的團隊都會自己封裝日誌工具,除非引擎已經集成了,在Unity也不例外,當時之前的同事封裝了一個有一個很大不爽的地方是:從Unity ConsoleWindow 雙擊日誌跳轉到程式碼總是跳轉到封裝類中的函式,而不能直接跳轉到呼叫封裝類被呼叫的地方。

       切好在準備新專案,我把原來不夠優良的地方都進行了改進直至儘可能的完美。之前一直就知道1.利用反射可以獲取Unity的private FieldInfo和 MethodInfo 可以做很多事情,2.可以利用Unity提供的api調整到指定的程式碼中去,3.Unity提供跳轉回調的機制。算是理論只是具備了,今天來公司就把這個給寫出來了,當然還對LogLevel和StackFrame資訊進行了優化(之前的有點醜,是13年一個前前同事寫的)。

其實是很簡單的,直接說下思路吧(Unity5.3):

       1.記錄通過封裝日誌工具的函式呼叫棧資訊 StackFrame。

       2.新增UnityEditor.Callbacks.OnOpenAssetAttribute(0)的回撥方法,處理從ConsoleWindow雙擊跳轉

       3.利用反射獲取ConsoleWindow 的 ListeViewState 的 row(當前雙擊的行)和總行數

       4.利用3得到行數反射獲取LogEntry資訊進行匹配獲得對應StackFrame

       5.呼叫AssetDatabase.OpenAsset()即可。

 更新到Unity5.3發現,他提供Logger這個類,本來還以為可以實現這些功能,不過簡單測試下來發現是不行的,我就還不清楚Unity構造一個Logger類是幹嘛的,搞得我把下面的類名改成LoggerUtility。

貼下完整的程式碼:

C#程式碼  收藏程式碼
  1. /* 
  2.  * File: Assets/Scripts/Game/Utility/LoggerUtility.cs 
  3.  * Project: **** 
  4.  * Company: Lucky 
  5.  * Code Porter: D.S.Qiu  
  6.  * Create Date: 10/9/2015 10:11:53 PM 
  7.  */  
  8. using System;  
  9. using System.Collections.Generic;  
  10. using System.Diagnostics;  
  11. using System.IO;  
  12. using System.Text;  
  13. #if UNITY_EDITOR  
  14. using System.Reflection;  
  15. using UnityEditor;  
  16. using UnityEditor.Callbacks;  
  17. #endif  
  18. using UnityEngine;  
  19. using Debug = UnityEngine.Debug;  
  20. namespace Utility  
  21. {  
  22.     public class LogUtility  
  23.     {  
  24.         public enum LogLevel : byte  
  25.         {  
  26.             None = 0,  
  27.             Exception = 1,  
  28.             Error = 2,  
  29.             Warning = 3,  
  30.             Info = 4,  
  31.         }  
  32.         public static LogLevel logLevel = LogLevel.Info;  
  33.         public static string infoColor = "#909090";  
  34.         public static string warningColor = "orange";  
  35.         public static string errorColor = "red";  
  36.         public static void LogBreak(object message, UnityEngine.Object sender = null)  
  37.         {  
  38.             LogInfo(message, sender);  
  39.             Debug.Break();  
  40.         }  
  41.         public static void LogFormat(string format, UnityEngine.Object sender, params object[] message)  
  42.         {  
  43.             if (logLevel >= LogLevel.Info)  
  44.                 LogLevelFormat(LogLevel.Info, string.Format(format, message), sender);  
  45.         }  
  46.         public static void LogFormat(string format, params object[] message)  
  47.         {  
  48.             if (logLevel >= LogLevel.Info)  
  49.                 LogLevelFormat(LogLevel.Info, string.Format(format, message), null);  
  50.         }  
  51.         public static void LogInfo(object message, UnityEngine.Object sender = null)  
  52.         {  
  53.             if(logLevel >= LogLevel.Info)  
  54.                 LogLevelFormat(LogLevel.Info,message,sender);  
  55.         }  
  56.         public static void LogWarning(object message, UnityEngine.Object sender = null)  
  57.         {  
  58.             if (logLevel >= LogLevel.Warning)  
  59.                 LogLevelFormat(LogLevel.Warning, message,  sender);  
  60.         }  
  61.         public static void LogError(object message, UnityEngine.Object sender = null)  
  62.         {  
  63.             if (logLevel >= LogLevel.Error)  
  64.             {  
  65.                 LogLevelFormat(LogLevel.Error, message, sender);  
  66.             }  
  67.         }  
  68.         public static void LogException(Exception exption, UnityEngine.Object sender = null)  
  69.         {  
  70.             if (logLevel >= LogLevel.Exception)  
  71.             {  
  72.                 LogLevelFormat(LogLevel.Exception, exption, sender);  
  73.             }  
  74.         }  
  75.         private static void LogLevelFormat(LogLevel level, object message, UnityEngine.Object sender)  
  76.         {  
  77.             string levelFormat =  level.ToString().ToUpper();  
  78.             StackTrace stackTrace = new StackTrace(true);  
  79.             var stackFrame = stackTrace.GetFrame(2);  
  80. #if UNITY_EDITOR  
  81.             s_LogStackFrameList.Add(stackFrame);  
  82. #endif  
  83.             string stackMessageFormat = Path.GetFileName(stackFrame.GetFileName()) + ":" + stackFrame.GetMethod().Name + "():at line " + stackFrame.GetFileLineNumber();  
  84.             string timeFormat = "Frame:" + Time.frameCount + "," + DateTime.Now.Millisecond + "ms";  
  85.             string objectName = string.Empty;  
  86.             string colorFormat = infoColor;  
  87.             if (level == LogLevel.Warning)  
  88.                 colorFormat = warningColor;  
  89.             else if (level == LogLevel.Error)  
  90.                 colorFormat = errorColor;  
  91.             StringBuilder sb = new StringBuilder();  
  92.             sb.AppendFormat("<color={3}>[{0}][{4}][{1}]{2}</color>", levelFormat, timeFormat, message, colorFormat, stackMessageFormat);  
  93.             Debug.Log(sb,sender);  
  94.         }  
  95. #if UNITY_EDITOR  
  96.         private static int s_InstanceID;  
  97.         private static int s_Line = 104;  
  98.         private static List<StackFrame> s_LogStackFrameList = new List<StackFrame>();  
  99.         //ConsoleWindow  
  100.         private static object s_ConsoleWindow;  
  101.         private static object s_LogListView;  
  102.         private static FieldInfo s_LogListViewTotalRows;  
  103.         private static FieldInfo s_LogListViewCurrentRow;  
  104.         //LogEntry  
  105.         private static MethodInfo s_LogEntriesGetEntry;  
  106.         private static object s_LogEntry;  
  107.         //instanceId 非UnityEngine.Object的執行時 InstanceID 為零所以只能用 LogEntry.Condition 判斷  
  108.         private static FieldInfo s_LogEntryInstanceId;  
  109.         private static FieldInfo s_LogEntryLine;  
  110.         private static FieldInfo s_LogEntryCondition;  
  111.         static LogUtility()  
  112.         {  
  113.             s_InstanceID = AssetDatabase.LoadAssetAtPath<MonoScript>("Assets/Scripts/Game/Utility/LoggerUtility.cs").GetInstanceID();  
  114.             s_LogStackFrameList.Clear();  
  115.             GetConsoleWindowListView();  
  116.         }  
  117.         private static void GetConsoleWindowListView()  
  118.         {  
  119.             if (s_LogListView == null)  
  120.             {  
  121.                 Assembly unityEditorAssembly = Assembly.GetAssembly(typeof(EditorWindow));  
  122.                 Type consoleWindowType = unityEditorAssembly.GetType("UnityEditor.ConsoleWindow");  
  123.                 FieldInfo fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);  
  124.                 s_ConsoleWindow = fieldInfo.GetValue(null);  
  125.                 FieldInfo listViewFieldInfo = consoleWindowType.GetField("m_ListView", BindingFlags.Instance | BindingFlags.NonPublic);  
  126.                 s_LogListView = listViewFieldInfo.GetValue(s_ConsoleWindow);  
  127.                 s_LogListViewTotalRows = listViewFieldInfo.FieldType.GetField("totalRows", BindingFlags.Instance | BindingFlags.Public);  
  128.                 s_LogListViewCurrentRow = listViewFieldInfo.FieldType.GetField("row", BindingFlags.Instance | BindingFlags.Public);  
  129.                 //LogEntries  
  130.                 Type logEntriesType = unityEditorAssembly.GetType("UnityEditorInternal.LogEntries");  
  131.                 s_LogEntriesGetEntry = logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public);  
  132.                 Type logEntryType = unityEditorAssembly.GetType("UnityEditorInternal.LogEntry");  
  133.                 s_LogEntry = Activator.CreateInstance(logEntryType);  
  134.                 s_LogEntryInstanceId = logEntryType.GetField("instanceID", BindingFlags.Instance | BindingFlags.Public);  
  135.                 s_LogEntryLine = logEntryType.GetField("line", BindingFlags.Instance | BindingFlags.Public);  
  136.                 s_LogEntryCondition = logEntryType.GetField("condition", BindingFlags.Instance | BindingFlags.Public);  
  137.             }  
  138.         }  
  139.         private static StackFrame GetListViewRowCount()  
  140.         {  
  141.             GetConsoleWindowListView();  
  142.             if (s_LogListView == null)  
  143.                 return null;  
  144.             else  
  145.             {  
  146.                 int totalRows = (int)s_LogListViewTotalRows.GetValue(s_LogListView);  
  147.                 int row = (int)s_LogListViewCurrentRow.GetValue(s_LogListView);  
  148.                 int logByThisClassCount = 0;  
  149.                 for (int i = totalRows - 1; i >= row; i--)  
  150.                 {  
  151.                     s_LogEntriesGetEntry.Invoke(nullnew object[] { i, s_LogEntry });  
  152.                     string condition = s_LogEntryCondition.GetValue(s_LogEntry) as string;  
  153.                     //判斷是否是由LoggerUtility列印的日誌  
  154.                     if (condition.Contains("][") && condition.Contains("Frame"))  
  155.                         logByThisClassCount++;  
  156.                 }  
  157.                 //同步日誌列表,ConsoleWindow 點選Clear 會清理  
  158.                 while (s_LogStackFrameList.Count > totalRows)  
  159.                     s_LogStackFrameList.RemoveAt(0);  
  160.                 if (s_LogStackFrameList.Count >= logByThisClassCount)  
  161.                     return s_LogStackFrameList[s_LogStackFrameList.Count - logByThisClassCount];  
  162.                 return null;  
  163.             }  
  164.         }  
  165.         [UnityEditor.Callbacks.OnOpenAssetAttribute(0)]  
  166.         public static bool OnOpenAsset(int instanceID, int line)  
  167.         {  
  168.             if (instanceID == s_InstanceID && s_Line == line)  
  169.             {  
  170.                 var stackFrame = GetListViewRowCount();  
  171.                 if (stackFrame != null)  
  172.                 {  
  173.                     string fileName = stackFrame.GetFileName();  
  174.                     string fileAssetPath = fileName.Substring(fileName.IndexOf("Assets"));  
  175.                     AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath<MonoScript>(fileAssetPath), stackFrame.GetFileLineNumber());  
  176.                     return true;  
  177.                 }  
  178.             }  
  179. 相關推薦

    Unity日誌工具——封裝

    //轉自 By D.S.Qiu 應該所有的團隊都會自己封裝日誌工具,除非引擎已經集成了,在Unity也不例外,當時之前的同事封裝了一個有一個很大不爽的地方是:從Unity ConsoleWindow 雙擊日誌跳轉到程式碼總是跳轉到封裝類中的函式,而不能直接跳轉到呼叫封裝

    4.13 apache用戶認證和訪問日誌

    用戶認證 跳轉 訪問日誌 Apache用戶認證 有的網站在訪問的時候需要我們輸入賬戶名和密碼,這樣做的好處是增加了安全性,但是用戶體驗會很差。但是在我們在工作中還需要在一些重要的地方做一些安全認證。 首先我們編輯虛擬主機的配置文件 vim /usr/local/apache2.4/conf/ext

    Unity 到應用商店評價app到Facebook某個介面判斷手機是否安裝某個APP

    private void OnGUI() { if (GUI.Button(new Rect(500, 500, 300, 150), "谷歌商店")) { openAPPinMarket(Applica

    IdentityServer4 登錄成功後到原來頁面

    localhost token IdentityServer4 登錄成功後,默認會跳轉到Config.Client配置的RedirectUris地址http://localhost:5003/callback.html,用於獲取 Token,比如跳轉後的地址:http://localhost:5003

    房上的貓:for循環語句與循環結構語句進階

    必須 特點 分享 com 變量賦值 修改 表達式 判斷 條件判斷 一.for循環 1.定義: for循環語句的主要作用是反復執行一段代碼,直到滿足一定條件為止 2.組成部分: (1)初始部分:設置循環的初始狀態 (2)循環體:重復執行的代碼 (3)叠代部分:下一次循

    導出excel時新空白頁不要怎麽改

    頁面 iframe open() color one bsp pan nbsp 需要 導出excel的時候,偶爾會出現跳轉到一個新頁面再導出exceljs中用window.open()做跳轉 不想讓它跳轉到新頁面,需要加一個隱藏的iframe <iframe nam

    通過js中的useragrent來判斷設備是pc端還是移動端不同的地址

    lenovo err agent indexof pad ren phi mobile 手機 if(/AppleWebKit.*Mobile/i.test(navigator.userAgent) || (/MIDP|SymbianOS|NOKIA|SAMSUNG|L

    根據訪客操作系統到不同鏈接

    indexof || gpo als cati orm linu log navi 根據訪客操作系統,區分PC、wap <script> var system ={ win : false, mac : false, xll : false }; var p

    js或jQuery中 郵箱的問題到指定郵箱(通過layui的ifram實現)

    按鈕 字符 新浪郵箱 實現 分享 顯示 最小化 span jquer 對剛做的東西記個筆記 如果遇到同樣問題解決起來又問題的歡迎留言 var emailtext = $("#TextBoxEmail").val();//獲得要截取的值

    laravel5.4 前後臺未登陸到各自的頁面

    post eight res token fault number ssi eset www https://www.imooc.com/wenda/detail/378208?t=266634 laravel我做了前後臺登陸,後臺未登錄跳轉到前臺登陸頁面了。 我想讓後臺未

    修改密碼成功後倒計時三秒後到登錄頁面

    ESS -c win interval 將在 window com clas .html 簡單描述:需求上說,修改密碼完成之後,要有個倒計時3秒,倒計時完成之後,才返回到登錄頁面,而不是修改完密碼,立即返回到登錄的頁面 代碼: //html代碼 <div class

    ci多表查詢訪問方式

    多表查詢: $data['upvideos'] = $this->db->from('upload_video')->join('vcat', 'vcat.catid = upload_video.catid')->order_by('createtime', 'DESC')-

    點選檔案到wps瀏覽

    當檔案下載完畢之後,需要進行瀏覽,有兩種方式: 1:使用TBS,即騰訊瀏覽服務,點選開啟連結 2:使用wps,由於專案受制於網路,故使用第二種方式實現,程式碼如下 public class WpsFileUtils { private static boolean isInstall

    jquery點擊一組按鈕中的一個至對應頁面處理策略。(如點擊訂單列表中的一個訂單至該訂單的詳情)

    跳轉 () 列表 操作 進行 斷點 cat 訂單 一個 將改組按鈕的數據設置一個相同的屬性(如class),然後每個按鈕設置不同的id 當用戶點擊屬性為class的按鈕,根據id屬性來判斷點擊的是哪個按鈕,然後進行相關操作。 代碼示例: <script> $("

    UI_多選框

    //跳轉 toimageview.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 彈框 Toast.makeText(MainActivity.t

    hexo next主題 站內搜尋出現異常無法正常時出現異常

    主要看看跳轉後的url是什麼,如果url異常,就需要在站點配置檔案(注意不是主題配置檔案)下面看看你的url和永久連結設定的是否正確。如下所示: # URL ## If your site is put in a subdirectory, set url as 'http://yo

    關於百度推送點選通知的問題(Android)

          今天在跟同事測試百度推送,之前一直困擾我的一個問題得到了有效解決,挺高興的,所以記錄一下,同時告誡自己,遇到問題,解決解決再解決,一定能夠解決的!      切入正題,百度推送的demo跟我們的app有些不一樣

    Android 應用在後臺時 Activity 會自動切換應用至前臺

    本部落格 demo 見:demo。 平常用手機的時候經常碰到這種情況,用首屏廣告舉個栗子~很多應用都會有首屏廣告 activity A,假設此應用是 app C,如果此時要使用別的應用,就會使得 app C 在後臺執行。可是當 

    vue 單頁應用點擊某個鏈接到新頁面的方式

    subst ams out 詳情 targe god str blank attribute <router-link class="goDetail" :to="{name: ‘detail‘,params: {id:item.id}}" target = _bl

    Android 啟動白屏黑屏以及冷啟動優化

    一,白屏 現象:啟動app,白屏一段時間後才出現歡迎頁 解決: 1,新增style <style name="AppTheme.Launcher"> <item name="android:windowDisablePreview">true<