123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- using System;
- using System.IO;
- using System.Text;
- using UnityEditor;
- using UnityEngine;
- using System.Reflection;
- namespace LLM.Editor.PoC
- {
- /// <summary>
- /// A Unity editor utility to export all current console log entries to a text file.
- /// </summary>
- public static class ConsoleLogExporter
- {
- // --- Internal Unity Types and Methods accessed via Reflection ---
- private static Type _logEntriesType;
- private static MethodInfo _startGettingEntriesMethod;
- private static MethodInfo _endGettingEntriesMethod;
- private static MethodInfo _getEntryInternalMethod;
- private static int _logEntryCount;
- private static object _logEntry;
- // --- Menu Item to Trigger the Export ---
- [MenuItem("Tools/Export Console Logs")]
- public static void ExportLogs()
- {
- if (!InitializeReflection())
- {
- Debug.LogError("Failed to initialize reflection for accessing console logs. The Unity Editor API may have changed.");
- return;
- }
- // Prompt user for a save location
- var path = EditorUtility.SaveFilePanel("Save Console Logs", "", "console_logs.txt", "txt");
- if (string.IsNullOrEmpty(path))
- {
- // User cancelled the save dialog
- return;
- }
- try
- {
- // Build the formatted log string
- var formattedLogs = BuildFormattedLogString();
- // Write the string to the chosen file
- File.WriteAllText(path, formattedLogs);
- Debug.Log($"<color=lime>Successfully exported console logs to:</color> <a href=\"{path}\">{path}</a>");
- }
- catch (Exception e)
- {
- Debug.LogError($"Failed to export console logs. Error: {e.Message}");
- }
- }
- /// <summary>
- /// Initializes the reflection members needed to access Unity's internal console log API.
- /// </summary>
- /// <returns>True if initialization was successful, otherwise false.</returns>
- private static bool InitializeReflection()
- {
- // Get the internal LogEntries class
- _logEntriesType = Type.GetType("UnityEditor.LogEntries, UnityEditor.dll");
- if (_logEntriesType == null) return false;
- // Get the necessary static methods from LogEntries
- _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries", BindingFlags.Static | BindingFlags.Public);
- _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Static | BindingFlags.Public);
- _getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public);
-
- // Get the total number of log entries
- var getCountMethod = _logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public);
- if (getCountMethod != null)
- {
- _logEntryCount = (int)getCountMethod.Invoke(null, null);
- }
- // Create an instance of the internal LogEntry class to reuse
- var logEntryType = Type.GetType("UnityEditor.LogEntry, UnityEditor.dll");
- if (logEntryType == null) return false;
-
- _logEntry = Activator.CreateInstance(logEntryType);
- return _startGettingEntriesMethod != null && _endGettingEntriesMethod != null && _getEntryInternalMethod != null;
- }
- /// <summary>
- /// Reads all console entries and formats them into a single, readable string.
- /// </summary>
- /// <returns>A formatted string containing all log data.</returns>
- private static string BuildFormattedLogString()
- {
- var stringBuilder = new StringBuilder();
- stringBuilder.AppendLine("==================================================");
- stringBuilder.AppendLine($" CONSOLE LOGS EXPORTED ON: {DateTime.Now}");
- stringBuilder.AppendLine("==================================================");
- stringBuilder.AppendLine();
- // Safely get all entries
- _startGettingEntriesMethod.Invoke(null, null);
- for (var i = 0; i < _logEntryCount; i++)
- {
- // Retrieve a single log entry by its index
- _getEntryInternalMethod.Invoke(null, new[] { i, _logEntry });
- // Extract message and stack trace using reflection from the LogEntry instance
- var messageField = _logEntry.GetType().GetField("message", BindingFlags.Instance | BindingFlags.Public);
- var stackTraceField = _logEntry.GetType().GetField("stackTrace", BindingFlags.Instance | BindingFlags.Public);
- var modeField = _logEntry.GetType().GetField("mode", BindingFlags.Instance | BindingFlags.Public);
- var message = messageField?.GetValue(_logEntry).ToString();
- var stackTrace = stackTraceField?.GetValue(_logEntry).ToString();
- var mode = (int)(modeField?.GetValue(_logEntry) ?? 0);
- // Append formatted entry to the string builder
- stringBuilder.AppendLine("--------------------------------------------------");
- stringBuilder.AppendLine($"TYPE: {GetLogTypeFromMode(mode)}");
- stringBuilder.AppendLine("--------------------------------------------------");
- stringBuilder.AppendLine(message?.Trim());
- stringBuilder.AppendLine();
- if (string.IsNullOrWhiteSpace(stackTrace)) continue;
-
- stringBuilder.AppendLine("STACK TRACE:");
- stringBuilder.AppendLine(stackTrace.Trim());
- stringBuilder.AppendLine();
- }
- // Clean up
- _endGettingEntriesMethod.Invoke(null, null);
- return stringBuilder.ToString();
- }
- /// <summary>
- /// Converts the internal 'mode' integer from a LogEntry into a human-readable log type.
- /// </summary>
- private static string GetLogTypeFromMode(int mode)
- {
- // These are flags, so we need to check for them with bitwise operations.
- // The key is knowing which bits correspond to which log type.
- // This is based on an analysis of Unity's internal LogEntry class.
- const int errorFlag = 1;
- const int assertFlag = 2;
- const int warningFlag = 4;
- const int logFlag = 8;
- // There are other flags for things like compiler errors, etc.
- if ((mode & errorFlag) != 0) return "Error";
- if ((mode & assertFlag) != 0) return "Assert";
- if ((mode & warningFlag) != 0) return "Warning";
- return (mode & logFlag) != 0 ? "Log" :
- // It's possible for a log entry to have other modes (e.g., from packages)
- "Unknown";
- }
- }
- }
|