Unity日志管理

作者: Aodota | 来源:发表于2016-12-17 16:16 被阅读312次

Unity日志管理

Unity的日志输出在编辑器中,而且格式诡异,不方便查看。着实不利于开发,在Java开发时候我们有Log4jLogback等日志框架,格式定义舒服又方便。这不Unity好像没有,那就自己打造一个吧。

需求分析

  1. 可以输出到文件
  2. 异步打印日志,提高性能
  3. 追加打印而不是重启重头开始
  4. 易于使用

需求实现

做过Java开发的人都知道,Java中的Log4j等日志框架都通过各种各样的Appender来扩展输出实现。
这里只是以一种实现讲解一下思路:

先定义一个Appender的接口

    /// <summary>
    /// 日志Appender
    /// </summary>
    public interface ILogAppender
    {
        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="log">Log.</param>
        void Log(LogData logData);
    }

来个RollingFileAppender的实现:

public class RollingFileAppender : AsyncTask, ILogAppender
    {
        #if UNITY_EDITOR
        string logRootPath = Application.dataPath + "/Log";
        #elif UNITY_STANDALONE_WIN
        string logRootPath = Application.dataPath + "/Log";
        #elif UNITY_STANDALONE_OSX
        string logRootPath = Application.dataPath + "/Log";
        #else
        string logRootPath = Application.persistentDataPath + "/Log";
        #endif
        // 时间格式化
        protected const string TIME_FORMATER = "yyyy-MM-dd hh:mm:ss,fff";

        /// <summary>
        /// 文件最大值 10M
        /// </summary>
        private int maxFileSize = 10 * 1024 * 1024;
        // 文件Writer
        private StreamWriter streamWriter;
        // 文件流
        private FileStream fileStream;
        // 文件路径
        private string filePath;
        // 写日志队列
        private List<LogData> writeList;
        // 等待队列
        private List<LogData> waitList;
        // 锁
        private object lockObj;
        // 是否停止的标志
        private bool stopFlag;
        /// <summary>
        /// 构造函数
        /// </summary>
        public RollingFileAppender ()
        {
            this.filePath = Path.Combine (logRootPath, "game.log");
            if (File.Exists (filePath)) {
                this.fileStream = new FileStream (filePath, FileMode.Append);
                this.streamWriter = new StreamWriter (this.fileStream);
                this.streamWriter.AutoFlush = true;

            } else {
                if (!Directory.Exists (logRootPath)) {
                    Directory.CreateDirectory (logRootPath);
                }
                this.fileStream = new FileStream (filePath, FileMode.Create);
                this.streamWriter = new StreamWriter (this.fileStream);
                this.streamWriter.AutoFlush = true;
            }
            this.writeList = new List<LogData> ();
            this.waitList = new List<LogData> ();
            this.lockObj = new object ();
            this.stopFlag = false;

            // 开始执行
            MultiThreadMgr.AddAsyncTask (this);
        }
        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="log">Log.</param>
        /// <param name="logData">Log data.</param>
        public void Log (LogData logData)
        {
            lock (lockObj) {
                waitList.Add (logData);
                Monitor.PulseAll (lockObj);
            }
        }
        /// <summary>
        /// 关闭执行
        /// </summary>
        public override void Close ()
        {
            this.stopFlag = true;
            if (null != this.fileStream) {
                this.fileStream.Close ();
            }
        }
        /// <summary>
        /// 开始运行
        /// </summary>
        public override void Run ()
        {
            while (!stopFlag) {
                lock (lockObj) {
                    if (waitList.Count == 0) {
                        Monitor.Wait (lockObj);            
                    }
                    this.writeList.AddRange (this.waitList);
                    this.waitList.Clear ();
                }

                foreach (LogData data in writeList) {
                    // 写日志
                    this.streamWriter.WriteLine (String.Format ("{0}#{1}#{2}", System.DateTime.Now.ToString (TIME_FORMATER), data.Tag, data.Log));

                    // 写堆栈
                    if (null != data.Track) {
                        this.streamWriter.WriteLine (data.Track);
                    }

                    // 判断是否触发策略
                    HandleTriggerEvent ();
                }
            }

        }
        /// <summary>
        /// 处理Trigger事件
        /// </summary>
        private void HandleTriggerEvent() {
            if (this.fileStream.Length >= maxFileSize) {
                // 文件超过大小,重头开始写
                this.streamWriter.Close ();
                this.fileStream.Close ();

                // 重新开始写
                this.fileStream = new FileStream (this.filePath, FileMode.Create);
                this.streamWriter = new StreamWriter (this.fileStream);
                this.streamWriter.AutoFlush = true;
            }
        }
    }

LogData定义

    /// <summary>
    /// Log的具体内容
    /// </summary>
    public class LogData
    {
        /// <summary>
        /// Log具体内容
        /// </summary>
        /// <value>The log.</value>
        public string Log{ get; set; }
        /// <summary>
        /// Log标记
        /// </summary>
        /// <value>The tag.</value>
        public string Tag{ get; set; }
        /// <summary>
        /// Log堆栈信息
        /// </summary>
        /// <value>The track.</value>
        public string Track{ get; set; }
    }

该实现用到了上一篇讲到的Unity多线程管理,基本思路如下:

  • 双队列,一个队列用于添加日志内容,一个队列用于写日志
  • 超过文件大小,重头开始写

我对外暴露一个Log接口,实现如下:

