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();
    }

运行截图:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring1

1.Spring系统架构图 (1)核心层 Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块 (2)AOP层 AOP:面向切面编程,它依赖核心层容器,目的是==在不改变原有代码的前提下对其进行功能增强== Aspects:AOP是思想,Aspects是对AOP思想的具体实现 (3)数据…

C语言进阶版第14课—内存函数

文章目录 1. memcpy函数的使用和模拟实现1.1 memcpy函数的使用1.2 模拟实现memcpy函数 2. memmove函数的使用和模拟实现2.1 memmove函数的使用2.2 memmove函数的模拟实现 3. memset函数4. memcmp函数 1. memcpy函数的使用和模拟实现 1.1 memcpy函数的使用 memcpy函数的原形voi…

虚商目前有哪些业务痛点?

虚拟运营商当前面临的业务痛点主要集中在市场竞争、运营成本、技术依赖、用户体验及政策监管等方面。 一、市场竞争激烈 1、竞争者数量增加: 随着市场准入门槛的降低&#xff0c;越来越多的企业进入虚拟运营商市场导致市场竟争日益激烈。为了争夺市场份额&#xff0c;企业不得不…

SRC漏洞挖掘 | 针对Spring-Boot 框架漏洞的初探

&#x1f497;想加内部圈子&#xff0c;请联系我&#xff01; &#x1f497;文章交流&#xff0c;请联系我&#xff01;&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 一个想当文人的黑客 &#xff0c;很高兴认识大家~ ✨主…

kafka分区和副本的关系?

概念来一波 比如一个topic的消息存放在两个分区中&#xff0c;分区1和分区2.每个分区都有自己的一个副本。即比如分区1有副本1/副本2/副本3&#xff0c;分区2也有分区2的副本1/副本2/副本3。一个节点上的一个topic的可以由多个分区存放&#xff0c;但是每个分区的leader副本会尽…

Spring Boot技术在足球青训管理中的创新应用

3 系统分析 3.1 可行性分析 可行性分析是该平台系统进行投入开发的基础第一步&#xff0c;必须对其进行可行性分析才能够降低不必要的需要从而使资源合理利用&#xff0c;更具有性价比和降低成本&#xff0c;同时也是系统平台的成功的未雨绸缪的一步。 3.1.1 技术可行性 技术可…

C#测试调用Ghostscript.NET浏览PDF文件

Ghostscript.NET是针对Ghostscript的C#封装库&#xff0c;支持解析PostScript语言、操作PDF文件等。使用Ghostscript.NET的GhostscriptViewer 模块可以以图片形式查看PDF文档。本文学习并测试调用Ghostscript.NET模块打开及浏览PDF文件的基本用法。   Ghostscript.NET目前主要…

Mac优化清理工具CleanMyMac X 4.15.6 for mac中文版

CleanMyMac X 4.15.6 for mac中文版下载是一款功能更加强大的系统优化清理工具&#xff0c;软件只需两个简单步骤就可以把系统里那些乱七八糟的无用文件统统清理掉&#xff0c;节省宝贵的磁盘空间。CleanMyMac X 4.15.6 for mac 软件与最新macOS系统更加兼容&#xff0c;流畅地…

24年九月份生活随笔

九月份最后一天&#xff0c;烈士纪念日。 上午看了一会儿直播&#xff0c;庄重的仪式&#xff0c;铭记先辈为新中国抛头颅洒热血&#xff0c;当今盛世&#xff0c;如您所愿。 郑州马拉松官方通告&#xff0c;今天十点公布直通&#xff0c;中签&#xff0c;候补结果。 看完直…

①无需编程 独立通道 Modbus主站EtherNet/IP转ModbusRTU/ASCII工业EIP网关串口服务器

Modbus主站EtherNet/IP转ModbusRTU/ASCII工业EIP网关串口服务器https://item.taobao.com/item.htm?ftt&id743840591638 EtherNet/IP 串口网关 EtherNet/IP 转 RS485 型号 2路总线EIP网关 MS-A1-2021 4路总线EIP网关 MS-A1-2041 4路总线EIP网关&#xff08;双网口&am…

Ubuntu 系统崩了,如何把数据拷下来

