优化Unity日志系统的消耗及在ILRuntime模式下双击能跳转到对应的文件行号

方案:

Unity的日志控制:
日志系统打开,但是只打印错误日志。这样其他级别的日志就不会有打印消耗。

UnityEngine.Debug.unityLogger.logEnabled = true;
Debug.unityLogger.filterLogType = LogType.Error;

但是还是有字符串拼接的消耗。
Conditional属性是一个C#特性,它允许你根据预处理器指令的定义来有条件地执行方法。例如下面的代码:

[Conditional("OPEN_MAIN_LOG_LOGWARNING")]
public static void Log(object message,Object context)
{
    UnityEngine.Debug.Log(message,context);
}

如果没有OPEN_MAIN_LOG_LOGWARNING宏,编译的时候,所有调用Log的方法都会从Dll里去除。因此就没有了字符串拼接的消耗。

检查发现的问题:

  1. Unity Editor下的Debug函数不应该被替换
  2. 给ProjectStttings.asset文件修改宏的时候,应该全平台替换。
  3. 双击日志,非ILRuntime下和ILRuntime都能跳转到对应代码的行号。注意如果ILRuntime下加了
    DISABLE_ILRUNTIME_DEBUG就不会有行号的打印。
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditorInternal;
using UnityEngine;

/// <summary>
/// 日志重定向相关的实用函数。
/// </summary>
internal static class LogRedirection
{
    private static readonly Regex LogRegex = new Regex(@" \(at (.+)\:(\d+)\)\r?\n");

    [OnOpenAsset(0)]
    private static bool OnOpenAsset(int instanceId, int line)
    {
        var scriptComponent = EditorUtility.InstanceIDToObject(instanceId);
        string selectedStackTrace = GetSelectedStackTrace();
        if (string.IsNullOrEmpty(selectedStackTrace))
        {
            return false;
        }

        //是ILRuntime下日志
        if (selectedStackTrace.Contains("ILRuntime StackTrace"))
        {
            //如果跳转的不是我们的GalaDebugger,证明打开的不是第一行的url,让系统跳转
            if (!scriptComponent.name.Contains("GalaDebugger"))
            {
                return false;
            }

            Match match = LogRegex.Match(selectedStackTrace);
            if (!match.Success)
            {
                return false;
            }

            InternalEditorUtility.OpenFileAtLineExternal(match.Groups[1].Value, int.Parse(match.Groups[2].Value));
            return true;
        }
        else
        {
            //操作本质就是
            //1,拿到堆栈 取得堆栈内容判断 找出正确的打开行
            //2,每一个Groups实际上就是一行 
            //3, Groups[1]取得正好是代码路径
            //4 当然自己根据实际的封装路径来搜寻正确的路径
            //5,因为目前日志内容带有url属性,可以直接跳转了,因此只要跳转的路径不是默认的路径(也就是第一个路径日志),都按其系统跳转
            if (!selectedStackTrace.Contains("GalaDebugger:"))
            {
                return false;
            }

            Match match = LogRegex.Match(selectedStackTrace);
            if (!match.Success)
            {
                return false;
            }

            //此时找到了第一个,如果跳转的不是第一个,证明我们手动选中了其它url  ---注意这里存在一个缺陷,如果选中的跳转url正好和默认路径的line一致,则会仍然跳转日志打印处
            //理论上可以判断脚本的instanceId
            int targetLine = int.Parse(match.Groups[2].Value);
            if (targetLine != line)
            {
                return false;
            }

            if (!match.Groups[1].Value.Contains("GalaDebugger.cs"))
            {
                return false;
            }

            match = match.NextMatch();
            if (!match.Success)
            {
                return false;
            }

            if (match.Groups[1].Value.Contains("GalaDebugger.cs"))
            {
                match = match.NextMatch();
                if (!match.Success)
                {
                    return false;
                }
            }

            InternalEditorUtility.OpenFileAtLineExternal(
                Path.Combine(Application.dataPath, match.Groups[1].Value.Substring(7)),
                int.Parse(match.Groups[2].Value));
            return true;
        }
    }

