ReadConsole: stable severity classification and filtering across Unity versions
- Classify severity via stacktrace/message first (LogError/LogWarning/Exception/Assertion), with safe fallback to mode-bit mapping - Fix error/warning/log mapping; treat Exception/Assert as errors for filtering - Return the current console buffer reliably and remove debug spam - No changes outside ReadConsole behaviormain
parent
b179ce1ed8
commit
a40db48132
|
|
@ -16,6 +16,8 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ReadConsole
|
public static class ReadConsole
|
||||||
{
|
{
|
||||||
|
// (Calibration removed)
|
||||||
|
|
||||||
// Reflection members for accessing internal LogEntry data
|
// Reflection members for accessing internal LogEntry data
|
||||||
// private static MethodInfo _getEntriesMethod; // Removed as it's unused and fails reflection
|
// private static MethodInfo _getEntriesMethod; // Removed as it's unused and fails reflection
|
||||||
private static MethodInfo _startGettingEntriesMethod;
|
private static MethodInfo _startGettingEntriesMethod;
|
||||||
|
|
@ -41,6 +43,8 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
);
|
);
|
||||||
if (logEntriesType == null)
|
if (logEntriesType == null)
|
||||||
throw new Exception("Could not find internal type UnityEditor.LogEntries");
|
throw new Exception("Could not find internal type UnityEditor.LogEntries");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Include NonPublic binding flags as internal APIs might change accessibility
|
// Include NonPublic binding flags as internal APIs might change accessibility
|
||||||
BindingFlags staticFlags =
|
BindingFlags staticFlags =
|
||||||
|
|
@ -100,6 +104,9 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
_instanceIdField = logEntryType.GetField("instanceID", instanceFlags);
|
_instanceIdField = logEntryType.GetField("instanceID", instanceFlags);
|
||||||
if (_instanceIdField == null)
|
if (_instanceIdField == null)
|
||||||
throw new Exception("Failed to reflect LogEntry.instanceID");
|
throw new Exception("Failed to reflect LogEntry.instanceID");
|
||||||
|
|
||||||
|
// (Calibration removed)
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -251,16 +258,44 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
// int instanceId = (int)_instanceIdField.GetValue(logEntryInstance);
|
// int instanceId = (int)_instanceIdField.GetValue(logEntryInstance);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(message))
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
continue; // Skip empty messages
|
continue; // Skip empty messages
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Calibration removed)
|
||||||
|
|
||||||
// --- Filtering ---
|
// --- Filtering ---
|
||||||
// Filter by type
|
// Prefer classifying severity from message/stacktrace; fallback to mode bits if needed
|
||||||
LogType currentType = GetLogTypeFromMode(mode);
|
LogType unityType = InferTypeFromMessage(message);
|
||||||
if (!types.Contains(currentType.ToString().ToLowerInvariant()))
|
if (unityType == LogType.Log)
|
||||||
{
|
{
|
||||||
continue;
|
unityType = GetLogTypeFromMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool want;
|
||||||
|
if (types.Contains("all"))
|
||||||
|
{
|
||||||
|
want = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Treat Exception/Assert as errors for filtering convenience
|
||||||
|
if (unityType == LogType.Exception)
|
||||||
|
{
|
||||||
|
want = types.Contains("error") || types.Contains("exception");
|
||||||
|
}
|
||||||
|
else if (unityType == LogType.Assert)
|
||||||
|
{
|
||||||
|
want = types.Contains("error") || types.Contains("assert");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
want = types.Contains(unityType.ToString().ToLowerInvariant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!want) continue;
|
||||||
|
|
||||||
// Filter by text (case-insensitive)
|
// Filter by text (case-insensitive)
|
||||||
if (
|
if (
|
||||||
!string.IsNullOrEmpty(filterText)
|
!string.IsNullOrEmpty(filterText)
|
||||||
|
|
@ -294,7 +329,7 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
default:
|
default:
|
||||||
formattedEntry = new
|
formattedEntry = new
|
||||||
{
|
{
|
||||||
type = currentType.ToString(),
|
type = unityType.ToString(),
|
||||||
message = messageOnly,
|
message = messageOnly,
|
||||||
file = file,
|
file = file,
|
||||||
line = line,
|
line = line,
|
||||||
|
|
@ -350,15 +385,12 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
|
|
||||||
// --- Internal Helpers ---
|
// --- Internal Helpers ---
|
||||||
|
|
||||||
// Mapping from LogEntry.mode bits to LogType enum
|
// Mapping bits from LogEntry.mode. These may vary by Unity version.
|
||||||
// Based on decompiled UnityEditor code or common patterns. Precise bits might change between Unity versions.
|
|
||||||
// See comments below for LogEntry mode bits exploration.
|
|
||||||
// Note: This mapping is simplified and might not cover all edge cases or future Unity versions perfectly.
|
|
||||||
private const int ModeBitError = 1 << 0;
|
private const int ModeBitError = 1 << 0;
|
||||||
private const int ModeBitAssert = 1 << 1;
|
private const int ModeBitAssert = 1 << 1;
|
||||||
private const int ModeBitWarning = 1 << 2;
|
private const int ModeBitWarning = 1 << 2;
|
||||||
private const int ModeBitLog = 1 << 3;
|
private const int ModeBitLog = 1 << 3;
|
||||||
private const int ModeBitException = 1 << 4; // Often combined with Error bits
|
private const int ModeBitException = 1 << 4; // often combined with Error bits
|
||||||
private const int ModeBitScriptingError = 1 << 9;
|
private const int ModeBitScriptingError = 1 << 9;
|
||||||
private const int ModeBitScriptingWarning = 1 << 10;
|
private const int ModeBitScriptingWarning = 1 << 10;
|
||||||
private const int ModeBitScriptingLog = 1 << 11;
|
private const int ModeBitScriptingLog = 1 << 11;
|
||||||
|
|
@ -367,46 +399,59 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
|
|
||||||
private static LogType GetLogTypeFromMode(int mode)
|
private static LogType GetLogTypeFromMode(int mode)
|
||||||
{
|
{
|
||||||
// First, determine the type based on the original logic (most severe first)
|
// Preserve Unity's real type (no remapping); bits may vary by version
|
||||||
LogType initialType;
|
if ((mode & (ModeBitException | ModeBitScriptingException)) != 0) return LogType.Exception;
|
||||||
if (
|
if ((mode & (ModeBitError | ModeBitScriptingError)) != 0) return LogType.Error;
|
||||||
(
|
if ((mode & (ModeBitAssert | ModeBitScriptingAssertion)) != 0) return LogType.Assert;
|
||||||
mode
|
if ((mode & (ModeBitWarning | ModeBitScriptingWarning)) != 0) return LogType.Warning;
|
||||||
& (
|
return LogType.Log;
|
||||||
ModeBitError
|
}
|
||||||
| ModeBitScriptingError
|
|
||||||
| ModeBitException
|
|
||||||
| ModeBitScriptingException
|
|
||||||
)
|
|
||||||
) != 0
|
|
||||||
)
|
|
||||||
{
|
|
||||||
initialType = LogType.Error;
|
|
||||||
}
|
|
||||||
else if ((mode & (ModeBitAssert | ModeBitScriptingAssertion)) != 0)
|
|
||||||
{
|
|
||||||
initialType = LogType.Assert;
|
|
||||||
}
|
|
||||||
else if ((mode & (ModeBitWarning | ModeBitScriptingWarning)) != 0)
|
|
||||||
{
|
|
||||||
initialType = LogType.Warning;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
initialType = LogType.Log;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the observed "one level lower" correction
|
// (Calibration helpers removed)
|
||||||
switch (initialType)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Classifies severity using message/stacktrace content. Works across Unity versions.
|
||||||
|
/// </summary>
|
||||||
|
private static LogType InferTypeFromMessage(string fullMessage)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fullMessage)) return LogType.Log;
|
||||||
|
|
||||||
|
// Fast path: look for explicit Debug API names in the appended stack trace
|
||||||
|
// e.g., "UnityEngine.Debug:LogError (object)" or "LogWarning"
|
||||||
|
if (fullMessage.IndexOf("LogError", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
return LogType.Error;
|
||||||
|
if (fullMessage.IndexOf("LogWarning", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
return LogType.Warning;
|
||||||
|
|
||||||
|
// Exceptions often include the word "Exception" in the first lines
|
||||||
|
if (fullMessage.IndexOf("Exception", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
return LogType.Exception;
|
||||||
|
|
||||||
|
// Unity assertions
|
||||||
|
if (fullMessage.IndexOf("Assertion", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
return LogType.Assert;
|
||||||
|
|
||||||
|
return LogType.Log;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the "one level lower" remapping for filtering, like the old version.
|
||||||
|
/// This ensures compatibility with the filtering logic that expects remapped types.
|
||||||
|
/// </summary>
|
||||||
|
private static LogType GetRemappedTypeForFiltering(LogType unityType)
|
||||||
|
{
|
||||||
|
switch (unityType)
|
||||||
{
|
{
|
||||||
case LogType.Error:
|
case LogType.Error:
|
||||||
return LogType.Warning; // Error becomes Warning
|
return LogType.Warning; // Error becomes Warning
|
||||||
case LogType.Warning:
|
case LogType.Warning:
|
||||||
return LogType.Log; // Warning becomes Log
|
return LogType.Log; // Warning becomes Log
|
||||||
case LogType.Assert:
|
case LogType.Assert:
|
||||||
return LogType.Assert; // Assert remains Assert (no lower level defined)
|
return LogType.Assert; // Assert remains Assert
|
||||||
case LogType.Log:
|
case LogType.Log:
|
||||||
return LogType.Log; // Log remains Log
|
return LogType.Log; // Log remains Log
|
||||||
|
case LogType.Exception:
|
||||||
|
return LogType.Warning; // Exception becomes Warning
|
||||||
default:
|
default:
|
||||||
return LogType.Log; // Default fallback
|
return LogType.Log; // Default fallback
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue