-
Notifications
You must be signed in to change notification settings - Fork 116
feat: Add search_console_logs tool for Unity log searching #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add search_console_logs tool for Unity log searching #49
Conversation
…ementation - Add ConsoleLogServiceType enum with EventBased and Unity6Enhanced options - Implement ConsoleLogsServiceUnity6 using internal LogEntries API for better reliability - Add command line argument support (-mcpConsoleLogService) for runtime switching - Update McpUnityServer to dynamically select service based on settings and Unity version - Add UI controls in McpUnityEditorWindow with warning dialogs for internal API usage - Default to safe EventBased implementation, Unity6Enhanced only when explicitly selected - Add clear startup logging to identify which implementation is active 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
…ered values - Create LogEntryModeFlags.cs with Unity's internal mode flag constants - Add support for observed mode values from debugging: * Compiler warnings: 266240 (0x41000) * Compiler errors: 272384 (0x42800) * Shader errors: 262212 (0x40044) * Runtime warnings: 8405504 (0x804200) * Runtime errors: 8405248 (0x804100) - Update Unity6InternalAPIReference.md with complete documentation - Add conditional debug logging for future mode value discovery - Improve error/warning classification accuracy in Unity 6 implementation This enables proper filtering of shader errors, compile errors, and runtime exceptions that were previously misclassified as "Log" type. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Removed GetEffectiveConsoleLogService() method - Console log service now only configurable via Unity Editor settings UI - Simplified configuration as command-line args are not practical for Unity Hub users 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Keep file locally but stop tracking it in git 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Add SearchConsoleLogsTool.cs with keyword/regex search support - Support case-sensitive/insensitive search with logType filtering - Include pagination (offset/limit) and optional stack trace inclusion - Implement both C# (Unity) and TypeScript (Node.js) components - Register tool in McpUnityServer and Node.js MCP server - Remove previous resource-based implementation in favor of tool-based approach - Add comprehensive search capabilities: - Keyword search with partial matching - Regular expression pattern matching - Log type filtering (error/warning/info) - Configurable case sensitivity - Stack trace inclusion/exclusion for token optimization - Pagination support for large result sets 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
📝 WalkthroughWalkthroughThis update introduces advanced search functionality for Unity console logs, supporting keyword and regex filtering, log type selection, case sensitivity, and pagination. It adds a Unity 6-specific log service using internal APIs, a new log mode flag utility, UI for selecting the log service, and integrates the search tool into both the Unity and Node.js servers. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as MCP Unity Editor Window
participant Settings as McpUnitySettings
participant Server as McpUnityServer
participant LogService as IConsoleLogsService (EventBased/Unity6Enhanced)
participant Tool as SearchConsoleLogsTool
UI->>Settings: User selects Console Log Service type
UI->>Server: (On restart) InitializeServices()
Server->>Settings: Read ConsoleLogService setting
Server->>LogService: Instantiate correct implementation (EventBased or Unity6Enhanced)
Server->>Tool: Register SearchConsoleLogsTool with LogService
Note over UI,Tool: User requests log search via UI or socket
UI->>Tool: Execute({keyword/regex, logType, ...})
Tool->>LogService: SearchLogsAsJson(...)
LogService-->>Tool: JSON results
Tool-->>UI: JSON response (with summary)
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate Unit Tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Apology for Incorrect PR 🙏I sincerely apologize for creating this pull request incorrectly. This PR contains code that was previously rejected and should not have been submitted to the upstream repository. Issues with this PR:
I will close this PR immediately to avoid any confusion or merge conflicts. Next Steps:
Again, my sincere apologies for this oversight. Thank you for your understanding. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (13)
.gitignore (1)
124-125
: Ignore local Claude configuration.Adding
.claude/settings.local.json
prevents committing personal Claude settings. Consider broadening this to ignore the entire.claude/
directory (e.g.,/.claude/
) to cover any additional files in that folder.Editor/UnityBridge/McpUnitySocketHandler.cs (1)
77-84
: Log level & verbosity
You prefix the messages with
[DEBUG]
but log them throughLogInfo
/LogWarning
.
• Consider switching to a dedicated debug-level method (or extendMcpLogger
) to avoid polluting info logs in production.
string.Join(", ", _server.GetResourceNames())
allocates on every unknown call. Probably fine, but if spam becomes an issue caching or conditional compilation might help.Minor, no blocker.
Editor/UnityBridge/McpUnitySettings.cs (2)
12-19
:Tooltip
attributes on enum members are ignored by Unity
Tooltip
works only on serialized fields, not on individual enum values. These attributes increase bundle size and developer expectations without any runtime/inspector effect.- [Tooltip("Event-based implementation (default, safe) - may occasionally miss logs")] EventBased, - - [Tooltip("Unity 6 enhanced implementation (experimental) - uses internal APIs for better reliability")] Unity6EnhancedIf you want per-item help you’ll need a custom
PropertyDrawer
.
52-54
: Persisting a brand-new setting can break older check-outsExisting projects that already have a
McpUnitySettings.json
won’t haveConsoleLogService
stored.
JsonUtility.FromJsonOverwrite
will silently leave the field at its default (EventBased
) on load, but SaveSettings() is called in the ctor and immediately writes the file back – that means the first project open will mutate the file and create unnecessary churn in version control.Consider deferring
SaveSettings()
until the user makes an explicit change, or add a version tag / migration path.Editor/Services/ConsoleLogsService.cs (1)
274-279
: Compile the regex for repeated useEach search may hit hundreds of log entries; adding
RegexOptions.Compiled
(where supported) gives ~2-3× perf gain for medium-size result sets.- searchRegex = new Regex(regex, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase); + var regexOptions = caseSensitive ? RegexOptions.Compiled + : RegexOptions.Compiled | RegexOptions.IgnoreCase; + searchRegex = new Regex(regex, regexOptions);Editor/UnityBridge/McpUnityServer.cs (1)
269-284
: Repeated fallback every domain reloadWhen
Unity6Enhanced
is selected in a pre-6 editor, we log a warning each reload but still keep the setting.
Couple this with the editor-window feedback above or automatically downgrade the enum here to avoid noisy logs.No code sample – see previous comment in
McpUnityEditorWindow.cs
.Editor/Services/Unity6InternalAPIReference.md (2)
8-10
: Table should be surrounded by blank lines (MD058).Insert a blank line before and after the table to satisfy markdown-lint and improve readability.
-| Field Name | Type | Description | + +| Field Name | Type | Description | … - | callstackTextStartUTF16 | Int32 | UTF-16 character position where stack trace starts | +
74-78
: Missing language identifier for fenced code block (MD040).Add
text
orcsharp
to the triple-backticks to enable proper syntax highlighting.-``` +```textEditor/Services/LogEntryModeFlags.cs (2)
24-31
: ObservedRuntime constants are never used.*Dead code increases maintenance cost and can confuse readers. Either remove the constants or extend
GetLogTypeFromMode
to make use of them.
10-23
: Prefer an enum with [Flags] over rawint
constants.An enum improves type-safety and self-documentation:
[Flags] public enum LogEntryMode { Error = 1 << 0, Assert = 1 << 1, ... }This change is optional but aligns with C# best practices.
Editor/Services/ConsoleLogsServiceUnity6.cs (3)
78-83
: Unused reflection fields.
_fileField
and_lineField
are fetched but never referenced, adding unnecessary reflection overhead. Remove them or integrate the data.
201-204
: Synthetic timestamp is misleading.
DateTime.Now.AddSeconds(-(currentCount - i))
fabricates a time that has no relation to the real log time and may even produce future timestamps when logs are cleared. Either omit the timestamp or derive it from Unity’sLogEntry
(if available).
320-346
: Duplicate log-type inference logic.
DetermineLogTypeFromModeAndContent
duplicates logic already inLogEntryModeFlags.GetLogTypeFromMode
. Rely exclusively on the central helper to avoid divergence.-// First try to determine from stack trace content … -… -// Fallback to mode flags -return GetLogTypeFromMode(mode); +return LogEntryModeFlags.GetLogTypeFromMode(mode);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.gitignore
(1 hunks)Editor/Services/ConsoleLogsService.cs
(2 hunks)Editor/Services/ConsoleLogsServiceUnity6.cs
(1 hunks)Editor/Services/ConsoleLogsServiceUnity6.cs.meta
(1 hunks)Editor/Services/IConsoleLogsService.cs
(1 hunks)Editor/Services/LogEntryModeFlags.cs
(1 hunks)Editor/Services/LogEntryModeFlags.cs.meta
(1 hunks)Editor/Services/Unity6InternalAPIReference.md
(1 hunks)Editor/Services/Unity6InternalAPIReference.md.meta
(1 hunks)Editor/Tools/SearchConsoleLogsTool.cs
(1 hunks)Editor/Tools/SearchConsoleLogsTool.cs.meta
(1 hunks)Editor/UnityBridge/McpUnityEditorWindow.cs
(1 hunks)Editor/UnityBridge/McpUnityServer.cs
(5 hunks)Editor/UnityBridge/McpUnitySettings.cs
(2 hunks)Editor/UnityBridge/McpUnitySocketHandler.cs
(1 hunks)Server~/build/index.js
(2 hunks)Server~/build/utils/errors.js
(1 hunks)Server~/build/utils/logger.js
(1 hunks)Server~/src/index.ts
(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
Editor/Services/IConsoleLogsService.cs (1)
Server~/build/resources/getConsoleLogsResource.js (4)
logType
(63-63)includeStackTrace
(70-70)offset
(67-67)limit
(68-68)
Editor/Services/LogEntryModeFlags.cs (1)
Editor/Services/ConsoleLogsServiceUnity6.cs (1)
GetLogTypeFromMode
(349-353)
Editor/UnityBridge/McpUnityEditorWindow.cs (1)
Editor/UnityBridge/McpUnitySettings.cs (1)
SaveSettings
(111-124)
🪛 markdownlint-cli2 (0.17.2)
Editor/Services/Unity6InternalAPIReference.md
8-8: Tables should be surrounded by blank lines
null
(MD058, blanks-around-tables)
74-74: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
🔇 Additional comments (12)
Server~/build/utils/logger.js (1)
8-8
: Explicit enum assignment for stability
Switching toLogLevel = LogLevel || (LogLevel = {})
ensures the enum variable is always initialized or reused before population, avoiding potential undefined behavior in various module loaders. This matches the pattern inerrors.js
.Server~/build/utils/errors.js (1)
9-9
: Align enum initialization pattern
The explicit assignmentErrorType = ErrorType || (ErrorType = {})
prevents uninitialized enum errors and keeps consistency with theLogLevel
change inlogger.js
.Editor/Services/ConsoleLogsServiceUnity6.cs.meta (1)
1-2
: Add Unity asset metadata
The.meta
file correctly specifiesfileFormatVersion
and a uniqueguid
for theConsoleLogsServiceUnity6
asset, enabling Unity to track and manage the script.Editor/Tools/SearchConsoleLogsTool.cs.meta (1)
1-2
: Add Unity asset metadata
The.meta
file provides the required version andguid
for the newSearchConsoleLogsTool
asset, ensuring proper Unity Editor integration.Editor/Services/Unity6InternalAPIReference.md.meta (1)
1-7
: Add Unity asset metadata for API doc
This metadata file correctly configuresfileFormatVersion
,guid
, and theTextScriptImporter
settings, enabling Unity to import and track the internal API reference document.Editor/Services/LogEntryModeFlags.cs.meta (1)
1-11
: Meta-file looks goodNothing to flag – the meta only registers the new script with Unity.
Server~/build/index.js (1)
23-24
: Import + registration align with existing patternThe added
registerSearchConsoleLogsTool
follows the same ordering and invocation style as the other tools, so the bootstrap sequence remains deterministic.Also applies to: 52-53
Server~/src/index.ts (1)
23-24
: ```shell
#!/bin/bash
set -eecho "Listing contents of Server~/src/tools recursively:"
ls -R 'Server~/src/tools'</details> <details> <summary>Editor/Services/IConsoleLogsService.cs (1)</summary> `45-57`: ```shell #!/bin/bash # Find SearchLogsAsJson signature and its documentation rg "SearchLogsAsJson" -n Editor/Services/IConsoleLogsService.cs ```) </details> <details> <summary>Editor/Tools/SearchConsoleLogsTool.cs (1)</summary> `50-71`: **Looks solid – good defensive parameter handling** Null-safe extraction, sensible min/max for `offset`/`limit`, and clean result shaping. </details> <details> <summary>Editor/UnityBridge/McpUnityServer.cs (1)</summary> `219-221`: **Null-check `_consoleLogsService` before tool registration** If `InitializeServices()` fails for any reason (e.g. reflection error inside `ConsoleLogsServiceUnity6`) `_consoleLogsService` could be `null`, and the constructor of `SearchConsoleLogsTool` would throw. ```diff -SearchConsoleLogsTool searchConsoleLogsTool = new SearchConsoleLogsTool(_consoleLogsService); -_tools.Add(searchConsoleLogsTool.Name, searchConsoleLogsTool); +if (_consoleLogsService != null) +{ + var searchConsoleLogsTool = new SearchConsoleLogsTool(_consoleLogsService); + _tools.Add(searchConsoleLogsTool.Name, searchConsoleLogsTool); +} +else +{ + McpLogger.LogError("[MCP Unity] Console log service unavailable – search_console_logs tool not registered."); +}
Editor/Services/LogEntryModeFlags.cs (1)
50-58
: Return value should distinguish unknown types.Falling back to
"Log"
hides genuinely unknown modes. Returning"Unknown"
(or throwing) will surface mis-classifications early.- return "Log"; // Default to Log instead of Unknown + return "Unknown";
// Console log service selection | ||
ConsoleLogServiceType newConsoleLogService = (ConsoleLogServiceType)EditorGUILayout.EnumPopup( | ||
new GUIContent("Console Log Service", "Select console log service implementation. EventBased is safe but may miss some logs. Unity6Enhanced uses internal APIs for better reliability but requires Unity 6+. Server restart required when changed."), | ||
settings.ConsoleLogService); | ||
if (newConsoleLogService != settings.ConsoleLogService) | ||
{ | ||
settings.ConsoleLogService = newConsoleLogService; | ||
settings.SaveSettings(); | ||
|
||
// Show warning for Unity6Enhanced | ||
if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced) | ||
{ | ||
#if UNITY_6000_0_OR_NEWER | ||
EditorUtility.DisplayDialog("Console Log Service Changed", | ||
"Unity 6 enhanced console log service selected. This uses internal Unity APIs for better reliability but may break in future Unity versions.\n\nRestart the MCP server for changes to take effect.", "OK"); | ||
#else | ||
EditorUtility.DisplayDialog("Console Log Service Warning", | ||
$"Unity 6 enhanced console log service selected but current Unity version is {Application.unityVersion}. The service will fall back to event-based implementation.\n\nRestart the MCP server for changes to take effect.", "OK"); | ||
#endif | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Storing an unsupported option can confuse users
On pre-Unity-6 editors the user can still select Unity6Enhanced.
You warn that it “will fall back”, but the value is nevertheless persisted, so every project reopen shows the (inoperative) selection again and re-triggers the warning.
settings.ConsoleLogService = newConsoleLogService;
settings.SaveSettings();
-
+#if !UNITY_6000_0_OR_NEWER
+ if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced)
+ {
+ // Immediately revert so the inspector reflects the real runtime state
+ settings.ConsoleLogService = ConsoleLogServiceType.EventBased;
+ settings.SaveSettings();
+ }
+#endif
Alternatively disable the enum value via EditorGUI.BeginDisabledGroup
when !UNITY_6000_0_OR_NEWER
.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Console log service selection | |
ConsoleLogServiceType newConsoleLogService = (ConsoleLogServiceType)EditorGUILayout.EnumPopup( | |
new GUIContent("Console Log Service", "Select console log service implementation. EventBased is safe but may miss some logs. Unity6Enhanced uses internal APIs for better reliability but requires Unity 6+. Server restart required when changed."), | |
settings.ConsoleLogService); | |
if (newConsoleLogService != settings.ConsoleLogService) | |
{ | |
settings.ConsoleLogService = newConsoleLogService; | |
settings.SaveSettings(); | |
// Show warning for Unity6Enhanced | |
if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced) | |
{ | |
#if UNITY_6000_0_OR_NEWER | |
EditorUtility.DisplayDialog("Console Log Service Changed", | |
"Unity 6 enhanced console log service selected. This uses internal Unity APIs for better reliability but may break in future Unity versions.\n\nRestart the MCP server for changes to take effect.", "OK"); | |
#else | |
EditorUtility.DisplayDialog("Console Log Service Warning", | |
$"Unity 6 enhanced console log service selected but current Unity version is {Application.unityVersion}. The service will fall back to event-based implementation.\n\nRestart the MCP server for changes to take effect.", "OK"); | |
#endif | |
} | |
} | |
// Console log service selection | |
ConsoleLogServiceType newConsoleLogService = (ConsoleLogServiceType)EditorGUILayout.EnumPopup( | |
new GUIContent("Console Log Service", "Select console log service implementation. EventBased is safe but may miss some logs. Unity6Enhanced uses internal APIs for better reliability but requires Unity 6+. Server restart required when changed."), | |
settings.ConsoleLogService); | |
if (newConsoleLogService != settings.ConsoleLogService) | |
{ | |
settings.ConsoleLogService = newConsoleLogService; | |
settings.SaveSettings(); | |
#if !UNITY_6000_0_OR_NEWER | |
if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced) | |
{ | |
// Immediately revert so the inspector reflects the real runtime state | |
settings.ConsoleLogService = ConsoleLogServiceType.EventBased; | |
settings.SaveSettings(); | |
} | |
#endif | |
// Show warning for Unity6Enhanced | |
if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced) | |
{ | |
#if UNITY_6000_0_OR_NEWER | |
EditorUtility.DisplayDialog("Console Log Service Changed", | |
"Unity 6 enhanced console log service selected. This uses internal Unity APIs for better reliability but may break in future Unity versions.\n\nRestart the MCP server for changes to take effect.", "OK"); | |
#else | |
EditorUtility.DisplayDialog("Console Log Service Warning", | |
$"Unity 6 enhanced console log service selected but current Unity version is {Application.unityVersion}. The service will fall back to event-based implementation.\n\nRestart the MCP server for changes to take effect.", "OK"); | |
#endif | |
} | |
} |
🤖 Prompt for AI Agents
In Editor/UnityBridge/McpUnityEditorWindow.cs around lines 146 to 166, the code
allows selecting the Unity6Enhanced console log service on Unity versions older
than 6, which causes the unsupported option to be saved and repeatedly trigger
warnings. To fix this, disable the Unity6Enhanced enum option in the EnumPopup
using EditorGUI.BeginDisabledGroup when UNITY_6000_0_OR_NEWER is not defined,
preventing users from selecting and saving an unsupported option.
lock (_logEntries) | ||
{ | ||
totalCount = _logEntries.Count; | ||
|
||
// Search through logs (newest first) | ||
for (int i = _logEntries.Count - 1; i >= 0; i--) | ||
{ | ||
var entry = _logEntries[i]; | ||
|
||
// Skip if filtering by log type and entry doesn't match | ||
if (unityLogTypes != null && !unityLogTypes.Contains(entry.Type.ToString())) | ||
continue; | ||
|
||
filteredCount++; | ||
|
||
// Check if entry matches search criteria | ||
bool matches = true; | ||
if (hasSearchCriteria) | ||
{ | ||
matches = false; | ||
|
||
// Search in message | ||
if (searchRegex != null) | ||
{ | ||
matches = searchRegex.IsMatch(entry.Message); | ||
if (!matches && includeStackTrace && !string.IsNullOrEmpty(entry.StackTrace)) | ||
{ | ||
matches = searchRegex.IsMatch(entry.StackTrace); | ||
} | ||
} | ||
else if (!string.IsNullOrEmpty(searchKeyword)) | ||
{ | ||
string messageToSearch = caseSensitive ? entry.Message : entry.Message.ToLower(); | ||
matches = messageToSearch.Contains(searchKeyword); | ||
|
||
if (!matches && includeStackTrace && !string.IsNullOrEmpty(entry.StackTrace)) | ||
{ | ||
string stackTraceToSearch = caseSensitive ? entry.StackTrace : entry.StackTrace.ToLower(); | ||
matches = stackTraceToSearch.Contains(searchKeyword); | ||
} | ||
} | ||
} | ||
|
||
if (!matches) continue; | ||
|
||
matchedCount++; | ||
|
||
// Check if we're in the offset range and haven't reached the limit yet | ||
if (currentIndex >= offset && logsArray.Count < limit) | ||
{ | ||
var logObject = new JObject | ||
{ | ||
["message"] = entry.Message, | ||
["type"] = entry.Type.ToString(), | ||
["timestamp"] = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff") | ||
}; | ||
|
||
// Only include stack trace if requested | ||
if (includeStackTrace) | ||
{ | ||
logObject["stackTrace"] = entry.StackTrace; | ||
} | ||
|
||
logsArray.Add(logObject); | ||
} | ||
|
||
currentIndex++; | ||
|
||
// Early exit if we've collected enough logs | ||
if (currentIndex >= offset + limit) break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid holding the _logEntries
lock while running heavy pattern matching
Regex.IsMatch
, ToLower()
, etc. inside the lock can freeze the UI thread while new editor logs are waiting to be appended.
- lock (_logEntries)
- {
- totalCount = _logEntries.Count;
- // iterate, regex-match, build result …
- }
+ List<LogEntry> snapshot;
+ lock (_logEntries)
+ {
+ totalCount = _logEntries.Count;
+ snapshot = _logEntries.ToList(); // copy once
+ }
+
+ // search over the *snapshot* outside the lock
+ for (int i = snapshot.Count - 1; i >= 0; i--)
+ {
+ var entry = snapshot[i];
+ …
+ }
This keeps append latency O(µs) even for expensive searches.
🤖 Prompt for AI Agents
In Editor/Services/ConsoleLogsService.cs between lines 313 and 384, the code
holds the _logEntries lock while performing expensive operations like
Regex.IsMatch and ToLower, which can block the UI thread. To fix this, refactor
the code to first copy the relevant _logEntries data into a separate list or
array inside the lock, then release the lock before performing any pattern
matching or string manipulation. This ensures the lock is held only briefly for
copying, preventing UI freezes during heavy searches.
// Check for observed compiler/shader message patterns first | ||
if (mode == ObservedCompilerError) return "Error"; | ||
if (mode == ObservedCompilerWarning) return "Warning"; | ||
if (mode == ObservedShaderError) return "Error"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Exact equality check may miss messages that include undocumented high bits.
mode == ObservedCompilerError
requires an exact match; Unity often ORs extra bits. Consider masking instead:
-if (mode == ObservedCompilerError) return "Error";
+if ((mode & ObservedCompilerError) == ObservedCompilerError) return "Error";
This makes the detection resilient to future Unity changes.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Check for observed compiler/shader message patterns first | |
if (mode == ObservedCompilerError) return "Error"; | |
if (mode == ObservedCompilerWarning) return "Warning"; | |
if (mode == ObservedShaderError) return "Error"; | |
// Check for observed compiler/shader message patterns first | |
if ((mode & ObservedCompilerError) == ObservedCompilerError) return "Error"; | |
if (mode == ObservedCompilerWarning) return "Warning"; | |
if (mode == ObservedShaderError) return "Error"; |
🤖 Prompt for AI Agents
In Editor/Services/LogEntryModeFlags.cs around lines 37 to 41, the code uses
exact equality checks for mode against ObservedCompilerError and similar
constants, which can fail if Unity ORs extra bits. To fix this, replace the
equality checks with bitwise masking to compare only the relevant bits, ensuring
the detection works even if additional bits are set in mode.
// Start getting entries | ||
_startGettingEntriesMethod?.Invoke(null, null); | ||
|
||
int currentCount = (int)_getCountMethod.Invoke(null, null); | ||
var collectedLogs = new List<JObject>(); | ||
|
||
// Iterate through all logs (newest first) | ||
for (int i = currentCount - 1; i >= 0; i--) | ||
{ | ||
// Create LogEntry instance | ||
var logEntry = Activator.CreateInstance(_logEntryType); | ||
|
||
// GetEntryInternal(int row, LogEntry outputEntry) | ||
bool success = (bool)_getEntryInternalMethod.Invoke(null, new object[] { i, logEntry }); | ||
|
||
if (!success) continue; | ||
|
||
// Extract fields (see Unity6InternalAPIReference.md for field details) | ||
string fullMessage = _messageField?.GetValue(logEntry) as string ?? ""; | ||
string file = _fileField?.GetValue(logEntry) as string ?? ""; | ||
int line = _lineField?.GetValue(logEntry) as int? ?? 0; | ||
int mode = _modeField?.GetValue(logEntry) as int? ?? 0; | ||
int callstackStartUTF8 = _callstackTextStartUTF8Field?.GetValue(logEntry) as int? ?? 0; | ||
int callstackStartUTF16 = _callstackTextStartUTF16Field?.GetValue(logEntry) as int? ?? 0; | ||
|
||
// Debug: Write mode values to file for analysis (disabled by default) | ||
#if MCP_UNITY_DEBUG_MODE_VALUES | ||
if (fullMessage.Contains("error") || fullMessage.Contains("Error") || | ||
fullMessage.Contains("warning") || fullMessage.Contains("Warning") || | ||
fullMessage.Contains("failed") || fullMessage.Contains("Failed") || | ||
fullMessage.Contains("exception") || fullMessage.Contains("Exception")) | ||
{ | ||
WriteDebugInfo(fullMessage, mode); | ||
} | ||
#endif | ||
|
||
// Parse message and stack trace using Unity's internal callstack position (prefer UTF-16) | ||
var (actualMessage, stackTrace) = ParseMessageAndStackTrace(fullMessage, callstackStartUTF16, callstackStartUTF8); | ||
|
||
// Determine log type from mode and stack trace | ||
string entryType = DetermineLogTypeFromModeAndContent(mode, stackTrace); | ||
|
||
// Skip if filtering and doesn't match | ||
if (filter && !unityLogTypes.Contains(entryType)) | ||
continue; | ||
|
||
// Create log object | ||
var logObject = new JObject | ||
{ | ||
["message"] = actualMessage, | ||
["type"] = entryType, | ||
["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff") | ||
}; | ||
|
||
// Include stack trace if requested | ||
if (includeStackTrace && !string.IsNullOrEmpty(stackTrace)) | ||
{ | ||
logObject["stackTrace"] = stackTrace; | ||
} | ||
|
||
collectedLogs.Add(logObject); | ||
} | ||
|
||
// End getting entries | ||
_endGettingEntriesMethod?.Invoke(null, null); | ||
|
||
// Apply pagination | ||
var paginatedLogs = collectedLogs | ||
.Skip(offset) | ||
.Take(limit) | ||
.ToList(); | ||
|
||
foreach (var log in paginatedLogs) | ||
{ | ||
logsArray.Add(log); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Inefficient pagination – all logs are materialised before slicing.
collectedLogs
stores every console entry, which can be tens of thousands, just to Skip(offset).Take(limit)
later. Build the result incrementally and stop once offset + limit
have been satisfied to reduce memory and CPU usage.
🤖 Prompt for AI Agents
In Editor/Services/ConsoleLogsServiceUnity6.cs between lines 152 and 226, the
code collects all log entries into a list before applying pagination with Skip
and Take, which is inefficient for large log volumes. Modify the loop to track
how many entries have been processed and only add logs to the result after
reaching the offset, stopping the iteration once offset plus limit entries have
been collected. This incremental approach avoids storing all logs in memory and
reduces CPU usage by terminating early.
// Get total count using reflection | ||
try | ||
{ | ||
totalCount = (int)_getCountMethod.Invoke(null, null); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.LogError($"[MCP Unity] Error getting log count: {ex.Message}"); | ||
return new JObject | ||
{ | ||
["logs"] = logsArray, | ||
["error"] = "Failed to access Unity console logs", | ||
["success"] = false | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Null-reference risk when reflection is unavailable.
SearchLogsAsJson
calls _getCountMethod.Invoke
without verifying that reflection succeeded. If InitializeReflection
failed, this will throw and crash the tool.
Add the same availability guard used in GetLogsAsJson
:
+if (_logEntriesType == null || _getCountMethod == null || _getEntryInternalMethod == null)
+{
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["message"] = "LogEntries API not available",
+ ["success"] = false
+ };
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Get total count using reflection | |
try | |
{ | |
totalCount = (int)_getCountMethod.Invoke(null, null); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError($"[MCP Unity] Error getting log count: {ex.Message}"); | |
return new JObject | |
{ | |
["logs"] = logsArray, | |
["error"] = "Failed to access Unity console logs", | |
["success"] = false | |
}; | |
} | |
// Guard against missing reflection APIs | |
if (_logEntriesType == null || _getCountMethod == null || _getEntryInternalMethod == null) | |
{ | |
return new JObject | |
{ | |
["logs"] = new JArray(), | |
["message"] = "LogEntries API not available", | |
["success"] = false | |
}; | |
} | |
// Get total count using reflection | |
try | |
{ | |
totalCount = (int)_getCountMethod.Invoke(null, null); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError($"[MCP Unity] Error getting log count: {ex.Message}"); | |
return new JObject | |
{ | |
["logs"] = logsArray, | |
["error"] = "Failed to access Unity console logs", | |
["success"] = false | |
}; | |
} |
🤖 Prompt for AI Agents
In Editor/Services/ConsoleLogsServiceUnity6.cs around lines 419 to 433, the code
calls _getCountMethod.Invoke without checking if _getCountMethod is null,
risking a null-reference exception if reflection initialization failed. Add a
null check for _getCountMethod before invoking it, similar to the guard used in
GetLogsAsJson, and handle the case where it is null by returning an error
JObject to prevent crashes.
Summary
Features Added
Implementation Details
LogEntryModeFlags.cs
with Unity's internal mode flag constants for better log type classificationTesting
shader
,texture
,Player
GameObject.*not found
,^\[MCP\].*
Test plan
🤖 Generated with Claude Code
Created with Palmier
Summary by CodeRabbit
New Features
Improvements
Documentation
Chores