unity console日志双击响应事件扩展

1 对于项目中一些比较长的日志,比如前后端交互协议具体数据等,这些日志内容可能会比较长,在unity控制面板上查看不是十分方便,我们可以对双击事件进行扩展,将日志保存到一个文本中,然后用系统默认的文本查看工具查看这个日志

2 项目中如果用到了lua,当我们在控制台输出lua文件的时候,能不能双击日志用我们的代码编辑器软件打开日志输出位置

console日志双击然后用记事本打开显示

我们先用 OnOpenAsset 特性,重写双击回调方法,这个方法返回false,会继续执行系统默认日志双击打开方法,返回true则会中断。

我们想要的效果是用记事本打开这个特殊日志,但是不需要跳转到对应的代码输出位置

首先我们先在lua里面把日志输出方法重写一下,在日志输出的时候,添加一个标签 "",当我们检测到双击的日志信息包含这个标签,则用记事本打开这一段日志(我们也可以把一些特殊的日志先在代码中缓存,在这个时候调用缓存读取那些特殊的日志进行输出显示)

lua 复制代码
function printLongLog(fmt, ...)
    local logText = string.format(fmt, ...)
    local debugInfo = debug.getinfo(2)
    local str = string.format("%s\n<open in file>", logText, 
                    debugInfo.short_src, debugInfo.currentline)
                    
    local logMessage = concatStrs(str, debug.traceback("", 2))
    UnityEngine.Debug.Log(logMessage)
end

然后再C#代码中监听这一段日志

csharp 复制代码
 [OnOpenAsset(0)]
    public static bool OnAsset(int instanceID, int line)
    {
        string assetPath = AssetDatabase.GetAssetPath(instanceID);
        string fileExtension = Path.GetExtension(assetPath);
         string stackTrace = GetStackTrace();
        if (string.IsNullOrEmpty(stackTrace))
        {
            return false;
        }

        if (stackTrace.Contains("<open in file>")) //对于一些比较长的日志,添加标签<open in file>,双击的时候再文本中打开
        {
            openLog(stackTrace);
            return true;
        }
         return false;
     }

获取console里面的日志

csharp 复制代码
private static string GetStackTrace()
    {
        var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
        var consoleWindowInstance = fieldInfo.GetValue(null);
        if (null != consoleWindowInstance && (object)EditorWindow.focusedWindow == consoleWindowInstance)
        {
            fieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
            string activeText = fieldInfo.GetValue(consoleWindowInstance).ToString();
            return activeText;
        }
        return "";
    }

用记事本打开日志文件,我们先把日志信息保存在本地,然后调用系统方法打开这个文件

这里用到了一个方法 System.Diagnostics.Process.Start(fileName),参数是文件的完整路径,调用系统默认方式打开改文件

csharp 复制代码
private static void openLog(string stackTrace)
    {
        string fileName = GetFileSaveName();
        string saveDirPath = Application.dataPath + "/../Debug";
        
        if (!Directory.Exists(saveDirPath))
        {
            Directory.CreateDirectory(saveDirPath);
        }
        string savePath = saveDirPath + "/" + fileName;
        File.WriteAllText(savePath, stackTrace);
        System.Diagnostics.Process.Start(savePath);
    }

    private static string GetFileSaveName()
    {
        DateTime currentTime = DateTime.Now;
        string customFormat = currentTime.ToString("yyyy_MM_dd_HH_mm_ss");
        string fileName = $"debug_{customFormat}.txt";
        return fileName;
    }

双击打开lua日志然后用lua编辑器定位到日志输出位置

对于lua输出的日志,我们为了便于打开可以用类似上面的方法重新封装一个lua日志输出方法,然后通关特殊标签的进行确定lua文件的名称和对应的行号。也可以进行拆分lua堆栈数据取到lua代码名称和对应堆栈。

添加标签方式的lua日志输出

lua 复制代码
function print(fmt, ...)
    local logText = string.format(fmt, ...)
    local debugInfo = debug.getinfo(2)
    local str = string.format("%s\n<filePath>%s</filePath><line>%s</line>", logText, 
                    debugInfo.short_src, debugInfo.currentline)
                    
    local logMessage = concatStrs(str, debug.traceback("", 2))
    UnityEngine.Debug.Log(logMessage)
end

获取文件名称和行号

csharp 复制代码
static bool OpenLua(string logText)
    {
        //获取lua文件路径
        Regex regex = new Regex(@"<filePath>.*<\/filePath>");
        Match match = regex.Match(logText);
        if (!match.Success)
        {
            return false;
        }
        string filePath = match.Groups[0].Value.Trim();
        int length = filePath.Length - 10 - 11; //去掉开头和结尾的字符串 <filePath>  </filePath>
        filePath = filePath.Substring(10, length);
        filePath = filePath.Replace(".", "/");
        if (!filePath.EndsWith(".lua"))
        {
            filePath = filePath + ".lua";
        }
        
        //获取日志行号
        Regex lineRegex = new Regex(@"<line>.*<\/line>");
        match = lineRegex.Match(logText);
        if (!match.Success)
        {
            return false;
        }
        string luaLineString = match.Groups[0].Value;
        luaLineString.Trim();
        length = luaLineString.Length - 6 - 7;
        luaLineString = luaLineString.Substring(6, length);
        int luaLine = int.Parse(luaLineString.Trim());

        return OpenFileAtLineExternal(filePath, luaLine);
    }

