467 lines
12 KiB
C#
467 lines
12 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using UnityEngine;
|
|
|
|
namespace DebuggingEssentials
|
|
{
|
|
[DefaultExecutionOrder(-5000000)]
|
|
public class HtmlDebug : MonoBehaviour
|
|
{
|
|
public static HtmlDebug instance;
|
|
|
|
public static Stopwatch timeSinceStartup = new Stopwatch();
|
|
|
|
public static float frameTime;
|
|
|
|
public static int currentFrame = 0;
|
|
|
|
public static int mainThreadId;
|
|
|
|
[Tooltip("Automatically deletes build Log files that are older than x days.")]
|
|
public bool deleteBuildLogs = true;
|
|
|
|
public int deleteBuildLogsAfterDays = 7;
|
|
|
|
[Tooltip("Open the HTML Log file manually by code if you want to give your own path and file name")]
|
|
public bool openLogManually;
|
|
|
|
[Tooltip("Only shows the first line of the stack trace with Debub.Log.\n\n(Only if Stack Trace is enabled in the Player Settings)")]
|
|
public bool normalLogOnlyFirstLineStackTrace = true;
|
|
|
|
public int titleFontSize = 30;
|
|
|
|
public int frameFontSize = 16;
|
|
|
|
public int logFontSize = 15;
|
|
|
|
public int stackFontSize = 13;
|
|
|
|
[NonSerialized]
|
|
public string logPathIncludingFilename;
|
|
|
|
[NonSerialized]
|
|
public string logPath;
|
|
|
|
private FastList<Log> logsThread = new FastList<Log>();
|
|
|
|
private FastList<Log> logs = new FastList<Log>();
|
|
|
|
private int lastFrame = -1;
|
|
|
|
private bool isLogEnabled;
|
|
|
|
private bool isEditor;
|
|
|
|
private bool isDebugBuild;
|
|
|
|
private StackTraceLogType logStackTraceLogType;
|
|
|
|
private StackTraceLogType assertStackTraceLogType;
|
|
|
|
private StackTraceLogType warningStackTraceLogType;
|
|
|
|
private StackTraceLogType errorStackTraceLogType;
|
|
|
|
private StackTraceLogType exceptionStackTraceLogType;
|
|
|
|
private string frameFontSizeString;
|
|
|
|
private string stackFontSizeString;
|
|
|
|
private string logFontSizeString;
|
|
|
|
private WaitCallback logCallBack;
|
|
|
|
private StreamWriter sw;
|
|
|
|
private bool isLogging;
|
|
|
|
private bool updateLogCallFromMainThread = true;
|
|
|
|
private bool isQuitting;
|
|
|
|
private const int skipFrames = 6;
|
|
|
|
public static void ResetStatic()
|
|
{
|
|
frameTime = 0f;
|
|
currentFrame = 0;
|
|
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (instance != null)
|
|
{
|
|
UnityEngine.Debug.LogError("You have more than 1 HtmlDebug GameObject, make sure to only use 1");
|
|
return;
|
|
}
|
|
instance = this;
|
|
logCallBack = WriteLogs;
|
|
if (!openLogManually)
|
|
{
|
|
OpenLog(Helper.GetConsoleLogPath());
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (instance == this)
|
|
{
|
|
instance = null;
|
|
}
|
|
if (!isQuitting)
|
|
{
|
|
CloseLog("OnDestroy");
|
|
}
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
isQuitting = true;
|
|
CloseLog("OnApplicationQuit");
|
|
}
|
|
|
|
private void CloseLog(string closeReason)
|
|
{
|
|
while (isLogging)
|
|
{
|
|
}
|
|
updateLogCallFromMainThread = false;
|
|
UnityDebugLogThread(closeReason, string.Empty, LogType.Log);
|
|
WriteLogs(logCallBack);
|
|
CloseLog();
|
|
}
|
|
|
|
public static int GetThreadId()
|
|
{
|
|
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
|
|
if (managedThreadId != mainThreadId)
|
|
{
|
|
return managedThreadId;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private void DeleteBuildLogsAfterXDays(string logPath)
|
|
{
|
|
if (!deleteBuildLogs)
|
|
{
|
|
return;
|
|
}
|
|
string[] files = Directory.GetFiles(logPath);
|
|
for (int i = 0; i < files.Length; i++)
|
|
{
|
|
FileInfo fileInfo = new FileInfo(files[i]);
|
|
if (fileInfo.LastWriteTime < DateTime.Now.AddDays(-deleteBuildLogsAfterDays) && (fileInfo.Name.Contains(".html") || fileInfo.Name.Contains(".log")))
|
|
{
|
|
fileInfo.Delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void OpenLog(string logPathIncludingFileName)
|
|
{
|
|
instance.OpenLogInternal(logPathIncludingFileName);
|
|
}
|
|
|
|
private void OpenLogInternal(string logPathIncludingFileName)
|
|
{
|
|
if (isLogEnabled)
|
|
{
|
|
UnityEngine.Debug.LogError("Html Debug Logs is already opened. Make sure if you call OpenLog manually to enable 'Open Log Manually' in the HTML Debug Inspector");
|
|
return;
|
|
}
|
|
isLogEnabled = true;
|
|
logStackTraceLogType = Application.GetStackTraceLogType(LogType.Log);
|
|
assertStackTraceLogType = Application.GetStackTraceLogType(LogType.Assert);
|
|
warningStackTraceLogType = Application.GetStackTraceLogType(LogType.Warning);
|
|
errorStackTraceLogType = Application.GetStackTraceLogType(LogType.Error);
|
|
exceptionStackTraceLogType = Application.GetStackTraceLogType(LogType.Exception);
|
|
isEditor = Application.isEditor;
|
|
isDebugBuild = UnityEngine.Debug.isDebugBuild;
|
|
frameFontSizeString = "font-size:" + frameFontSize + "px;\">";
|
|
stackFontSizeString = "font-size:" + stackFontSize + "px;\">";
|
|
logFontSizeString = "font-size:" + logFontSize + "px;\">";
|
|
try
|
|
{
|
|
logPathIncludingFileName = logPathIncludingFileName.Replace(".log", ".html");
|
|
logPathIncludingFilename = logPathIncludingFileName;
|
|
logPath = logPathIncludingFileName.Substring(0, logPathIncludingFileName.LastIndexOf("/"));
|
|
Directory.CreateDirectory(Path.GetDirectoryName(logPathIncludingFileName));
|
|
sw = new StreamWriter(logPathIncludingFileName, append: false, Encoding.ASCII, 8192);
|
|
sw.Write("<html>");
|
|
sw.Write("<body style=\"font-family:consolas; font-size:100%; background-color:#1E1E1E;\" >");
|
|
sw.Write("<strong><span style=\"color:#DCDCDC;");
|
|
sw.Write("font-size:");
|
|
sw.Write(titleFontSize.ToString());
|
|
sw.Write("px;\">");
|
|
sw.Write(Helper.GetApplicationInfo());
|
|
sw.Write("</span></strong><br><br><br>");
|
|
sw.Write("<span style=\"color:#DCDCDC;");
|
|
sw.Write(frameFontSizeString);
|
|
sw.Write("Unity version ");
|
|
sw.Write(Application.unityVersion);
|
|
sw.Write("</span><ul>");
|
|
Application.logMessageReceivedThreaded += UnityDebugLogThread;
|
|
DeleteBuildLogsAfterXDays(logPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
UnityEngine.Debug.LogError(ex.ToString());
|
|
}
|
|
}
|
|
|
|
private void CloseLog()
|
|
{
|
|
isLogEnabled = false;
|
|
Application.logMessageReceivedThreaded -= UnityDebugLogThread;
|
|
if (sw != null)
|
|
{
|
|
sw.Write("</ul></body>");
|
|
sw.Write("</html>");
|
|
sw.Close();
|
|
}
|
|
}
|
|
|
|
private bool UseStackTrace(LogType logType)
|
|
{
|
|
if (logType == LogType.Log && logStackTraceLogType != StackTraceLogType.None)
|
|
{
|
|
return true;
|
|
}
|
|
if (logType == LogType.Assert && assertStackTraceLogType != StackTraceLogType.None)
|
|
{
|
|
return true;
|
|
}
|
|
if (logType == LogType.Warning && warningStackTraceLogType != StackTraceLogType.None)
|
|
{
|
|
return true;
|
|
}
|
|
if (logType == LogType.Error && errorStackTraceLogType != StackTraceLogType.None)
|
|
{
|
|
return true;
|
|
}
|
|
if (logType == LogType.Exception && exceptionStackTraceLogType != StackTraceLogType.None)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void UpdateLogs()
|
|
{
|
|
if (!isLogging && logsThread.Count != 0)
|
|
{
|
|
isLogging = true;
|
|
ThreadPool.QueueUserWorkItem(logCallBack);
|
|
}
|
|
}
|
|
|
|
private void WriteLogs(object callback)
|
|
{
|
|
try
|
|
{
|
|
logs.GrabListThreadSafe(logsThread, fastClear: true);
|
|
for (int i = 0; i < logs.Count; i++)
|
|
{
|
|
Log log = logs.items[i];
|
|
UnityDebugLog(log.logString, log.stackTraceString, log.logType, log.isMainThread, log.threadId, log.stackTrace);
|
|
}
|
|
logs.FastClear();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
UnityEngine.Debug.LogException(exception);
|
|
}
|
|
finally
|
|
{
|
|
isLogging = false;
|
|
}
|
|
}
|
|
|
|
private void UnityDebugLogThread(string logString, string stackTraceString, LogType logType)
|
|
{
|
|
if (isLogEnabled && sw != null)
|
|
{
|
|
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
|
|
bool flag = managedThreadId == mainThreadId;
|
|
bool flag2 = UseStackTrace(logType);
|
|
logsThread.AddThreadSafe(new Log
|
|
{
|
|
logString = logString,
|
|
stackTraceString = stackTraceString,
|
|
logType = logType,
|
|
isMainThread = flag,
|
|
threadId = managedThreadId,
|
|
stackTrace = (flag2 ? new StackTrace(6, fNeedFileInfo: true) : null)
|
|
});
|
|
if (flag && updateLogCallFromMainThread)
|
|
{
|
|
UpdateLogs();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UnityDebugLog(string logString, string stackTraceString, LogType logType, bool isMainThread, int threadId = -1, StackTrace stackTrace = null, EntryType2 entryType = EntryType2.Unity, bool closeLi = true)
|
|
{
|
|
if (!isLogEnabled || sw == null)
|
|
{
|
|
return;
|
|
}
|
|
if (currentFrame != lastFrame)
|
|
{
|
|
lastFrame = currentFrame;
|
|
sw.Write("</ul><br><strong><span style=\"color:#508EA1;");
|
|
sw.Write(frameFontSizeString);
|
|
sw.Write("[Frame ");
|
|
sw.Write(currentFrame.ToString("D6"));
|
|
sw.Write("][Time ");
|
|
sw.Write(Helper.ToTimeFormat(frameTime));
|
|
sw.Write("] -----------------------------------------------------------------------------------------------");
|
|
sw.Write("</span></strong><ul>");
|
|
}
|
|
switch (logType)
|
|
{
|
|
case LogType.Error:
|
|
sw.Write("<li style =\"color:#FF0000;");
|
|
break;
|
|
case LogType.Exception:
|
|
sw.Write("<li style =\"color:#9D00FF;");
|
|
break;
|
|
case LogType.Warning:
|
|
sw.Write("<li style =\"color:#FFFF00;");
|
|
break;
|
|
default:
|
|
if (entryType == EntryType2.Unity)
|
|
{
|
|
sw.Write("<li style =\"color:#F0F0F0;");
|
|
break;
|
|
}
|
|
if (!closeLi)
|
|
{
|
|
sw.Write("<li style =\"color:#");
|
|
}
|
|
else
|
|
{
|
|
sw.Write("<span style =\"color:#");
|
|
}
|
|
switch (entryType)
|
|
{
|
|
case EntryType2.Command:
|
|
sw.Write(ColorUtility.ToHtmlStringRGB(Color.green) + ";");
|
|
break;
|
|
case EntryType2.CommandResult:
|
|
sw.Write(ColorUtility.ToHtmlStringRGB(Helper.colCommandResult) + ";");
|
|
break;
|
|
case EntryType2.CommandFault:
|
|
sw.Write(ColorUtility.ToHtmlStringRGB(Helper.colCommandResultFailed) + ";");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
sw.Write(logFontSizeString);
|
|
if (entryType != EntryType2.Unity)
|
|
{
|
|
sw.Write("<strong>");
|
|
}
|
|
sw.Write(logString);
|
|
if (entryType != EntryType2.Unity)
|
|
{
|
|
sw.Write("</strong>");
|
|
}
|
|
if (!isMainThread)
|
|
{
|
|
sw.Write(" <i>[Thread ");
|
|
sw.Write(threadId);
|
|
sw.Write("]</i>");
|
|
}
|
|
sw.Write("</br>");
|
|
string[] array = null;
|
|
if (logType == LogType.Exception)
|
|
{
|
|
sw.Write("<span style=\"color:#7D00DF;");
|
|
sw.Write(stackFontSizeString);
|
|
array = stackTraceString.Split('\n');
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
sw.Write(array[i]);
|
|
if (i < array.Length - 1)
|
|
{
|
|
sw.Write("<br>");
|
|
}
|
|
}
|
|
sw.Write("</span>");
|
|
}
|
|
else if (entryType == EntryType2.Unity && UseStackTrace(logType))
|
|
{
|
|
switch (logType)
|
|
{
|
|
case LogType.Error:
|
|
sw.Write("<span style=\"color:#A00000;");
|
|
break;
|
|
case LogType.Warning:
|
|
sw.Write("<span style=\"color:#A0A000;");
|
|
break;
|
|
default:
|
|
sw.Write("<span style=\"color:#909090;");
|
|
break;
|
|
}
|
|
sw.Write(stackFontSizeString);
|
|
int num = ((logType == LogType.Log && normalLogOnlyFirstLineStackTrace) ? 1 : stackTrace.FrameCount);
|
|
if ((bool)RuntimeConsole.instance)
|
|
{
|
|
array = new string[num];
|
|
}
|
|
for (int j = 0; j < num; j++)
|
|
{
|
|
StackFrame frame = stackTrace.GetFrame(j);
|
|
if (frame == null)
|
|
{
|
|
continue;
|
|
}
|
|
MethodBase method = frame.GetMethod();
|
|
string text = method.DeclaringType.Name;
|
|
sw.Write(text);
|
|
sw.Write(".");
|
|
sw.Write(method);
|
|
if (isEditor || isDebugBuild)
|
|
{
|
|
sw.Write(":");
|
|
int fileLineNumber = frame.GetFileLineNumber();
|
|
sw.Write(fileLineNumber);
|
|
if ((bool)RuntimeConsole.instance)
|
|
{
|
|
array[j] = text + "." + method?.ToString() + ":" + fileLineNumber;
|
|
}
|
|
}
|
|
else if ((bool)RuntimeConsole.instance)
|
|
{
|
|
array[j] = text + "." + method;
|
|
}
|
|
sw.Write("<br>");
|
|
}
|
|
sw.Write("</style></span>");
|
|
}
|
|
if (entryType == EntryType2.Unity && (bool)RuntimeConsole.instance)
|
|
{
|
|
RuntimeConsole.Log(logString, array, logType, Color.white, threadId);
|
|
}
|
|
if (closeLi)
|
|
{
|
|
sw.Write("</li>");
|
|
sw.Write("<span style=\"font-size:9px;\"><br></span>");
|
|
}
|
|
else
|
|
{
|
|
sw.Write("</span>");
|
|
}
|
|
sw.Flush();
|
|
}
|
|
}
|
|
}
|