unity/EditorPlugin/UnityEventLogSender.cs (77 lines of code) (raw):

using System; using JetBrains.Lifetimes; using JetBrains.Rider.Model.Unity; using JetBrains.Rider.Unity.Editor.Utils; using UnityEditor; using UnityEngine; namespace JetBrains.Rider.Unity.Editor { internal static class UnityEventLogSender { private static readonly BoundedSynchronizedQueue<LogEvent> ourDelayedLogEvents = new BoundedSynchronizedQueue<LogEvent>(1000); private static bool ourLogEventsCollectorEnabled; public static void Start(Lifetime lifetime) { ourLogEventsCollectorEnabled = PluginSettings.LogEventsCollectorEnabled; if (!ourLogEventsCollectorEnabled) return; lifetime.Bracket(() => EditorApplication.update += Process, () => EditorApplication.update -= Process); lifetime.Bracket(() => Application.logMessageReceivedThreaded += ApplicationOnLogMessageReceived, ()=> Application.logMessageReceivedThreaded -= ApplicationOnLogMessageReceived); PlayModeStateTracker.Current.Advise(lifetime, _ => { // Work around an issue in Unity 2017.1+ that stops sending log messages to the handler when leaving play mode. // The issue will not be fixed because it might break compatibility of existing workarounds // https://issuetracker.unity3d.com/issues/general-unityengine-dot-application-dot-logmessagereceived-is-not-being-raised-after-exiting-play-mode // Note that although the issue says 2017.4+ it is actually 2017.1 and above. I haven't been able to test 5.x if (!EditorApplication.isPlayingOrWillChangePlaymode) { Application.logMessageReceivedThreaded -= ApplicationOnLogMessageReceived; Application.logMessageReceivedThreaded += ApplicationOnLogMessageReceived; } }); } private static void Process() { // main thread ourLogEventsCollectorEnabled = PluginSettings.LogEventsCollectorEnabled; if (ourLogEventsCollectorEnabled) ProcessQueue(); } private static void ApplicationOnLogMessageReceived(string message, string stackTrace, LogType type) { if (!ourLogEventsCollectorEnabled) // stop collecting, if setting was disabled return; LogEventType eventType; switch (type) { case LogType.Error: case LogType.Exception: eventType = LogEventType.Error; break; case LogType.Warning: eventType = LogEventType.Warning; break; default: eventType = LogEventType.Message; break; } // TODO: How can we tell if the message is from a player? // This mode reflects the editor state, not the fact that it's a play, which is of course in play mode var mode = PlayModeStateTracker.Current.Value == PlayModeState.Stopped ? LogEventMode.Edit : LogEventMode.Play; var ticks = DateTime.UtcNow.Ticks; var evt = new LogEvent(ticks, eventType, mode, message, stackTrace); ourDelayedLogEvents.Enqueue(evt); } private static void ProcessQueue() { if (UnityEditorProtocol.Models.Count > 0) // maybe worth checking Any( with .Lifetime.IsAlive ), but shows up more expensive in Profiler { LogEvent element; while ((element = ourDelayedLogEvents.Dequeue()) != null) { SendLogEvent(element); } } } private static void SendLogEvent(LogEvent logEvent) { foreach (var model in UnityEditorProtocol.Models) model.ConsoleLogging.OnConsoleLogEvent(logEvent); } } }