对于系统默认输出日志,我们通关拆分字符串方式获取

csharp 复制代码
 static bool OpenLuaDefault(string logText)
    {
        int index = logText.IndexOf("stack traceback:");
        string temp = logText.Substring(index, logText.Length - index);
        string[] arr = temp.Split(':');
        if (arr.Length < 3)
        {
            return false;
        }

        string filePath = arr[1].Trim();
        int luaLine = int.Parse(arr[2]);
        filePath = filePath + ".lua";
        return OpenFileAtLineExternal(filePath, luaLine);
    }

有了文件名和行号,我们就可以通关调用VScode或者Idea编辑器软件,打开对应的文件并定位到指定的行

csharp 复制代码
static void OpenFileWith(string fileName, int line)
    {
        string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo.FileName = editorPath;
        string projectRootPath = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        if (string.IsNullOrEmpty(projectRootPath))
        {
            SetLuaProjectRoot();
            return;
        }
        string procArgument = "";
        if (editorPath.IndexOf("idea") != -1) //idea
        {
            procArgument = string.Format("{0} --line {1} {2}", projectRootPath, line, fileName);
        }
        else if (editorPath.IndexOf("Code.exe") != -1) // VSCode
        {
            string filePath = Path.Combine(projectRootPath, fileName);
            procArgument = string.Format("-g {0}:{1}:0", filePath, line);
        }
        else
        {
            procArgument = string.Format("{0}:{1}:0", fileName, line);
        }
        proc.StartInfo.Arguments = procArgument;
        proc.Start(); 
    }

完整代码

lua 日志方法

lua 复制代码
function print(fmt, ...)
    local logText = string.format(fmt, ...)
    local debugInfo = debug.getinfo(2)
    local str = string.format("%s\n<filePath>%s</filePath><line>%s</line>", logText, 
                    debugInfo.short_src, debugInfo.currentline)
                    
    local logMessage = concatStrs(str, debug.traceback("", 2))
    UnityEngine.Debug.Log(logMessage)
end

function printLongLog(fmt, ...)
    local logText = string.format(fmt, ...)
    local debugInfo = debug.getinfo(2)
    local str = string.format("%s\n<open in file>", logText, 
                    debugInfo.short_src, debugInfo.currentline)
                    
    local logMessage = concatStrs(str, debug.traceback("", 2))
    UnityEngine.Debug.Log(logMessage)
end

C#方法

csharp 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

public class ConsoleTools : MonoBehaviour
{
    public const string EXTERNAL_EDITOR_PATH_KEY = "mTv8";
    public const string LUA_PROJECT_ROOT_FOLDER_PATH_KEY = "obUd";
    
    [OnOpenAsset(0)]
    public static bool OnAsset(int instanceID, int line)
    {
        string assetPath = AssetDatabase.GetAssetPath(instanceID);
        string fileExtension = Path.GetExtension(assetPath);
        // string fileName = Path.GetFileNameWithoutExtension(assetPath);
        if(fileExtension != ".cs" && fileExtension != ".lua")
        {
            return false; 
        }

        string stackTrace = GetStackTrace();
        if (string.IsNullOrEmpty(stackTrace))
        {
            return false;
        }

        if (stackTrace.Contains("<open in file>")) //对于一些比较长的日志,添加标签<open in file>,双击的时候再文本中打开
        {
            openLog(stackTrace);
            return true;
        }
        

        if (stackTrace.Contains("stack traceback:")) //lua输出堆栈信息,会自带 ""stack traceback:" 这描述,这里用来筛选lua日志
        {
            bool isOpenSuccess = false;
            if (stackTrace.Contains("<filePath>"))
            {
                isOpenSuccess = OpenLua(stackTrace);
            }
            else
            {
                isOpenSuccess = OpenLuaDefault(stackTrace);
            }
            return isOpenSuccess;
        }
        
        return false;
    }
    
    /// <summary>
    /// 通关日志堆栈获取日志信息
    /// </summary>
    /// <returns></returns>
    private static string GetStackTrace()
    {
        var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
        var consoleWindowInstance = fieldInfo.GetValue(null);
        if (null != consoleWindowInstance && (object)EditorWindow.focusedWindow == consoleWindowInstance)
        {
            fieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
            string activeText = fieldInfo.GetValue(consoleWindowInstance).ToString();
            return activeText;
        }
        return "";
    }

    /// <summary>
    /// 以文件个格式打开日志
    /// </summary>
    /// <param name="stackTrace"></param>
    private static void openLog(string stackTrace)
    {
        string fileName = GetFileSaveName();
        string saveDirPath = Application.dataPath + "/../Debug";
        
        if (!Directory.Exists(saveDirPath))
        {
            Directory.CreateDirectory(saveDirPath);
        }
        string savePath = saveDirPath + "/" + fileName;
        File.WriteAllText(savePath, stackTrace);
        System.Diagnostics.Process.Start(savePath);
    }

    private static string GetFileSaveName()
    {
        DateTime currentTime = DateTime.Now;
        string customFormat = currentTime.ToString("yyyy_MM_dd_HH_mm_ss");
        string fileName = $"debug_{customFormat}.txt";
        return fileName;
    }

    static void SetExternalEditorPath()
    {
        string path = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        path = EditorUtility.OpenFilePanel(
            "设置lua文件默认打开的编辑器软件路径(选择exe文件)",
            path,
            "exe");

        if (path != "")
        {
            EditorUserSettings.SetConfigValue(EXTERNAL_EDITOR_PATH_KEY, path);
            Debug.Log("Set Editor Path: " + path);
        }
    } 
    
    static void SetLuaProjectRoot()
    {
        string path = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        path = EditorUtility.OpenFolderPanel(
            "设置lua项目根目录位置",
            path,
            "");

        if (path != "")
        {
            EditorUserSettings.SetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY, path);
            Debug.Log("Set Editor Path: " + path);
        }
    }

    static bool OpenLua(string logText)
    {
        //获取lua文件路径
        Regex regex = new Regex(@"<filePath>.*<\/filePath>");
        Match match = regex.Match(logText);
        if (!match.Success)
        {
            return false;
        }
        string filePath = match.Groups[0].Value.Trim();
        int length = filePath.Length - 10 - 11; //去掉开头和结尾的字符串 <filePath>  </filePath>
        filePath = filePath.Substring(10, length);
        filePath = filePath.Replace(".", "/");
        if (!filePath.EndsWith(".lua"))
        {
            filePath = filePath + ".lua";
        }
        
        //获取日志行号
        Regex lineRegex = new Regex(@"<line>.*<\/line>");
        match = lineRegex.Match(logText);
        if (!match.Success)
        {
            return false;
        }
        string luaLineString = match.Groups[0].Value;
        luaLineString.Trim();
        length = luaLineString.Length - 6 - 7;
        luaLineString = luaLineString.Substring(6, length);
        int luaLine = int.Parse(luaLineString.Trim());

        return OpenFileAtLineExternal(filePath, luaLine);
    }
    /// <summary>
    /// 打开没有标签的日志对应的脚本
    /// </summary>
    /// <param name="logText"></param>
    /// <returns></returns>
    static bool OpenLuaDefault(string logText)
    {
        int index = logText.IndexOf("stack traceback:");
        string temp = logText.Substring(index, logText.Length - index);
        string[] arr = temp.Split(':');
        if (arr.Length < 3)
        {
            return false;
        }

        string filePath = arr[1].Trim();
        int luaLine = int.Parse(arr[2]);
        filePath = filePath + ".lua";
        return OpenFileAtLineExternal(filePath, luaLine);
    }
    
    /// <summary>
    /// 打开指定的文件
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="line">行号</param>
    /// <returns></returns>
    static bool OpenFileAtLineExternal(string fileName, int line)
    {
        string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        if (string.IsNullOrEmpty(editorPath) || !File.Exists(editorPath))
        {   // 没有path就弹出面板设置
            SetExternalEditorPath();
        }
        OpenFileWith(fileName, line);
        return true;
    }

    static void OpenFileWith(string fileName, int line)
    {
        string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo.FileName = editorPath;
        string projectRootPath = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        if (string.IsNullOrEmpty(projectRootPath))
        {
            SetLuaProjectRoot();
            return;
        }
        string procArgument = "";
        if (editorPath.IndexOf("idea") != -1) //idea
        {
            procArgument = string.Format("{0} --line {1} {2}", projectRootPath, line, fileName);
        }
        else if (editorPath.IndexOf("Code.exe") != -1) // VSCode
        {
            string filePath = Path.Combine(projectRootPath, fileName);
            procArgument = string.Format("-g {0}:{1}:0", filePath, line);
        }
        else
        {
            procArgument = string.Format("{0}:{1}:0", fileName, line);
        }
        proc.StartInfo.Arguments = procArgument;
        proc.Start(); 
    }
     
    
    
}
相关推荐
biyezuopinvip7 分钟前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
脸大是真的好~10 分钟前
EasyExcel的使用
java·excel
小宋102112 分钟前
Java 项目结构 vs Python 项目结构:如何快速搭一个可跑项目
java·开发语言·python
JavaGuide33 分钟前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
吃虫子的人39 分钟前
记录使用Arthas修改线上源码重新加载的一次过程
java·arthas
我的offer在哪里1 小时前
示例 Unity 项目结构(Playable Game Template)
unity·游戏引擎
figo10tf1 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva1 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
橙露1 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot