123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- // threaded Debug.Log support (mischa 2022)
- //
- // Editor shows Debug.Logs from different threads.
- // Builds don't show Debug.Logs from different threads.
- //
- // need to hook into logMessageReceivedThreaded to receive them in builds too.
- using System.Collections.Concurrent;
- using System.Threading;
- using UnityEngine;
- namespace Mirror
- {
- public static class ThreadLog
- {
- // queue log messages from threads
- struct LogEntry
- {
- public int threadId;
- public LogType type;
- public string message;
- public string stackTrace;
- public LogEntry(int threadId, LogType type, string message, string stackTrace)
- {
- this.threadId = threadId;
- this.type = type;
- this.message = message;
- this.stackTrace = stackTrace;
- }
- }
- // ConcurrentQueue allocations are fine here.
- // logs allocate anywway.
- static readonly ConcurrentQueue<LogEntry> logs =
- new ConcurrentQueue<LogEntry>();
- // main thread id
- static int mainThreadId;
- #if !UNITY_EDITOR
- // Editor as of Unity 2021 does log threaded messages.
- // only builds don't.
- // do nothing in editor, otherwise we would log twice.
- // before scene load ensures thread logs are all caught.
- // otherwise some component's Awake may be called before we hooked it up.
- // for example, ThreadedTransport's early logs wouldn't be caught.
- [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
- static void Initialize()
- {
- // set main thread id
- mainThreadId = Thread.CurrentThread.ManagedThreadId;
- // receive threaded log calls
- Application.logMessageReceivedThreaded -= OnLog; // remove old first. TODO unnecessary?
- Application.logMessageReceivedThreaded += OnLog;
- // process logs on main thread Update
- NetworkLoop.OnLateUpdate -= OnLateUpdate; // remove old first. TODO unnecessary?
- NetworkLoop.OnLateUpdate += OnLateUpdate;
- // log for debugging
- Debug.Log("ThreadLog initialized.");
- }
- #endif
- static bool IsMainThread() =>
- Thread.CurrentThread.ManagedThreadId == mainThreadId;
- // callback runs on the same thread where the Debug.Log is called.
- // we can use this to buffer messages for main thread here.
- static void OnLog(string message, string stackTrace, LogType type)
- {
- // only enqueue messages from other threads.
- // otherwise OnLateUpdate main thread logging would be enqueued
- // as well, causing deadlock.
- if (IsMainThread()) return;
- // queue for logging from main thread later
- logs.Enqueue(new LogEntry(Thread.CurrentThread.ManagedThreadId, type, message, stackTrace));
- }
- static void OnLateUpdate()
- {
- // process queued logs on main thread
- while (logs.TryDequeue(out LogEntry entry))
- {
- switch (entry.type)
- {
- case LogType.Log:
- Debug.Log($"[T{entry.threadId}] {entry.message}\n{entry.stackTrace}");
- break;
- case LogType.Warning:
- Debug.LogWarning($"[T{entry.threadId}] {entry.message}\n{entry.stackTrace}");
- break;
- case LogType.Error:
- Debug.LogError($"[T{entry.threadId}] {entry.message}\n{entry.stackTrace}");
- break;
- case LogType.Exception:
- Debug.LogError($"[T{entry.threadId}] {entry.message}\n{entry.stackTrace}");
- break;
- case LogType.Assert:
- Debug.LogAssertion($"[T{entry.threadId}] {entry.message}\n{entry.stackTrace}");
- break;
- }
- }
- }
- }
- }
|