[Unity] 自定义日志系统 解决Unity Log的痛点

当前Unity日志存在的问题:

1.日志打印没有时间

2.日志文件中Log、Warning、和Error区分度不大

3.长时间没有清理容易产生动辄几十MB,几十万行的日志文件

本日志系统区别于Unity原生Log主要有以下几点:

1.日志打印添加时间戳

2.普通打印可忽略堆栈信息

3.每次启动生成一份日志文件

4.日志文件包含设备信息

5.关闭Log可自定义关闭内容,关闭Log默认情况下仅屏蔽普通打印,不屏蔽Warning和Error

6.关闭Log情况下出现Error自动打印前20条Log方便跟踪报错

效果展示:

使用方式:

需要在程序初始化时调用初始化日志系统,以便日志系统捕获系统报错

Editor:

日志文件:

源码:

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

namespace AuthorZhidai
{

    public class Log
    {

        private const int LOG_COUNT = 20;// 最多临时保存LOG_COUNT条日志
        private const int LOG_FILE_COUNT = 10;// 最多保存的日志文件数


        public static bool EnableLog = true;// 是否启用日志,仅可控制普通级别的日志的启用与关闭,LogError和LogWarn都是始终启用的。
        public static bool EnableSave = true;// 是否允许保存日志,即把日志写入到文件中

        public static string LogFileDir = Application.dataPath.Replace("Assets", "") + "Log";// 日志存放目录:和Assets同级目录下的Log
        public static string LogFileName = "";
        public static string Prefix = "> ";// 用于与Unity默认的系统日志做区分。本日志系统输出的日志头部都会带上这个标记。
        public static StreamWriter LogFileWriter = null;


        //日志列表,忽略Info时报错回溯使用
        public static List<string> ListLogs = new List<string>();

        //第一次执行打印log
        private static bool FirstLogTag = true;

        public static void Init()
        {
            Application.logMessageReceived += OnLogByUnity;
        }

        #region 日志
        public static void Info(object message, bool recordStackTrace = false)
        {
            string str = "[I]" + GetLogTime() + message;
            AddListLogs(str);
            if (!EnableLog)
                return;

            Debug.Log(Prefix + str, null);
            LogToFile(str, recordStackTrace);
        }

        public static void Warning(object message)
        {
            string str = "[W]" + GetLogTime() + message;
            AddListLogs(str);
            Debug.LogWarning(Prefix + str, null);
            LogToFile(str, true);
        }

        public static void Error(object message)
        {
            string str = "[E]" + GetLogTime() + message;
            Debug.LogError(Prefix + str, null);
            if (!EnableLog)
            {
                OutputListLogs(LogFileWriter);// 忽略Info时报错,自动将日志记录到文件中方便回溯
            }
            else
            {
                AddListLogs(str);
            }
            LogToFile(str, true);
        }

        /// <summary>
        /// 输出列表中所有日志
        /// 可能会造成卡顿,谨慎使用。
        /// </summary>
        /// <param name="sw"></param>
        public static void OutputListLogs(StreamWriter sw)
        {
            if (sw == null || ListLogs.Count < 1)
                return;
            sw.WriteLine($"---------------- Log History Start [以下是报错前{ListLogs.Count}条日志]---------------- ");
            foreach (var i in ListLogs)
            {
                sw.WriteLine(i);
            }
            sw.WriteLine($"---------------- Log History  End  [以上是报错前{ListLogs.Count}条日志]---------------- ");
            ListLogs.Clear();
        }
        #endregion

        public static void CloseLog()
        {
            if (LogFileWriter != null)
            {
                try
                {
                    LogFileWriter.Flush();
                    LogFileWriter.Close();
                    LogFileWriter.Dispose();
                    LogFileWriter = null;
                }
                catch (Exception)
                {
                }
            }
        }

        public static void CheckClearLog()
        {
            if (!Directory.Exists(LogFileDir))
            {
                return;
            }

            DirectoryInfo direction = new DirectoryInfo(LogFileDir);
            var files = direction.GetFiles("*");
            if (files.Length >= LOG_FILE_COUNT)
            {
                var oldfile = files[0];
                var lastestTime = files[0].CreationTime;
                foreach (var file in files)
                {
                    if (lastestTime > file.CreationTime)
                    {
                        oldfile = file;
                        lastestTime = file.CreationTime;
                    }

                }
                oldfile.Delete();
            }

        }

        private static void OnLogByUnity(string condition, string stackTrace, LogType type)
        {
            // 过滤自己的输出
            if (type == LogType.Log || condition.StartsWith(Prefix))
            {
                return;
            }
            var str = type == LogType.Warning ? "[W]" : "[E]" + GetLogTime() + condition + "\n" + stackTrace;
            if (!EnableLog && type != LogType.Warning)
                OutputListLogs(LogFileWriter);// 忽略Info时报错,自动将日志记录到文件中方便回溯
            else
                AddListLogs(str);
            LogToFile(str);
        }

        private static void AddListLogs(string str)
        {
            if (ListLogs.Count > LOG_COUNT)
            {
                ListLogs.RemoveAt(0);
            }
            ListLogs.Add(str);
        }

        private static string GetLogTime()
        {
            string str = "";

            str = DateTime.Now.ToString("HH:mm:ss.fff") + " ";


            return str;
        }

