分享一个我用的 unity 日志系统

我们在开发中,往往会觉得unity自带的日志系统不是那么好用,比如,不能筛选日志,不能打印在屏幕上,想生成自己的日志文件,等等等等的需求,这时候就需要自己来扩展编辑器的相关功能了。

我这里实现的需求比较简单,就是给日志加了个key,可以根据这个key来对暂时不需要显示的日志进行过滤,还有就是将我们的日志打印到屏幕上去。

打印屏幕的类参考了由 Jeremy Hollingsworth 和 Simon Waite 这2人写的一个 DebugConsole.cs 类,不过他们2个的版本实在是太早了,而且还有错误和啰嗦的地方,有兴趣的话你可以去搜来看看。外加老外真的很喜欢用while循环呀,好多能用for的地方也是while,是因为这样写字比较少吗?看来大家都是懒癌晚期患者,(:зゝ∠)

一直犯懒,终于把它给写了。。。有时间我也给它扔到 github 上去。。。

外加如果想要输出日志文件,或者其它什么功能的,你就自己加就好了,我当前的需求还是挺简单的~

效果如图:

这样用

或者这样用

一共由2个类组成,一个负责控制台打印,一个负责屏幕打印

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace GameFramework
{
    /// <summary>
    /// 日志管理器
    /// </summary>
    public class DebugMgr
    {
        #region 日志结构

        /// <summary>
        /// 日志基类
        /// </summary>
        protected abstract class LogDataBase
        {
            /// <summary>
            /// key
            /// </summary>
            public string key;
            /// <summary>
            /// 日志
            /// </summary>
            public string log;

            /// <summary>
            /// 构造
            /// </summary>
            /// <param name="_key">key</param>
            /// <param name="_log">日志</param>
            public LogDataBase(string _key, string _log)
            {
                key = _key;
                log = _log;
            }

            /// <summary>
            /// 获取log
            /// </summary>
            /// <param name="format">格式</param>
            /// <returns>log</returns>
            protected string GetLog(string format)
            {
                string txt = string.Format(format, key, log);
                return txt;
            }

            /// <summary>
            /// 打印
            /// </summary>
            /// <param name="format">格式</param>
            /// <returns>日志</returns>
            public abstract string Print(string format);
        }

        /// <summary>
        /// 日志类
        /// </summary>
        protected class LogData : LogDataBase
        {
            public LogData(string key, string message)
                : base(key, message)
            { }

            public override string Print(string format)
            {
                string logTxt = base.GetLog(format);
                Debug.Log(logTxt);
                return logTxt;
            }
        }

        /// <summary>
        /// 警告类
        /// </summary>
        protected class LogWarningData : LogDataBase
        {
            public LogWarningData(string key, string message)
                : base(key, message)
            { }

            public override string Print(string format)
            {
                string logTxt = base.GetLog(format);
                Debug.LogWarning(logTxt);
                return logTxt;
            }
        }

        /// <summary>
        /// 错误类
        /// </summary>
        protected class LogErrorData : LogDataBase
        {
            public LogErrorData(string key, string message)
                : base(key, message)
            { }

            public override string Print(string format)
            {
                string logTxt = base.GetLog(format);
                Debug.LogError(logTxt);
                return logTxt;
            }
        }

        #endregion 日志结构类 ------------------------------------

        /// <summary>
        /// 日志格式
        /// </summary>
        public static string format = "{0}:{1}";

        /// <summary>
        /// 屏幕显示
        /// </summary>
        public static bool screenDisplay = false;

        // 过滤key集合
        private static List<string> s_filterKeyList = new List<string>();

        /// <summary>
        /// 清除所有key
        /// </summary>
        public static void ClearFilterKey()
        {
            s_filterKeyList.Clear();
        }
        /// <summary>
        /// 增加key
        /// </summary>
        /// <param name="key">key</param>
        public static void AddFilterKey(string key)
        {
            // 不加重复的key
            if (s_filterKeyList.Contains(key))
                return;
            s_filterKeyList.Add(key);
        }
        /// <summary>
        /// 移除key
        /// </summary>
        /// <param name="key">key</param>
        public static void RemoveFilterKey(string key)
        {
            if (s_filterKeyList.Contains(key))
                s_filterKeyList.Remove(key);
        }

        // 打印
        private static string Print(LogDataBase ld)
        {
            // 日志不能为空
            if (ld == null)
                return "";

            // 日志key是被过滤的
            if (s_filterKeyList.Contains(ld.key))
                return "";

            // 打印日志
            string logTxt = ld.Print(format);
            return logTxt;
        }

        /// <summary>
        /// 日志
        /// </summary>
        /// <param name="log">日志</param>
        /// <param name="key">key</param>
        public static void Log(string log, string key = "日志")
        {
            LogData ld = new LogData(key, log);
            string logTxt = DebugMgr.Print(ld);

            if (screenDisplay)
                DebugConsole.Log(logTxt);
            else
                DebugConsole.Clear();
        }
        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="log">日志</param>
        /// <param name="key">key</param>
        public static void LogWarning(string log, string key = "警告")
        {
            LogWarningData ld = new LogWarningData(key, log);
            string logTxt = DebugMgr.Print(ld);

            if (screenDisplay)
                DebugConsole.LogWarning(logTxt);
            else
                DebugConsole.Clear();
        }
        /// <summary>
        /// 错误
        /// </summary>
        /// <param name="log">日志</param>
        /// <param name="key">key</param>
        public static void LogError(string log, string key = "错误")
        {
            LogErrorData ld = new LogErrorData(key, log);
            string logTxt = DebugMgr.Print(ld);

            if (screenDisplay)
                DebugConsole.LogError(logTxt);
            else
                DebugConsole.Clear();
        }
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace GameFramework
{
    public class DebugConsole : MonoBehaviour
    {
        /// <summary>
        /// 日志颜色枚举
        /// </summary>
        public enum LogColor
        {
            NORMAL, WARNING, ERROR
        }

        /// <summary>
        /// 普通颜色
        /// </summary>
        public Color normalColor = Color.green;
        /// <summary>
        /// 警告颜色
        /// </summary>
        public Color warningColor = Color.yellow;
        /// <summary>
        /// 错误颜色
        /// </summary>
        public Color errorColor = Color.red;

        /// <summary>
        /// 第一个gui组件
        /// </summary>
        public GameObject debugGui = null;
        /// <summary>
        /// 默认gui位置
        /// </summary>
        public Vector3 defaultGuiPosition = new Vector3(0.01f, 0.98f, 0f);

        /// <summary>
        /// 显示最大日志数
        /// </summary>
        public int maxLogs = 30;
        /// <summary>
        /// 行间距
        /// </summary>
        public float lineSpacing = 0.03f;

        /// <summary>
        /// 能拖动
        /// </summary>
        public bool draggable = true;
        /// <summary>
        /// 是否显示
        /// </summary>
        public bool visible = true;

        // 集合
        private List<GUIText> m_guiList = new List<GUIText>();
        private List<string> m_logList = new List<string>();
        private List<LogColor> m_colorList = new List<LogColor>();

        // 单例
        private static DebugConsole s_instance = null;
        /// <summary>
        /// 获取单例
        /// </summary>
        /// <returns>单例</returns>
        public static DebugConsole GetInstance()
        {
            if (s_instance == null)
            {
                s_instance = (DebugConsole)FindObjectOfType(typeof(DebugConsole));
                if (s_instance == null)
                {
                    GameObject console = new GameObject();
                    console.name = "DebugConsole";
                    console.AddComponent<DebugConsole>();

                    s_instance = (DebugConsole)FindObjectOfType(typeof(DebugConsole));
                }
            }

            return s_instance;
        }

        void Awake()
        {
            DontDestroyOnLoad(this);
            s_instance = this;
            this.InitGuis();
        }

        private void InitGuis()
        {
            // 初始化第一个gui
            if (debugGui == null)
            {
                debugGui = new GameObject();
                debugGui.name = "DebugGUI(0)";
                debugGui.transform.SetParent(this.transform, false);
                debugGui.transform.position = defaultGuiPosition;

                GUIText gt = debugGui.AddComponent<GUIText>();
                m_guiList.Add(gt);
            }

            // 创建其它的gui
            Vector3 position = debugGui.transform.position;
            int guiCount = 1;
            while (guiCount < maxLogs)
            {
                position.y -= lineSpacing;

                GameObject clone = (GameObject)Instantiate(debugGui, position, transform.rotation);
                clone.name = string.Format("DebugGUI({0})", guiCount);
                GUIText gt = clone.GetComponent<GUIText>();
                m_guiList.Add(gt);

                position = clone.transform.position;

                ++guiCount;
            }
            // 设置父节点
            guiCount = 0;
            while (guiCount < m_guiList.Count)
            {
                GUIText temp = (GUIText)m_guiList[guiCount];
                temp.transform.parent = debugGui.transform;

                ++guiCount;
            }
        }

        // 连接到手指
        private bool connectedToMouse = false;
        void Update()
        {
            // 拖拽移动
            if (draggable)
            {
                if (Input.GetMouseButtonDown(0))
                {
                    if (connectedToMouse)
                    {
                        connectedToMouse = false;
                    }
                    else if (connectedToMouse == false && debugGui.GetComponent<GUIText>().HitTest((Vector3)Input.mousePosition))
                    {
                        connectedToMouse = true;
                    }
                }

                if (connectedToMouse)
                {
                    float posX = Input.mousePosition.x / Screen.width;
                    float posY = Input.mousePosition.y / Screen.height;
                    debugGui.transform.position = new Vector3(posX, posY, 0F);
                }
            }
        }

        /// <summary>
        /// 清屏
        /// </summary>
        private void ClearScreen()
        {
            // 对象还没创建全的情况下不清
            if (m_guiList.Count < maxLogs)
                return;

            int count = 0;
            while (count < m_guiList.Count)
            {
                GUIText gt = m_guiList[count];
                gt.text = "";

                ++count;
            }
        }

        /// <summary>
        /// 精简多出来的消息
        /// </summary>
        private void Prune()
        {
            if (m_logList.Count <= maxLogs)
                return;

            int diff = m_logList.Count - maxLogs;

            m_logList.RemoveRange(0, diff);
            m_colorList.RemoveRange(0, diff);
        }

        /// <summary>
        /// 显示
        /// </summary>
        private void Display()
        {
            if (visible)
            {
                // 先尝试是否能精简
                this.Prune();

                // gui不够
                if (m_guiList.Count < maxLogs)
                    return;

                // 显示
                int guiCount = 0;
                while (guiCount < m_guiList.Count && guiCount < m_logList.Count)
                {
                    GUIText gt = m_guiList[guiCount];

                    // 文本
                    gt.text = m_logList[guiCount];

                    // 颜色
                    LogColor lc = m_colorList[guiCount];
                    switch (lc)
                    {
                        case LogColor.NORMAL:
                            {
                                gt.material.color = this.normalColor;
                            }
                            break;
                        case LogColor.WARNING:
                            {
                                gt.material.color = this.warningColor;
                            }
                            break;
                        case LogColor.ERROR:
                            {
                                gt.material.color = this.errorColor;
                            }
                            break;
                    }

                    // 循环
                    ++guiCount;
                }
            }
            else
            {
                // 清屏
                this.ClearScreen();
            }
        }

        // 加日志
        private void AddLog(string log, LogColor color = LogColor.NORMAL)
        {
            m_logList.Add(log);
            m_colorList.Add(color);
            this.Display();
        }
        // 清日志
        private void ClearLog()
        {
            m_logList.Clear();
            m_colorList.Clear();
            this.ClearScreen();
        }

        /// <summary>
        /// 日志
        /// </summary>
        /// <param name="log">日志</param>
        public static void Log(string log)
        {
            DebugConsole.GetInstance().AddLog(log, LogColor.NORMAL);
        }
        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="log">日志</param>
        public static void LogWarning(string log)
        {
            DebugConsole.GetInstance().AddLog(log, LogColor.WARNING);
        }
        /// <summary>
        /// 错误
        /// </summary>
        /// <param name="log">日志</param>
        public static void LogError(string log)
        {
            DebugConsole.GetInstance().AddLog(log, LogColor.ERROR);
        }
        /// <summary>
        /// 清除
        /// </summary>
        public static void Clear()
        {
            DebugConsole.GetInstance().ClearLog();
        }
    }
}

测试类:

using UnityEngine;
using System.Collections;

using GameFramework;

public class Test : MonoBehaviour
{
    public bool screenDisplay = false;

    // Use this for initialization
    void Start()
    {
        DebugMgr.Log("开始了");
    }

    private int m_count = 0;

    void OnGUI()
    {
        if (GUI.Button(new Rect(500, 0, 100, 100), "加log"))
        {
            DebugMgr.screenDisplay = screenDisplay;

            string log = "count = " + m_count;
            DebugMgr.Log(log);
            DebugMgr.LogWarning(log);
            DebugMgr.LogError(log);

            ++m_count;
        }
    }
}

友情提示一下,你可能发现了,现在你左键点一下 Console 窗口下的日志,系统会给你跳转到 DebugMgr 类里,而不是真正打日志的地方,想要解决这个问题也很简单。你就把这2个类编译封装成一个单独的c#类库项目,生成dll后,再导入项目中就可以了,这样 unity 在追踪程序的堆栈调用时,跳转的位置就会指向你真正打日志的地方了,轻松又愉快~

时间: 2024-10-11 01:40:28

分享一个我用的 unity 日志系统的相关文章

日志系统之定时任务执行引擎

概述 最近这段时间在强化日志系统自身的稳定性和可靠性,一个稳定可靠的系统离不开监控,我们这里谈及的监控除了服务是否存活还有这些组件的核心metrics采集与抓取,为此我们将这些任务做成了定时任务来执行.由于大致的思路以及设计已经成型,所以今天来分享一下日志系统在定时任务这块的选型与设计. 组件运行时监控 从我之前分享的文章中不难看出我们日志系统的各个组件的选型: 采集agent : Flume-NG 消息系统 : Kafka 实时流处理 : Storm 分布式搜索/日志存储(暂时) : Elas

DNS的视图功能以及日志系统

实验环境:RHEL5.8 32Bit DNS的视图功能以及日志系统详解 DNS的主配置文件中的allow-recursion参数用来定义能够和DNS服务器进行递归的客户端来源,allow-query参数用来定义允许到DNS服务器上面发起查询请求的客户端,allow-transfer参数用来定义允许和DNS服务器进行区域传送的客户端,区域传送主要有两种方式,axfr和ixfr,使用dig命令也可以模拟实现区域传送: 如果我们的DNS服务器允许进行递归.发起查询请求以及进行区域传送的客户端比较多的话

使用线程局部存储实现多线程下的日志系统(转)

http://www.ibm.com/developerworks/cn/linux/1310_qianbh_threadlog/index.html 多线程编程向来不容易,在多线程环境下实现日志系统是很多程序员亟须解决的问题.在本文中详细介绍了线程局部存储的概念.原理,并用代码示例详细展示了如何使用线程局部存储来实现多线程下的日志系统. 概述 通常来说,在应用程序中需要日志来记录程序运行的状态,以便后期问题的跟踪定位.在日志系统的设计中,通常会有一个总的日志系统来统一协调这些日志的设置如位置.

Linux下一个简单的日志系统的设计及其C代码实现

1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回值及其执行情况.脚本执行及调用情况等.通过阅读日志文件,我们能够较快地跟踪程序流程,并发现程序问题. 因此,熟练掌握日志系统的编写方法并快速地阅读日志文件,是对一个软件开发工程师的基本要求. 本文详细地介绍了Linux下一个简单的日志系统的设计方法,并给出了其C代码实现.本文为相关开发项目Linux

分享一个用安卓手机就能引导pc安装linux系统办法

1.首先安卓手机下载软件DriveDroid.apk http://pan.baidu.com/s/1qW4pbT6 2.下载linux镜像文件放手机存储卡存储,放到Download/images/下面 3.打开软件会自动读取这个文件夹下面镜像,也可以在软件里面下载需要的镜像文件 4.软件设置usb连接模式 5.然后手机usb通过数据线连接电脑,电脑选择手机引导,便开始进入linux引导安装界面进行安装,我使用的linux deepin2014的镜像使用uefi引导安装,完美进行安装,安装完进行

分享一个PHP调试日志类

分享一个我自己用的在 WordPress 开发中用得到的一个调试日志类. <?php /** * @author: suifengtec coolwp.com * @date: 2013-02-03 09:55:55 * @last Modified by: suifengtec coolwp.com * @last Modified time: 2015-07-12 18:40:02 */ if(function_exists('add_action')){ defined('ABSPATH'

分享并开源一个简单粗暴的redmine测试报告系统

背景 软件测试的最后有一道比较繁琐的工作,就是编写测试报告.手写测试报告在数据统计和分析上面要耗费比较大的事件和精力.之前工作室使用mantis管理bug缺陷.公司有内部有个系统,可以直接从mantis上面获取数据并进行统计,生成一份测试报告.后来换了一个工作室,bug缺陷管理平台也从原来的mantis 换成了redmine (http://www.redmine.org/ ). 然后走上了一条手写测试报告的不归路(测试人员前期还是推荐手写测试报告的).搞游戏的都知道,平时加班加点压力就够大的.

【分享】我们用了不到200行代码实现的文件日志系统,极佳的IO性能和高并发支持,附压力测试数据

很多项目都配置了日志记录的功能,但是,却只有很少的项目组会经常去看日志.原因就是日志文件生成规则设置不合理,将严重的错误日志跟普通的错误日志混在一起,分析起来很麻烦. 其实,我们想要的一个日志系统核心就这2个要求: 日志文件能够按照 /_logs/{group}/yyyy-MM/yyyy-MM-dd-{sequnce}.log 这样的规则生成: 调用写日志的方法能够带 group 这个字符串参数,差不多是这样:LogHelper.TryLog(string group, string messa

日志系统之Flume采集加morphline解析

概述 这段时间花了部分时间在处理消息总线跟日志的对接上.这里分享一下在日志采集和日志解析中遇到的一些问题和处理方案. 日志采集-flume logstash VS flume 首先谈谈我们在日志采集器上的选型.由于我们选择采用ElasticSearch作为日志的存储与搜索引擎.而基于ELK(ElasticSearch,Logstash,Kibana)的技术栈在日志系统方向又是如此流行,所以把Logstash列入考察对象也是顺理成章,Logstash在几大主流的日志收集器里算是后起之秀,被Elas