Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 96 additions & 41 deletions UnityMcpBridge/Editor/Tools/ReadConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace UnityMcpBridge.Editor.Tools
/// </summary>
public static class ReadConsole
{
// (Calibration removed)

// Reflection members for accessing internal LogEntry data
// private static MethodInfo _getEntriesMethod; // Removed as it's unused and fails reflection
private static MethodInfo _startGettingEntriesMethod;
Expand All @@ -41,6 +43,8 @@ static ReadConsole()
);
if (logEntriesType == null)
throw new Exception("Could not find internal type UnityEditor.LogEntries");



// Include NonPublic binding flags as internal APIs might change accessibility
BindingFlags staticFlags =
Expand Down Expand Up @@ -100,6 +104,9 @@ static ReadConsole()
_instanceIdField = logEntryType.GetField("instanceID", instanceFlags);
if (_instanceIdField == null)
throw new Exception("Failed to reflect LogEntry.instanceID");

// (Calibration removed)

}
catch (Exception e)
{
Expand Down Expand Up @@ -251,16 +258,38 @@ bool includeStacktrace
// int instanceId = (int)_instanceIdField.GetValue(logEntryInstance);

if (string.IsNullOrEmpty(message))
{
continue; // Skip empty messages
}

// (Calibration removed)

// --- Filtering ---
// Filter by type
LogType currentType = GetLogTypeFromMode(mode);
if (!types.Contains(currentType.ToString().ToLowerInvariant()))
// Prefer classifying severity from message/stacktrace; fallback to mode bits if needed
LogType unityType = InferTypeFromMessage(message);
bool isExplicitDebug = IsExplicitDebugLog(message);
if (!isExplicitDebug && unityType == LogType.Log)
{
continue;
unityType = GetLogTypeFromMode(mode);
}

bool want;
// 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)
if (
!string.IsNullOrEmpty(filterText)
Expand Down Expand Up @@ -294,7 +323,7 @@ bool includeStacktrace
default:
formattedEntry = new
{
type = currentType.ToString(),
type = unityType.ToString(),
message = messageOnly,
file = file,
line = line,
Expand Down Expand Up @@ -350,15 +379,12 @@ bool includeStacktrace

// --- Internal Helpers ---

// Mapping from LogEntry.mode bits to LogType enum
// 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.
// Mapping bits from LogEntry.mode. These may vary by Unity version.
private const int ModeBitError = 1 << 0;
private const int ModeBitAssert = 1 << 1;
private const int ModeBitWarning = 1 << 2;
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 ModeBitScriptingWarning = 1 << 10;
private const int ModeBitScriptingLog = 1 << 11;
Expand All @@ -367,46 +393,75 @@ bool includeStacktrace

private static LogType GetLogTypeFromMode(int mode)
{
// First, determine the type based on the original logic (most severe first)
LogType initialType;
if (
(
mode
& (
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;
}
// Preserve Unity's real type (no remapping); bits may vary by version
if ((mode & (ModeBitException | ModeBitScriptingException)) != 0) return LogType.Exception;
if ((mode & (ModeBitError | ModeBitScriptingError)) != 0) return LogType.Error;
if ((mode & (ModeBitAssert | ModeBitScriptingAssertion)) != 0) return LogType.Assert;
if ((mode & (ModeBitWarning | ModeBitScriptingWarning)) != 0) return LogType.Warning;
return LogType.Log;
}

// (Calibration helpers removed)

/// <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;

// Compiler diagnostics (C#): "warning CSxxxx" / "error CSxxxx"
if (fullMessage.IndexOf(" warning CS", StringComparison.OrdinalIgnoreCase) >= 0
|| fullMessage.IndexOf(": warning CS", StringComparison.OrdinalIgnoreCase) >= 0)
return LogType.Warning;
if (fullMessage.IndexOf(" error CS", StringComparison.OrdinalIgnoreCase) >= 0
|| fullMessage.IndexOf(": error CS", StringComparison.OrdinalIgnoreCase) >= 0)
return LogType.Error;

// Exceptions (avoid misclassifying compiler diagnostics)
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;
}

// Apply the observed "one level lower" correction
switch (initialType)
private static bool IsExplicitDebugLog(string fullMessage)
{
if (string.IsNullOrEmpty(fullMessage)) return false;
if (fullMessage.IndexOf("Debug:Log (", StringComparison.OrdinalIgnoreCase) >= 0) return true;
if (fullMessage.IndexOf("UnityEngine.Debug:Log (", StringComparison.OrdinalIgnoreCase) >= 0) return true;
return false;
}

/// <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:
return LogType.Warning; // Error becomes Warning
case LogType.Warning:
return LogType.Log; // Warning becomes Log
case LogType.Assert:
return LogType.Assert; // Assert remains Assert (no lower level defined)
return LogType.Assert; // Assert remains Assert
case LogType.Log:
return LogType.Log; // Log remains Log
case LogType.Exception:
return LogType.Warning; // Exception becomes Warning
default:
return LogType.Log; // Default fallback
}
Expand Down