问题描述&#xff1a; Linux系统中安装输入法后&#xff0c;重启后&#xff0c;导致系统无法进入&#xff0c;进入 recovery mode下的resume 也启动不了&#xff0c;所以决定将需要的东西复制到U盘 解决方案&#xff1a; 1.重启ubuntu&#xff0c;随即点按Esc进入grub菜单&am…

编程魔法:基于LLM的AI function开发,如何实现高效数据生成?

基于大语言模型&#xff08;LLM&#xff09;的AI function开发&#xff0c;简直就是现代编程界的“魔法棒”&#xff01; 你好&#xff0c;我是三桥君 最近三桥君有个任务&#xff0c;需要造一些测试数据&#xff0c;比如姓名、手机号、银行卡号、邮箱啥的&#xff0c;用来做测…

OCR识别系统 YOLOv8 +Paddle 方案落地

YOLOv8 PaddleOCR 技术方案落地 Yolov8相关文档Step 1 证件模型的训练Step 2 Yolov8进行图片推理Step 3 PaddleOCR进行识别Step 4 整合Yolov8 PaddleOCR 进行OCR Yolov8相关文档 《yolov8 官方网站》 《Yolov8 保姆级别安装》 Ultralytics YOLOv8 是一款尖端的、最先进的 (S…

js将对象的键和值分别归纳进对象,并将多层对象转化成数据的方法

前言&#xff1a; 后端传给我一个没有处理过的json串&#xff0c;但是我要传入el-tree做渲染&#xff0c;此篇来记录一下整个数据处理过程以及el-tree的使用 需求描述&#xff1a; 一、树结构可以展开可以收缩&#xff0c;默认全部展开 二、有一些关键词需要高亮展示红色 …

SW - 装配图旋转到一个想要的正视图

文章目录 SW - 装配图旋转到一个想要的正视图概述笔记将装配图旋转到自己想要的视图的方法保存当前视图选择自己保存的视图END SW - 装配图旋转到一个想要的正视图 概述 在弄装配图。 如果按照SW默认的视图&#xff0c;Y方向是反的。 原因在于我画零件图时&#xff0c;方向就…

从源码中学习动态代理模式

动态代理模式 动态代理是 Java 反射&#xff08;Reflection&#xff09;API 提供的一种强大机制&#xff0c;它允许在运行时创建对象的代理实例&#xff0c;而不需要在编译时静态地创建。 Java 提供了两种主要的方式来实现动态代理&#xff1a; 基于接口的动态代理&#xff1a…

GIT安装及集成到IDEA中操作步骤

最近深感GIT使用技能太差&#xff0c;我只会些皮毛&#xff0c;还是得看官网&#xff0c;总结一下常用的操作方法吧。 GIT环境配置到IDEA中安装 一、GIt的基本的安装 这个不在这里赘述了&#xff0c;自己装一个git吧 二、给IDEA指定本地GIT的安装路径 1、下图这个是我本地的…

postgresql僵尸进程的处理思路

简介 僵尸进程&#xff08;zombie process&#xff09;是指一个已经终止但仍然在进程表中保留条目的进程。正常情况下&#xff0c;当一个进程完成执行并退出时&#xff0c;操作系统会通过父进程调用的wait()或waitpid()系统调用来收集该子进程的退出状态。如果父进程未及时调用…

CI/CD中的自动化测试:在持续集成/持续部署流程中引入网页自动化测试

目录 引言 一、CI/CD流程概述 1.1 什么是CI/CD 1.2 CI/CD流程的主要阶段 1.3 CI/CD的优点 二、自动化测试基础 2.1 自动化测试概述 2.2 自动化测试的作用 2.3 自动化测试的主要类型 三、Web自动化测试工具 3.1 Selenium 3.1.1 Selenium WebDriver常用API 3.1.2 示例…

【Linux服务器】git和github交互使用

前言&#xff1a;有时候pycharm连接不上github&#xff0c;还是得命令行操作 目录 1. 准备git2. 配置github账户3. 上传项目3.1 创建本地仓库3.2 提交本地代码3.3 上传到github 4. 注意 1. 准备git 下载链接&#xff1a;官网 下载后直接运行安装&#xff0c;cmd输入git --vers…