using System; using System.Text; using System.Diagnostics; //NOTE: *** methods without 'Safe' postfix ARE NOT thread safe, // use them only from the Unity main thread *** public static class Log { public static int frame_count = 0; public static int tick_count = 0; public static string CLIENT_VERSION = "1"; public static string CLIENT_ERR_URL = ""; public static System.Func PreprocessRemoteLog; static StringBuilder history = new StringBuilder(); public static System.Action on_info = null; public static System.Action on_debug = null; public static System.Action on_error = null; public static System.Action on_warn = null; public static System.Action on_fatal = null; #if UNITY_EDITOR static int main_thread_id; #endif static StringBuilder MakeMsg(object message) { var now = DateTime.Now; StringBuilder sb = new StringBuilder(64); sb.Append(now.Hour); sb.Append(":"); sb.Append(now.Minute); sb.Append(":"); sb.Append(now.Second); sb.Append("."); sb.Append(now.Millisecond); sb.Append(" "); sb.Append(frame_count); sb.Append("("); sb.Append(tick_count); sb.Append(") "); sb.Append(message?.ToString()); sb.Append("\n"); return sb; } static string MakeMsgAndAddToHistory(object message) { var txt = MakeMsg(message); #if UNITY_EDITOR CheckThreadId(message); #endif history.Append(txt); return txt.ToString(); } #if UNITY_EDITOR static void CheckThreadId(object message) { int current_id = System.Threading.Thread.CurrentThread.ManagedThreadId; if(main_thread_id != 0 && current_id != main_thread_id) { UnityEngine.Debug.LogError($"Unsafe log method called from a background thread - use log method with *Safe suffix here. Message: '{message}'"); return; } main_thread_id = current_id; } #endif public static string GetHistory() { return history.ToString(); } public static void Info(object message) { var res = MakeMsgAndAddToHistory(message); UnityEngine.Debug.Log(res); if(on_info != null) on_info(res); } //NOTE: thread safe public static void InfoSafe(object message) { var res = MakeMsg(message).ToString(); UnityEngine.Debug.Log(res); } #if STRIP_LOGS [Conditional("COND_STRIP_LOGS")] public static void Debug(object message) {} #else public static void Debug(object message) { var res = MakeMsgAndAddToHistory(message); UnityEngine.Debug.Log(res); if(on_debug != null) on_debug(res); } #endif #if STRIP_LOGS [Conditional("COND_STRIP_LOGS")] public static void DebugSafe(object message) {} #else //NOTE: thread safe public static void DebugSafe(object message) { var res = MakeMsg(message).ToString(); UnityEngine.Debug.Log(res); } #endif public static void Error(object message) { var res = MakeMsgAndAddToHistory(message); UnityEngine.Debug.LogError(res); on_error?.Invoke(res); } //NOTE: thread safe public static void ErrorSafe(object message) { var res = MakeMsg(message).ToString(); UnityEngine.Debug.LogError(res); } #if STRIP_LOGS [Conditional("COND_STRIP_LOGS")] public static void Warn(object message) {} #else public static void Warn(object message) { var res = MakeMsgAndAddToHistory(message); UnityEngine.Debug.LogWarning(res); if(on_warn != null) on_warn(res); } #endif #if STRIP_LOGS [Conditional("COND_STRIP_LOGS")] public static void WarnSafe(object message) {} #else //NOTE: thread safe public static void WarnSafe(object message) { var res = MakeMsg(message).ToString(); UnityEngine.Debug.LogWarning(res); } #endif }