1 日志写入器概要
设计目的
有一段时间游戏战斗系统里出现了一个不是必现的BUG,偶尔出现偶尔不出现,Unity虽然安装了Reporter调试插件,但是日志还是无法写入本地,就很蛋疼。同事说可以连接logcat做定位,可是logcat用过的小伙伴都知道,总是会输出一大堆不需要的log,容易遗漏关键信息。最麻烦的是需要插着线调试非常麻烦。定位一些不是很好复现的BUG的时候,很麻烦。 unity3d打印出来的日志不会写入本地,有时候移动端开发如果没有log很难定位BUG。所以搭这套框架的时候考虑到了这点,加了日志写入器。
2 代码设计
为了避免频繁写入,文件频繁IO影响程序性能,根据数量策略向用户可写区Append日志数据。好困,想到再写~
LoggerManager.cs代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using YouYou; namespace Myh { public class LoggerManager : ManagerBase, IDisposable { //日志数组 private List<string> m_LogArray; //记录(写入)日志的路径 private string m_LogPath = null; //单个日志文件最大日志数量 private int m_LogMaxCapacity = 500; //当前日志数量(当前日志文件写入的日志数量) private int m_CurrLogCount = 0; //日志缓存的数量(内存中有多少条log会写入磁盘一次,应该叫落地数量吧) private int m_LogBufferMaxNumber = 10; public override void Init() { m_LogArray = new List<string>(); if (string.IsNullOrEmpty(m_LogPath)) { //在可写区(persistent data path 也叫local path)创建日志文件 //文件格式以年-月-日-时-分-秒+Start.txt 命名 m_LogPath = Application.persistentDataPath + "//" + "MYH_LOG_"+DateTime.Now.ToString("yyyy-MM-dd_hh.mm.ss") + "-Start.txt"; } GameEntry.Log(LogCategory.Normal, "LoggerManager.Init LogPath{0}", m_LogPath); } //写入日志,并且检查是否存在日志文件,弱不存在则创建日志文件 private void CreateFile(string pathAndName, string info) { StreamWriter sw; FileInfo fileInfo = new FileInfo(pathAndName); if (!fileInfo.Exists) { //CreateText方法限定了文本文件,只能创建文本文件 //Create应该可以创建任何类型的文件 sw = fileInfo.CreateText(); } else { sw = fileInfo.AppendText(); } //写入一行 sw.WriteLine(info); //关闭流 sw.Close(); //销毁? sw.Dispose(); } //同步日志(写入磁盘) public void SyncLog() { if (!string.IsNullOrEmpty(m_LogPath)) { int iLen = m_LogArray.Count; for (int i = 0; i < iLen; ++i) { //写入日志 CreateFile(m_LogPath, m_LogArray[i]); } ClearLogArray(); } } //附加日志到文件尾部 public void AppendDataToFile(string writerFileData) { if (!string.IsNullOrEmpty(writerFileData)) { m_LogArray.Add(writerFileData); } if (m_LogArray.Count % m_LogBufferMaxNumber == 0) { SyncLog(); } } //写入日志 public void Write(string strWriteFileData, LogType logType) { //判断单个文件写入数量是否超过上限,如果超过上限用当前时间新创建一个日志文件 if (m_CurrLogCount >= m_LogMaxCapacity) { m_LogPath = Application.persistentDataPath + "//" + "MYH_LOG_" + DateTime.Now.ToString("yyyy-MM-dd_hh.mm.ss") + ".txt"; m_CurrLogCount = 0; } ++m_CurrLogCount; if (!string.IsNullOrEmpty(strWriteFileData)) { //拼接写入的时间 strWriteFileData = "[" + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss") + "] [" + logType + "] " + strWriteFileData; AppendDataToFile(strWriteFileData); } } //清理Log数组 private void ClearLogArray() { if (null != m_LogArray) { m_LogArray.Clear(); } } public void Dispose() { m_LogArray.Clear(); } } }
3 测试
设计完之后,最重要的是测试并且验证实现过的功能。在Win32平台测试后,日志可以正常写入到可写区。
测试代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using YouYou; using UnityEngine; public class TestUtils : ITest { private void TestLogger() { GameEntry.Log(LogCategory.Normal, "TestLogger start"); //int iLen = 0; for (int i = 0; i < 400; ++i) { GameEntry.Logger.Write("我是一般Log:" + i.ToString(), LogType.Log); GameEntry.Logger.Write("我是警告Log:" + i.ToString(), LogType.Warning); GameEntry.Logger.Write("我是断言Log:" + i.ToString(), LogType.Assert); GameEntry.Logger.Write("我是错误Log:" + i.ToString(), LogType.Error); GameEntry.Logger.Write("我是异常Log:" + i.ToString(), LogType.Exception); } GameEntry.Log(LogCategory.Normal, "TestLogger end"); } private void TestLog() { GameEntry.Log(LogCategory.Normal, "这是一个Normal测试日志 value:{0} {1}", "xx", 1); GameEntry.Log(LogCategory.Procedure, "这是一个Procedure测试日志 value:{0} {1}", "xx", 2); GameEntry.Log(LogCategory.Proto, "这是一个Proto测试日志 value:{0} {1}", "xx", 3); GameEntry.Log(LogCategory.Resource, "这是一个Resource测试日志 value:{0} {1}", "xx", 4); } public void OnTestStart() { //TestLog(); //TestLogger(); } public void OnTestUpdate() { } }