unity一键注释日志和反注释日志

开发背景:游戏中日志也是很大的开销,虽然有些日志不打印但是毕竟有字符串的开销,甚至有字符串拼接的开销,有些还有装箱和拆箱的开销,比如Debug.Log(1) 这种

因此需要注释掉,当然还需要提供反注释的功能,需要的时候能立马找回来。

也就是说我们只需要打包的时候处理即可。

1.先检测代码中是否存在不规范代码,类似if(a > 1) Debug.Log("11") 这种代码如果注释掉日志会引起业务逻辑问题需要先找出来,类似的不限于if else for foreach while这些 所以尽可能的要做这个检测,有这类问题直接报错。然后才可以进行后续流程

2.注释Debug.Log或者UnityEngine.Debug.Log,但是有一个问题就是注释之后再注释的问题,所以在调用这个接口前需要调用反注释然后调用注释,这样就可以保证注释是没有问题的。

3.需要提供反注释的代码,主要是配合2的功能

注意事项:大家写代码的过程中想打日志就打日志这个没有限制,打包的时候会自动处理日志。

如果是特别的日志可以打错误日志或者警告,或者自定义日志那种日志不去处理即可。

未来规划是用代码分析给日志加标签,平常开发还是按unity的习惯来我绝对不改变大家的unity标准,在做扩展的时候尽量做到神不知鬼不觉,不让大家有额外操作不让大家有压力。

接着上面扩展 :如下图所示这种else日志不被允许

那么如何进行csharp的语法分析呢 ,我们建一个c#的控制台程序,使用nuget安装3个包

 Microsoft.CodeAnalysis.CSharp 和 Microsoft.CodeAnalysis.CSharp.Workspaces  和Microsoft.CodeAnalysis

