diff --git a/Documentation~/zh/changelog.md b/Documentation~/zh/changelog.md index 9b2f44b1..cf4b0f9e 100644 --- a/Documentation~/zh/changelog.md +++ b/Documentation~/zh/changelog.md @@ -73,6 +73,7 @@ slug: /changelog 日志详情: +* (2023.07.25) 增加`XLog`日志系统 * (2023.07.18) 完善`Pie`饼图的交互动画效果 * (2023.07.14) 增加`Animation`的`Interaction`交互动画配置支持 * (2023.07.11) 增加`Animation`的`Addition`新增动画配置支持 diff --git a/Runtime/XLog.meta b/Runtime/XLog.meta new file mode 100644 index 00000000..998fed38 --- /dev/null +++ b/Runtime/XLog.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a4ed57531ebf43999c449f6aa58595c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/XLog/XLog.cs b/Runtime/XLog/XLog.cs new file mode 100644 index 00000000..3380aadc --- /dev/null +++ b/Runtime/XLog/XLog.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// + /// Log system. Used to output logs with date and log type, support output to file, support custom output log type. + /// |日志系统。用于输出带日期和日志类型的日志,支持输出到文件,支持自定义输出的日志类型。 + /// + public class XLog : MonoBehaviour + { + public const int ALL = 0; + public const int WARNING = 1; + public const int DEBUG = 2; + public const int INFO = 3; + public const int PROTO = 4; + public const int VITAL = 5; + public const int ERROR = 6; + public const int EXCEPTION = 7; + + private const int MAX_ERROR_LOG = 20; + + public static bool isReportBug = false; + public static bool isOutputLog = false; + public static bool isUploadLog = false; + public static bool isCloseOutLog = false; + + public static int errorCount = 0; + public static int exceptCount = 0; + public static int uploadTick = 20; + public static int reportTick = 10; + + private static bool initFileSuccess = false; + private static bool[] levelList = new bool[] { true, true, true, true, true, true, true, true }; + private static List writeList = new List(); + private static float uploadTime = 0; + private static float reportTime = 0; + + private string outpath; + private StreamWriter writer; + private string[] temp; + + public int logCount = 0; + public static List errorList = new List(); + private static object m_Lock = new object(); + + private static XLog m_Instance; + public static XLog Instance + { + get + { + // if (m_Instance == null) + // { + // GameObject go = new GameObject("XLog"); + // m_Instance = go.AddComponent(); + // DontDestroyOnLoad(go); + // } + return m_Instance; + } + } + + void Awake() + { + if (m_Instance != null) + { + Destroy(gameObject); + return; + } + m_Instance = this; + InitLogFile(); + // Application.logMessageReceived += HandleLog; + Application.logMessageReceivedThreaded += HandleLog; + } + + void OnDestroy() + { + if (writer != null) + { + writer.Close(); + writer.Dispose(); + } + // Application.logMessageReceived -= HandleLog; + Application.logMessageReceivedThreaded -= HandleLog; + } + + void Update() + { + uploadTime += Time.deltaTime; + reportTime += Time.deltaTime; + lock (m_Lock) + { + if (writeList.Count > 0) + { + logCount = writeList.Count; + if (!initFileSuccess) + { + writeList.Clear(); + return; + } + try + { + temp = writeList.ToArray(); + int count = 0; + foreach (var str in temp) + { + count++; + writer.WriteLine(str); + writeList.Remove(str); + if (count > 10) break; + } + writer.Flush(); + } + catch (Exception e) + { + initFileSuccess = false; + //Application.logMessageReceived -= HandleLog; + Application.logMessageReceivedThreaded -= HandleLog; + UnityEngine.Debug.LogError("write outlog.txt error:" + e.Message); + } + } + } + } + + private void InitLogFile() + { + ClearAllLog(); + XLog.EnableLog(ALL); + if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) + { + XLog.ClearAllLog(); + XLog.EnableLog(VITAL); + XLog.EnableLog(ERROR); + XLog.isReportBug = true; + XLog.isUploadLog = true; + } + else + { + XLog.isUploadLog = false; + XLog.isReportBug = false; + } + outpath = GetLogOutputPath(); + try + { + if (File.Exists(outpath)) + { + File.Delete(outpath); + } + writer = new StreamWriter(outpath, false, Encoding.UTF8); + writer.WriteLine(GetNowTime() + "init file success!!"); + UnityEngine.Debug.Log(GetNowTime() + "init file success:" + outpath); + writer.Flush(); + initFileSuccess = true; + } + catch (Exception e) + { + initFileSuccess = false; + Application.logMessageReceived -= HandleLog; + UnityEngine.Debug.LogError("write outlog.txt error:" + e.Message); + } + } + + private static string GetLogOutputPath() + { +#if UNITY_EDITOR + string path = Application.dataPath + "/../outlog.txt"; +#else + string path = Application.persistentDataPath + "/outlog.txt"; + if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer) + { + path = Application.persistentDataPath + "/outlog.txt"; + } + else + { + path = Application.dataPath + "/../outlog.txt"; + } +#endif + return path; + } + + private void HandleLog(string logString, string stackTrace, LogType type) + { + lock (m_Lock) + { + if (!initFileSuccess) return; + int index = logString.IndexOf("stack traceback"); + if (index > 0) + { + string log = logString.Substring(0, index); + string trace = logString.Substring(index, logString.Length - index); + logString = log; + stackTrace = trace; + } + + if (type == LogType.Log) + { + } + else if (type == LogType.Error) + { + if (logString.IndexOf("LUA ERROR") > 0 || logString.IndexOf("stack traceback") > 0) exceptCount++; + else errorCount++; + + writeList.Add(logString); + //writeList.Add(stackTrace + "\n"); + + if (errorList.Count >= MAX_ERROR_LOG) + { + errorList.RemoveAt(1); + } + + if (errorList.Count < MAX_ERROR_LOG) + { + errorList.Add(logString); + // errorList.Add(stackTrace + "\n"); + } + } + else if (type == LogType.Exception) + { + exceptCount++; + + writeList.Add(logString); + writeList.Add(stackTrace + "\n"); + + if (errorList.Count >= MAX_ERROR_LOG) + { + errorList.RemoveAt(1); + } + + if (errorList.Count < MAX_ERROR_LOG) + { + errorList.Add(logString); + errorList.Add(stackTrace + "\n"); + } + } + } + } + + public static void FlushLog() + { + var instance = XLog.Instance; + if (instance != null && instance.writer != null) + { + for (int i = 0; i < writeList.Count; i++) + { + instance.writer.WriteLine(writeList[i]); + } + instance.writer.Flush(); + writeList.Clear(); + } + } + + public static void EnableLog(int logType) + { + if (logType < 0 || logType >= levelList.Length) return; + levelList[logType] = true; + } + + public static void ClearAllLog() + { + for (int i = 0; i < levelList.Length; i++) + { + levelList[i] = false; + } + } + + public static bool CanLog(int level) + { + if (level < 0 || level >= levelList.Length) return false; + return levelList[level] || levelList[0]; + } + + public static void Log(string log) + { + Debug(log); + } + + public static void LogError(string log) + { + Error(log); + } + + public static void LogWarning(string log) + { + Warning(log); + } + + public static void Debug(string log) + { + if (!CanLog(DEBUG)) return; + UnityEngine.Debug.Log(GetNowTime() + "[DEBUG]\t" + log); + } + + public static void Vital(string log) + { + if (!CanLog(INFO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[VITAL]\t" + log); + } + + public static void Info(string log) + { + if (!CanLog(INFO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[INFO]\t" + log); + } + + public static void Proto(string log) + { + if (!CanLog(PROTO)) return; + UnityEngine.Debug.Log(GetNowTime() + "[PROTO]\t" + log); + } + + public static void Warning(string log) + { + if (!CanLog(WARNING)) return; + UnityEngine.Debug.LogWarning(GetNowTime() + "[WARN]\t" + log); + } + + public static void Error(string log) + { + if (!CanLog(ERROR)) return; + UnityEngine.Debug.LogError(GetNowTime() + "[ERROR]\t" + log); + } + + public static string GetNowTime(string formatter = null) + { + DateTime now = DateTime.Now; + if (formatter == null) + return now.ToString("[HH:mm:ss fff]", DateTimeFormatInfo.InvariantInfo); + else + return now.ToString(formatter, DateTimeFormatInfo.InvariantInfo); + } + + public static ulong GetTimestamp() + { + return (ulong)(DateTime.Now - new DateTime(190, 1, 1, 0, 0, 0, 0)).TotalSeconds; + } + } +} diff --git a/Runtime/XLog/XLog.cs.meta b/Runtime/XLog/XLog.cs.meta new file mode 100644 index 00000000..79a55607 --- /dev/null +++ b/Runtime/XLog/XLog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: baf125f6000464daeb59d4c183eed941 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: