Unity日志管理

Unity日志管理

Unity的日志輸出在編輯器中初肉,而且格式詭異厢钧,不方便查看。著實不利于開發(fā)睹逃,在Java開發(fā)時候我們有Log4j盗扇,Logback等日志框架,格式定義舒服又方便沉填。這不Unity好像沒有粱玲,那就自己打造一個吧。

需求分析

  1. 可以輸出到文件
  2. 異步打印日志拜轨,提高性能
  3. 追加打印而不是重啟重頭開始
  4. 易于使用

需求實現(xiàn)

做過Java開發(fā)的人都知道抽减,Java中的Log4j等日志框架都通過各種各樣的Appender來擴展輸出實現(xiàn)。
這里只是以一種實現(xiàn)講解一下思路:

先定義一個Appender的接口

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

來個RollingFileAppender的實現(xiàn):

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;
        // 是否停止的標(biāo)志
        private bool stopFlag;
        /// <summary>
        /// 構(gòu)造函數(shù)
        /// </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;

            // 開始執(zhí)行
            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>
        /// 關(guān)閉執(zhí)行
        /// </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);
                    }

                    // 判斷是否觸發(fā)策略
                    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的具體內(nèi)容
    /// </summary>
    public class LogData
    {
        /// <summary>
        /// Log具體內(nèi)容
        /// </summary>
        /// <value>The log.</value>
        public string Log{ get; set; }
        /// <summary>
        /// Log標(biāo)記
        /// </summary>
        /// <value>The tag.</value>
        public string Tag{ get; set; }
        /// <summary>
        /// Log堆棧信息
        /// </summary>
        /// <value>The track.</value>
        public string Track{ get; set; }
    }

該實現(xiàn)用到了上一篇講到的Unity多線程管理卵沉,基本思路如下:

  • 雙隊列颠锉,一個隊列用于添加日志內(nèi)容,一個隊列用于寫日志
  • 超過文件大小史汗,重頭開始寫

我對外暴露一個Log接口琼掠,實現(xiàn)如下:

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>
        /// 設(shè)置日志級別
        /// </summary>
        /// <value>The log level.</value>
        public int LogLevel {
            get;
            set;
        }
        /// <summary>
        /// 構(gòu)造函數(shù)
        /// </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 ();
        }
    }

后續(xù)改進

  1. 多Appender支持停撞,至少要擴展ConsoleAppender
  2. 可配置
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓷蛙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子戈毒,更是在濱河造成了極大的恐慌艰猬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埋市,死亡現(xiàn)場離奇詭異冠桃,居然都是意外死亡,警方通過查閱死者的電腦和手機道宅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門食听,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人污茵,你說我怎么就攤上這事樱报。” “怎么了泞当?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵肃弟,是天一觀的道長。 經(jīng)常有香客問我零蓉,道長,這世上最難降的妖魔是什么穷缤? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任敌蜂,我火速辦了婚禮,結(jié)果婚禮上津肛,老公的妹妹穿的比我還像新娘章喉。我一直安慰自己,他們只是感情好身坐,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布秸脱。 她就那樣靜靜地躺著,像睡著了一般部蛇。 火紅的嫁衣襯著肌膚如雪摊唇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天涯鲁,我揣著相機與錄音巷查,去河邊找鬼有序。 笑死,一個胖子當(dāng)著我的面吹牛岛请,可吹牛的內(nèi)容都是我干的旭寿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼崇败,長吁一口氣:“原來是場噩夢啊……” “哼盅称!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起后室,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缩膝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后咧擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逞盆,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年松申,在試婚紗的時候發(fā)現(xiàn)自己被綠了云芦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡贸桶,死狀恐怖舅逸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情皇筛,我是刑警寧澤琉历,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站水醋,受9級特大地震影響旗笔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拄踪,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一蝇恶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惶桐,春花似錦撮弧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至救恨,卻和暖如春贸辈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肠槽。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工裙椭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躏哩,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓揉燃,卻偏偏與公主長得像扫尺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子炊汤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 在應(yīng)用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況正驻,周期性的記錄到文件中供其他應(yīng)用進行統(tǒng)計分析...
    時待吾閱讀 5,055評論 1 13
  • 在應(yīng)用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況,周期性的記錄到文件中供其他應(yīng)用進行統(tǒng)計分析...
    時待吾閱讀 4,991評論 0 6
  • from:https://www.cnblogs.com/ITtangtang/p/3926665.html一抢腐、L...
    enshunyan閱讀 3,294評論 0 0
  • 一姑曙、Log4j簡介 Log4j有三個主要的組件:Loggers(記錄器),Appenders (輸出源)和Layo...
    默默守護閱讀 1,913評論 2 8
  • log4j 1.1 簡介 Log4j是一個由Java編寫可靠迈倍、靈活的日志框架伤靠,是Apache旗下的一個開源項目;現(xiàn)...
    賈博巖閱讀 7,909評論 1 32