分析c#代码检测是否存在if else for while foreach 后直接接Debug.Log或者UnityEngine.Debug.Log完整控制台程序如下

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace cs_test
{
    internal class Program
    {
        public static long getCurrentTimeMillis()
        {
            long milliseconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond;
            return milliseconds;
        }
        static long firstTime = 0;
        const float LogWritetimeOutTime = 2f;
        static List<string> strList = new List<string>();
        static void SetTimer(float time = LogWritetimeOutTime)
        {
            System.Timers.Timer timer = new System.Timers.Timer();
            timer.Interval = time;
            timer.AutoReset = false;
            timer.Enabled = true;
            
            timer.Elapsed += (a, b) =>
            {
                using (var fs = new FileStream(logPath, FileMode.Append,FileAccess.Write,FileShare.Write))
                {
                    using (var sw = new StreamWriter(fs, Encoding.UTF8))
                    {
                        lock (strList)
                        {
                            foreach (var item in strList)
                            {
                                sw.WriteLine(item);
                            }
                            strList.Clear();
                        }
                    }
                }
            };
        }
        static void Log(string str,bool useFileSystem = true)
        {
            if(useFileSystem && isLogToFile)
            {
                lock (strList)
                {
                    strList.Add(str);
                }
                if (firstTime == 0)
                {
                    firstTime = getCurrentTimeMillis();
                    SetTimer(LogWritetimeOutTime);
                    return;
                }
                if (getCurrentTimeMillis() - firstTime > LogWritetimeOutTime)
                {
                    firstTime = 0;
                    SetTimer(LogWritetimeOutTime);
                }
            }
            else
            {
                Console.WriteLine(str);
            }
        }
        public class LogStatementWalker : CSharpSyntaxWalker
        {
            public List<Issue> Issues { get; } = new List<Issue>();
            public override void VisitIfStatement(IfStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                if (node.Else != null)
                {
                    CheckElseStatement(node.Else);
                }
                base.VisitIfStatement(node);
            }
            public override void VisitForStatement(ForStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitForStatement(node);
            }
            public override void VisitForEachStatement(ForEachStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitForEachStatement(node);
            }

            public override void VisitWhileStatement(WhileStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitWhileStatement(node);
            }

            private void CheckElseStatement(ElseClauseSyntax elseClause)
            {
                if (elseClause.Statement is IfStatementSyntax)
                {
                    // Skip checking if it's an "else if" statement
                    return;
                }
                CheckBlockStatement(elseClause.Statement, elseClause.GetLocation());
            }
            private void CheckBlockStatement(StatementSyntax statement, Location location)
            {
                if (statement is BlockSyntax)
                {
                    return;
                }
                var logStatements = statement.DescendantNodes()
                                             .OfType<InvocationExpressionSyntax>()
                                             .Where(inv => inv.Expression is MemberAccessExpressionSyntax memberAccess &&
                                                           (memberAccess.Name.ToString() == "Log" || memberAccess.Name.ToString() == "Debug.Log"))
                                             .ToList();
                foreach (var logStatement in logStatements)
                {
                    var lineSpan = logStatement.GetLocation().GetLineSpan();
                    Issues.Add(new Issue
                    {
                        LineNumber = lineSpan.StartLinePosition.Line + 1,
                        Message = $"Debug.Log or UnityEngine.Debug.Log found in a single-line {statement.Kind()} statement without braces."
                    });
                }
            }
        }
        public class Issue
        {
            public int LineNumber { get; set; }
            public string Message { get; set; }
        }
        static string unityRoot;
        static string logPath = "D:/tempLog.txt";
        static bool isLogToFile = true;
        static void Main(string[] args)
        {
            List<string> pathList = new List<string>();
            if (args == null || args.Length == 0)
            {
                var path = System.Environment.CurrentDirectory;
                path = path.Substring(0, path.IndexOf("tools"));
                path = path.Replace("\\", "/");
                args = new string[]
                {
                     path + "Assets/HotUpdate"
                };
            }
            unityRoot = args[0].Substring(0, args[0].IndexOf("Assets"));
            logPath = unityRoot + "tools/tempLog.txt";
            if(File.Exists(logPath))
            {
                File.Delete(logPath);
            }
            pathList.AddRange(args);
            isLogToFile = bool.Parse(pathList[pathList.Count - 1]);
            pathList.RemoveAt(pathList.Count - 1);
            List<string> allCsharpFiles = new List<string>();
            foreach(var path_item in pathList)
            {
                allCsharpFiles.AddRange(Directory.GetFiles(path_item, "*.cs", SearchOption.AllDirectories));
            }
            bool hasInsue = false;
            strList.Clear();
            foreach(var item in allCsharpFiles)
            {
                string sourceCode = File.ReadAllText(item);
                SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode);
                CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
                var diagnostics = tree.GetDiagnostics();
                if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
                {
                    Log("Syntax errors found in the source code.");
                    return;
                }
                var walker = new LogStatementWalker();
                walker.Visit(root);
                foreach (var issue in walker.Issues)
                {
                    hasInsue = true;
                    var itemName = item.Substring(item.IndexOf("Assets"));
                    Log($"请使用括号封装你的日志! at line {issue.LineNumber}: {itemName}");
                }
            }
            if(!hasInsue)
            {
                Log("未发现不规范代码");
            }
        }
        /// <summary>
        /// 检测判断if语句后面是否带花括号,由于在for循环中经常会遇到if不带花括号的情况,故这个检测暂时放弃 不然
        /// 会有非常多的代码提示 
        /// 这里的代码暂时封存 
        /// </summary>
        /// <param name="args"></param>
        static void Main2(string[] args)
        {

            if(File.Exists(logPath))
            {
                File.Delete(logPath);
            }
            File.Create(logPath).Dispose();
            string directoryPath = "D:\\qiangsheng_wx\\Assets\\HotUpdate"; // 替换为你的目录路径
            // 获取目录中的所有 .cs 文件
            string[] files = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories);
            foreach (string filePath in files)
            {
                Log($"Processing file: {filePath}");
                string code = File.ReadAllText(filePath);
                SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
                var root = tree.GetRoot();
                var walker = new IfElseSyntaxWalker();
                walker.Visit(root);
            }
            Console.ReadLine();
        }
        class IfElseSyntaxWalker : CSharpSyntaxWalker
        {
            public override void VisitIfStatement(IfStatementSyntax node)
            {
                base.VisitIfStatement(node);

                // 检查 if 语句是否有花括号
                if (node.Statement is BlockSyntax) { }
                else
                {
                    var str = $"警告: if 语句 at line {node.GetLocation().GetLineSpan().StartLinePosition.Line + 1} does not use 花括号";
                    Log(str);
                }
                // 检查 else 语句是否有花括号
                if (node.Else != null)
                {
                    if (node.Else.Statement is BlockSyntax) { }
                    else
                    {
                        var str = $"警告: else 语句 at line {node.Else.GetLocation().GetLineSpan().StartLinePosition.Line + 1} does not use 花括号";
                        Log(str);
                    }
                }
            }
        }
    }
}
控制台程序做完之后还需要对接到unity项目中,我是直接将微信api的代码分析回调日志直接写入了某个文件,让unity调用这个exe文件,你可以封装成bat将参数传给exe执行,unity那边读取文件将文本内容Debug.LogError输出即可。

上诉工作做完了之后就可以提供注释和反注释的代码接口了,将下述代码封装到你的编辑器工具类中或者某个编辑器类下的对象中 

 private const string LogPattern = @"(?s)(Debug\.Log\s*\(.*?\);|UnityEngine\.Debug\.Log\s*\(.*?\);)";
    private const string unLogPattern = @"(?s)/\*(\s*Debug\.Log\s*\(.*?\);\s*|UnityEngine\.Debug\.Log\s*\(.*?\);\s*)\*/";

    public static void ForbidLog(string filePath)
    {
        
        string fileContent = File.ReadAllText(filePath,Encoding.UTF8);

     // 正则表达式匹配 Debug.Log 调用及其相关代码
     string pattern = LogPattern;
        string replacement = @"/*$1*/";

     if(!Regex.IsMatch(fileContent, pattern))
     {
         return;
     }
        string newContent = Regex.Replace(fileContent, pattern, replacement);

     using (StreamWriter writer = new StreamWriter(filePath, false, new UTF8Encoding(false)))
     {
         writer.Write(newContent);
     }
 }

    public static void UnForbidLog(string filePath)
    {
        
        string fileContent = File.ReadAllText(filePath, Encoding.UTF8);

     // 正则表达式匹配被注释的 Debug.Log 调用及其相关代码
     string pattern = unLogPattern;
        string replacement = @"$1";

     if (!Regex.IsMatch(fileContent, pattern))
     {
         return;
     }

     string newContent = Regex.Replace(fileContent, pattern, replacement);

     using (StreamWriter writer = new StreamWriter(filePath, false, new UTF8Encoding(false)))
     {
         writer.Write(newContent);
     }
     
    }
    [MenuItem("Tools/日志/注释所有日志", priority = -1999)]
 public static void ReMoveLog()
 {
     
     var listFolder = new List<string>();
     listFolder.Add(System.Environment.CurrentDirectory + "/Assets/HotUpdate");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/SDKTool");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/DevWork");
     var allCsharpFiles = new List<string>();    
     foreach(string folder in listFolder)
     {
         allCsharpFiles.AddRange(Directory.GetFiles(folder, "*.cs",SearchOption.AllDirectories));
     }
     string[] filters = new string[]
     {
         "InitCtrl.cs",
         "Global.cs",
         "LoginPanel.cs",
         "LoginCode.cs",
         "ProductModel.cs",
     };
     foreach(string csFile in allCsharpFiles)
     {
         var filename = Path.GetFileName(csFile);
         if(filters.Contains(filename))
         {
             Debug.Log("跳过过滤文件" + csFile);
             continue;
         }
ForbidLog(csFile);
        }
        allCsharpFiles.Clear();
        listFolder.Clear();
        listFolder = null;
        allCsharpFiles = null;
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }
    [MenuItem("Tools/日志/反注释所有日志", priority = -1999)]
    public static void UnReMoveLog()
    {
        var listFolder = new List<string>();
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/HotUpdate");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/SDKTool");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/DevWork");
        var allCsharpFiles = new List<string>();
        foreach (string folder in listFolder)
        {
allCsharpFiles.AddRange(Directory.GetFiles(folder, "*.cs", SearchOption.AllDirectories));
        }
        foreach (string csFile in allCsharpFiles)
        {
UnForbidLog(csFile);
        }
     allCsharpFiles.Clear();
     listFolder.Clear();
     listFolder = null;
     allCsharpFiles = null;
     AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

运行截图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/436286.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通信工程学习:什么是MAC媒体接入控制

MAC&#xff1a;媒体接入控制 MAC&#xff08;Medium Access Control&#xff09;&#xff0c;即媒体接入控制&#xff0c;是计算机网络中数据链路层的一个重要组成部分&#xff0c;负责协调多个发送和接收站点对一个共享传输媒体的占用。以下是关于MAC的详细解释&#xff1a; …

闭源与开源嵌入模型比较以及提升语义搜索效果的技术探讨

上图为执行语义搜索前的聚类演示 &#xff0c;嵌入技术是自然语言处理的核心组成部分。虽然嵌入技术的应用范围广泛&#xff0c;但在检索应用中的语义搜索仍是其最常见的用途之一。 尽管知识图谱等可以提升检索的准确率和效率&#xff0c;但标准向量检索技术仍然具有其实用价值…

实战笔记:Vue2项目Webpack 3升级到Webpack 4的实操指南

在Web开发领域&#xff0c;保持技术的更新是非常重要的。随着前端构建工具的快速发展&#xff0c;Webpack已经更新到5.x版本&#xff0c;如果你正在使用Vue2项目&#xff0c;并且还在使用Webpack 3&#xff0c;那么是时候考虑升级一下Webpack了。我最近将我的Vue2项目从Webpack…

MicoZone-Maven

一、理论 Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供项目构建和依赖管理支持的工具。 通过Maven管理依赖的优势&#xff1a; 1、通过在pom.xml中指定jar包坐标即可自动从仓库下载依赖 2、如果jar包存在子依赖会自动下载子依赖包 3、如果jar包之间存在冲突…

【初阶数据结构】详解插入排序 希尔排序(内含排序的概念和意义)

文章目录 前言1. 排序的概念及其应用1.1 排序的概念1.2 排序的应用 2. 插入排序2.1 基本思想2.2 插入排序的代码实现2.3 插入排序算法总结 3. 希尔排序3.1 基本思想3.2 希尔排序的代码实现3.3 希尔排序的特征总结 前言 初级数据结构系列已经进入到了排序的部分了。相信大家听到…

TCP CUBIC 曲线对 BIC 折线的拟合

bic 旨在对 reno 改进&#xff0c;用二分逼近替换线性遍历逼近&#xff0c;时间规模从 O ( W m a x ) O(W_{max}) O(Wmax​) 下降到 O ( ln ⁡ W m a x ) O(\ln {W_{max}}) O(lnWmax​)&#xff0c;这是本质&#xff0c;而 cubic 可以看作对 bic 的 bugfix&#xff0c;解除了…

【Iceberg分析】调研Iceberg中表的原地演变

调研Iceberg中表的原地演变 文章目录 调研Iceberg中表的原地演变原生非分区表文件关系图表的原地演变之表schema演变新增字段new_column文件关系变化图为新增字段写入数据文件关系变化图删除新增字段文件关系变化图新增字段new_column2文件关系变化图删除数据文件关系变化图 原…

Spring MVC__入门

目录 一、SpringMVC简介1、什么是MVC2、什么是SpringMVC 二、Spring MVC实现原理2.1核心组件2.2工作流程 三、helloworld1、开发环境2、创建maven工程3、配置web.xml4、创建请求控制器5、创建springMVC的配置文件6、测试HelloWorld7、总结 一、SpringMVC简介 1、什么是MVC MV…

强化学习-python案例

强化学习是一种机器学习方法&#xff0c;旨在通过与环境的交互来学习最优策略。它的核心概念是智能体&#xff08;agent&#xff09;在环境中采取动作&#xff0c;从而获得奖励或惩罚。智能体的目标是最大化长期奖励&#xff0c;通过试错的方式不断改进其决策策略。 在强化学习…

Linux操作系统中MongoDB

1、什么是MongoDB 1、非关系型数据库 NoSQL&#xff0c;泛指非关系型的数据库。随着互联网web2.0网站的兴起&#xff0c;传统的关系数据库在处理web2.0网站&#xff0c;特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心&#xff0c;出现了很多难以克服的问…

sysbench 命令:跨平台的基准测试工具

一、命令简介 sysbench 是一个跨平台的基准测试工具&#xff0c;用于评估系统性能&#xff0c;包括 CPU、内存、文件 I/O、数据库等性能。 ‍ 比较同类测试工具 bench.sh 在上文 bench.sh&#xff1a;Linux 服务器基准测试中介绍了 bench.sh 一键测试脚本&#xff0c;它对…

曲线图异常波形检测系统源码分享

曲线图异常波形检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

华为OD机试 - 最长元音子串的长度(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

Redis入门第三步:Redis事务处理

欢迎继续跟随《Redis新手指南&#xff1a;从入门到精通》专栏的步伐&#xff01;在本文中&#xff0c;我们将探讨Redis的事务处理机制。了解如何使用事务来保证一系列操作的原子性和一致性&#xff0c;这对于构建可靠的应用程序至关重要 1 什么是Redis事务&#x1f340; ​ R…

解锁数据宝藏:AI驱动搜索工具,让非结构化数据“说话

哈哈,说起这个 AI 搜索演示啊,那可真是个有意思的话题!非结构化数据,这家伙虽然难搞,但价值却是杠杠的。今天呢,咱就好好聊聊怎么借助 Fivetran 和 Milvus,快速搭建一个 AI 驱动的搜索工具,让企业能从那些乱七八糟的数据里淘到金子! 一、非结构化数据的挑战与机遇 首…

堆【数据结构C语言版】【 详解】

目录-笔记整理 一、思考二、堆概念与性质三、堆的构建、删除、添加1. 构建2. 删除3. 添加 四、复杂度分析4.1 时间复杂度4.2 空间复杂度 五、总结 一、思考 设计一种数据结构&#xff0c;来存放整数&#xff0c;要求三个接口&#xff1a; 1&#xff09;获取序列中的最值&#…

Thinkphp/Laravel旅游景区预约系统的设计与实现

目录 技术栈和环境说明具体实现截图设计思路关键技术课题的重点和难点&#xff1a;框架介绍数据访问方式PHP核心代码部分展示代码目录结构解析系统测试详细视频演示源码获取 技术栈和环境说明 采用PHP语言开发&#xff0c;开发环境为phpstudy 开发工具notepad并使用MYSQL数据库…

景联文科技入选《2024中国AI大模型产业图谱2.0版》数据集代表厂商

近日&#xff0c;大数据产业领域头部媒体数据猿携手上海大数据联盟联合发布了备受瞩目的《2024中国AI大模型产业图谱2.0版》。以大数据与AI为代表的智能技术为主要视角&#xff0c;聚焦全产业链&#xff0c;为业内提供更为专业直观的行业指导。 景联文科技凭借高质量数据集&…

基于大数据的学生体质健康信息系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Vue Mini基于 Vue 3 的小程序框架

新的小程序框架 https://vuemini.org/ Vue Mini 是一个基于 Vue 3 的小程序框架&#xff0c;它允许开发者利用 Vue 3 的强大功能来构建微信小程序。Vue Mini 的核心优势在于它的响应式系统和组合式 API&#xff0c;这些特性让开发者能够以一种更声明式、更高效的方式来编写和…