public class Log
    {
        // logAppender
        private ILogAppender logAppender;
        /// <summary>
        /// 日志等级,为不同输出配置用
        /// </summary>
        public const int DEBUG = 0;
        public const int INFO = 1;
        public const int WARNING = 2;
        public const int ERROR = 3;
        public const int FATAL = 4;
        // Log实例
        protected static Log instance = null;
        /// <summary>
        /// 获取实例
        /// </summary>
        /// <value>The instance.</value>
        public static Log Instance {
            get{ 
                if (null == instance) {
                    instance = new Log ();
                }
                return instance;
            }
        } 

        /// <summary>
        /// 设置日志级别
        /// </summary>
        /// <value>The log level.</value>
        public int LogLevel {
            get;
            set;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        private Log() {
            LogLevel = INFO;
            logAppender = new RollingFileAppender();
        }
        /// <summary>
        /// debug Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void debug(object log) {
            if (LogLevel > DEBUG) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "DEBUG";
            logAppender.Log (data);
        }
        /// <summary>
        /// info Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void info(object log) {
            if (LogLevel > INFO) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "INFO";
            logAppender.Log (data);
        }
        /// <summary>
        /// warn Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void warn(object log) {
            if (LogLevel > WARNING) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "WARN";
            logAppender.Log (data);
        }
        /// <summary>
        /// warn Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void warn(object log, string track) {
            if (LogLevel > WARNING) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "WARN";
            data.Track = track;
            logAppender.Log (data);
        }
        /// <summary>
        /// warn Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void warn(object log, Exception e) {
            if (LogLevel > WARNING) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "WARN";
            data.Track = GetExceptionTrack(e);
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void error(object log) {
            if (LogLevel > ERROR) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "ERROR";
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void error(object log, string track) {
            if (LogLevel > ERROR) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "ERROR";
            data.Track = track;
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void error(object log, Exception e) {
            if (LogLevel > ERROR) {
                return;
            }
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "ERROR";
            data.Track = GetExceptionTrack(e);
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void fatal(object log) {
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "FATAL";
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void fatal(object log, string track) {
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "FATAL";
            data.Track = track;
            logAppender.Log (data);
        }
        /// <summary>
        /// error Log
        /// </summary>
        /// <param name="log">Log.</param>
        public void fatal(object log, Exception e) {
            LogData data = new LogData ();
            data.Log = log.ToString();
            data.Tag = "FATAL";
            data.Track = GetExceptionTrack(e);
            logAppender.Log (data);
        }
        /// <summary>
        /// 获取异常堆栈
        /// </summary>
        /// <returns>The exception track.</returns>
        /// <param name="e">E.</param>
        private string GetExceptionTrack(Exception e) {
            StringBuilder builder = new StringBuilder (120);
            builder.Append (e.Message).Append("\n");
            if (!string.IsNullOrEmpty (e.StackTrace)) {
                builder.Append (e.StackTrace);
            }
            return builder.ToString ();
//
//            StackTrace stackTrace = e.StackTrace;
//            StackFrame[] stackFrames = stackTrace.GetFrames ();
//            foreach (StackFrame frame in stackFrames) {
//                builder.Append ("\t at ")
//                    .Append (frame.GetFileName())
//                    .Append (".")
//                    .Append(frame.GetMethod())
//                    .Append(":")
//                    .Append(frame.GetFileLineNumber())
//                    .Append("\n");
//            }
//            return builder.ToString ();
        }
    }

后续改进

  1. 多Appender支持,至少要扩展ConsoleAppender
  2. 可配置

相关文章

  • Unity日志管理

    Unity日志管理 Unity的日志输出在编辑器中,而且格式诡异,不方便查看。着实不利于开发,在Java开发时候我...

  • unity日志查看器

    前一段时间 为了方便查看unity的日志输出,自己写了一套粗略的unity日志查看器,直接unity出包,然后可以...

  • Unity未能启动包管理器本地服务器线程

    Unity未能启动包管理器本地服务器线程 Unity报错描述: Failed to start the Unity...

  • 20171012 日志管理

    日志介绍rsyslog日志管理journalctlMySQL管理日志 一、日志介绍 (一)日志的基本概念 日志:将...

  • HDFS Using QJM

    HDFS 使用分布式日志管理,日志管理的是nameNode的fsimages和eidts日志文件。

  • 27-日志管理

    本章内容 ◆ 日志介绍◆ 日志配置◆ 日志管理◆ 远程日志◆ 基于MYSQL的日志 日志介绍 rsyslog 启用...

  • Unity下载及版本管理

    unity下载可以在unity.cn里面下载旧版本,unity.com里面可以下载Hub用于版本管理,也可以在Hu...

  • 02、Kafka日志Log源码分析

    日志是日志段的容器,里面定义了很多管理日志段的操作。 既然日志要管理日志段对象,那么首先得加载所有日志段对象到内存...

  • 第十章 使用日志监控 - 配置日志监控

    第十章 使用日志监控 - 配置日志监控 日志监视器管理器实用程序 ^MONMGR 允许配置和管理日志管理器。可以停...

  • Unity 性能优化及一些其他技巧

    更新日志:(由于此篇会长期不定时更新,所以添加一个“更新日志”)Unity 的托管内存优化 2017.09.08X...

网友评论

    本文标题:Unity日志管理

    本文链接:https://www.haomeiwen.com/subject/iqaomttx.html