        /// <summary>
        /// 将日志写入到文件中
        /// </summary>
        /// <param name="message"></param>
        /// <param name="EnableStack"></param>
        private static void LogToFile(string message, bool EnableStack = false)
        {
            if (!EnableSave)
                return;

            if (LogFileWriter == null)
            {
                CheckClearLog();
                LogFileName = DateTime.Now.GetDateTimeFormats('s')[0].ToString();
                LogFileName = LogFileName.Replace("-", "_");
                LogFileName = LogFileName.Replace(":", "_");
                LogFileName = LogFileName.Replace(" ", "");
                LogFileName = LogFileName.Replace("T", "_");
                LogFileName = LogFileName + ".log";
                if (string.IsNullOrEmpty(LogFileDir))
                {
                    try
                    {
                        if (!Directory.Exists(LogFileDir))
                        {
                            Directory.CreateDirectory(LogFileDir);
                        }
                    }
                    catch (Exception exception)
                    {
                        Debug.Log(Prefix + "获取 Application.streamingAssetsPath 报错!" + exception.Message, null);
                        return;
                    }
                }
                string path = LogFileDir + "/" + LogFileName;

                Debug.Log("Log Path :" + LogFileDir + "\nLog Name :" + LogFileName);
                try
                {
                    if (!Directory.Exists(LogFileDir))
                    {
                        Directory.CreateDirectory(LogFileDir);
                    }
                    LogFileWriter = File.AppendText(path);
                    LogFileWriter.AutoFlush = true;
                }
                catch (Exception exception2)
                {
                    LogFileWriter = null;
                    Debug.Log("LogToCache() " + exception2.Message + exception2.StackTrace, null);
                    return;
                }
            }
            if (LogFileWriter != null)
            {
                try
                {
                    if (FirstLogTag)
                    {
                        FirstLogTag = false;
                        PhoneSystemInfo(LogFileWriter);
                    }
                    LogFileWriter.WriteLine(message);
                    if (EnableStack)
                    {
                        //把无关的log去掉
                        var st = StackTraceUtility.ExtractStackTrace();
#if UNITY_EDITOR
                        for (int i = 0; i < 3; i++)
#else
                        for (int i = 0; i < 2; i++)
#endif
                        {
                            st = st.Remove(0, st.IndexOf('\n') + 1);
                        }
                        LogFileWriter.WriteLine(st);
                    }
                }
                catch (Exception)
                {
                }
            }
        }

        private static void PhoneSystemInfo(StreamWriter sw)
        {
            sw.WriteLine("*********************************************************************************************************start");
            sw.WriteLine("By " + SystemInfo.deviceName);
            DateTime now = DateTime.Now;
            sw.WriteLine(string.Concat(new object[] { now.Year.ToString(), "年", now.Month.ToString(), "月", now.Day, "日  ", now.Hour.ToString(), ":", now.Minute.ToString(), ":", now.Second.ToString() }));
            sw.WriteLine();
            sw.WriteLine("操作系统:  " + SystemInfo.operatingSystem);
            sw.WriteLine("系统内存大小:  " + SystemInfo.systemMemorySize);
            sw.WriteLine("设备模型:  " + SystemInfo.deviceModel);
            sw.WriteLine("设备唯一标识符:  " + SystemInfo.deviceUniqueIdentifier);
            sw.WriteLine("处理器数量:  " + SystemInfo.processorCount);
            sw.WriteLine("处理器类型:  " + SystemInfo.processorType);
            sw.WriteLine("显卡标识符:  " + SystemInfo.graphicsDeviceID);
            sw.WriteLine("显卡名称:  " + SystemInfo.graphicsDeviceName);
            sw.WriteLine("显卡标识符:  " + SystemInfo.graphicsDeviceVendorID);
            sw.WriteLine("显卡厂商:  " + SystemInfo.graphicsDeviceVendor);
            sw.WriteLine("显卡版本:  " + SystemInfo.graphicsDeviceVersion);
            sw.WriteLine("显存大小:  " + SystemInfo.graphicsMemorySize);
            sw.WriteLine("显卡着色器级别:  " + SystemInfo.graphicsShaderLevel);
            sw.WriteLine("是否图像效果:  " + SystemInfo.supportsImageEffects);
            sw.WriteLine("是否支持内置阴影:  " + SystemInfo.supportsShadows);
            sw.WriteLine("*********************************************************************************************************end");
            sw.WriteLine("LogInfo:");
            sw.WriteLine();
        }

    }

}

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Unity中的自定义弹框可以使用Unity3D Custom Popup插件来实现。该插件可以推迟构造弹出窗口的显示,对于构造弹出窗口相对昂贵的情况非常有用。你可以通过执行`yarn add rotorz/unity3d-custom-popup`命令来安装该软件包,并将其与Unity工具兼容。然后按照插件的说明进行操作,将自定义弹出控件添加到Unity编辑器界面中。 具体而言,你可以在代码中调用`MyPermissionDialog.Builder`来创建一个自定义弹框。可以使用`setPositiveButton`方法设置弹框的确认按钮的点击事件,并在点击事件中执行相应的操作。通过`setTitleAndContext`方法可以设置弹框的标题和内容。最后,通过调用`create`方法创建弹框,并使用`show`方法显示出来。 请注意,这只是一种实现自定义弹框的方式,根据你的具体需求,可能还有其他的方法可供选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Android自定义弹框以及弹框资源加载](https://blog.csdn.net/u014146238/article/details/104824842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [unity3d-custom-popup:Unity编辑器界面的自定义弹出控件,将弹出菜单的构造推迟到显示为止。 这对于弹出...](https://download.csdn.net/download/weixin_42131443/18312293)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值