    private static string GetSelectedStackTrace()
    {
        Assembly editorWindowAssembly = typeof(EditorWindow).Assembly;
        if (editorWindowAssembly == null)
        {
            return null;
        }

        System.Type consoleWindowType = editorWindowAssembly.GetType("UnityEditor.ConsoleWindow");
        if (consoleWindowType == null)
        {
            return null;
        }

        FieldInfo consoleWindowFieldInfo =
            consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
        if (consoleWindowFieldInfo == null)
        {
            return null;
        }

        EditorWindow consoleWindow = consoleWindowFieldInfo.GetValue(null) as EditorWindow;
        if (consoleWindow == null)
        {
            return null;
        }

        if (consoleWindow != EditorWindow.focusedWindow)
        {
            return null;
        }

        //目的就是要拿到当前选中的文本
        FieldInfo activeTextFieldInfo =
            consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
        if (activeTextFieldInfo == null)
        {
            return null;
        }

        return (string)activeTextFieldInfo.GetValue(consoleWindow);
    }
}
  1. 打底包和打热更的时候都应该加入Jenkins参数控制宏。
  2. 增加了新的日志系统,需要显示ILRuntime的堆栈,需要加上CLR重定向
unsafe static void CLRRedirection(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
        {
            Type debugType = typeof(DebugEX);
            var logMethod = debugType.GetMethod("Log", new[] { typeof(object) });
            appdomain.RegisterCLRMethodRedirection(logMethod, Log);
            var logWarningMethod = debugType.GetMethod("LogWarning", new[] { typeof(object) });
            appdomain.RegisterCLRMethodRedirection(logWarningMethod, LogWarning);
            var logErrorMethod = debugType.GetMethod("LogError", new[] { typeof(object) });
            appdomain.RegisterCLRMethodRedirection(logErrorMethod, LogError);
        }

        /// <summary>
        /// DebugEX.Log 实现
        /// </summary>
        /// <param name="__intp"></param>
        /// <param name="__esp"></param>
        /// <param name="__mStack"></param>
        /// <param name="__method"></param>
        /// <param name="isNewObj"></param>
        /// <returns></returns>
        unsafe static StackObject* Log(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
            CLRMethod __method, bool isNewObj)
        {
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* ptr_of_this_method;
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);

            object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
            __intp.Free(ptr_of_this_method);

            var stacktrace = __domain.DebugService.GetStackTrace(__intp);
            DebugEX.Log(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
            return __ret;
        }

        /// <summary>
        /// DebugEX.LogError 实现
        /// </summary>
        /// <param name="__intp"></param>
        /// <param name="__esp"></param>
        /// <param name="__mStack"></param>
        /// <param name="__method"></param>
        /// <param name="isNewObj"></param>
        /// <returns></returns>
        unsafe static StackObject* LogError(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
            CLRMethod __method, bool isNewObj)
        {
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* ptr_of_this_method;
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);

            object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
            __intp.Free(ptr_of_this_method);

            var stacktrace = __domain.DebugService.GetStackTrace(__intp);
            DebugEX.LogError(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
            return __ret;
        }

        /// <summary>
        /// DebugEX.LogWarning 实现
        /// </summary>
        /// <param name="__intp"></param>
        /// <param name="__esp"></param>
        /// <param name="__mStack"></param>
        /// <param name="__method"></param>
        /// <param name="isNewObj"></param>
        /// <returns></returns>
        unsafe static StackObject* LogWarning(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
            CLRMethod __method, bool isNewObj)
        {
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* ptr_of_this_method;
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);

            object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
            __intp.Free(ptr_of_this_method);

            var stacktrace = __domain.DebugService.GetStackTrace(__intp);
            DebugEX.LogWarning(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
            return __ret;
        }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KindSuper_liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值