Compare commits
1 Commits
32ecbf4dc4
...
363eb99a91
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
363eb99a91 |
@@ -49,11 +49,6 @@ namespace Editor
|
||||
|
||||
#region UI State
|
||||
|
||||
private bool showClassFilter = true;
|
||||
private bool showMethodFilter = false;
|
||||
private bool showLevelFilter = true;
|
||||
private bool showTimeFilter = false;
|
||||
|
||||
// Colors for log levels
|
||||
private readonly Color debugColor = Color.white;
|
||||
private readonly Color infoColor = Color.white;
|
||||
@@ -123,7 +118,6 @@ namespace Editor
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawToolbar();
|
||||
DrawFilters();
|
||||
DrawLogEntries();
|
||||
}
|
||||
|
||||
@@ -144,11 +138,46 @@ namespace Editor
|
||||
// Auto-scroll toggle
|
||||
autoScroll = GUILayout.Toggle(autoScroll, "Auto-scroll", EditorStyles.toolbarButton, GUILayout.Width(80));
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// Class Filter Button
|
||||
string classLabel = selectedClassFilters.Count == 0 ? "Classes: All" :
|
||||
selectedClassFilters.Count == activeClassTags.Count ? "Classes: All" :
|
||||
$"Classes: {selectedClassFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(classLabel, "Filter by class"), EditorStyles.toolbarDropDown, GUILayout.Width(100)))
|
||||
{
|
||||
ShowClassFilterMenu();
|
||||
}
|
||||
|
||||
// Method Filter Button
|
||||
string methodLabel = selectedMethodFilters.Count == 0 ? "Methods: All" :
|
||||
selectedMethodFilters.Count == activeMethodTags.Count ? "Methods: All" :
|
||||
$"Methods: {selectedMethodFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(methodLabel, "Filter by method"), EditorStyles.toolbarDropDown, GUILayout.Width(110)))
|
||||
{
|
||||
ShowMethodFilterMenu();
|
||||
}
|
||||
|
||||
// Log Level Filter Button
|
||||
string levelLabel = selectedLevelFilters.Count == 4 ? "Levels: All" :
|
||||
selectedLevelFilters.Count == 0 ? "Levels: None" :
|
||||
$"Levels: {selectedLevelFilters.Count}";
|
||||
if (GUILayout.Button(new GUIContent(levelLabel, "Filter by log level"), EditorStyles.toolbarDropDown, GUILayout.Width(90)))
|
||||
{
|
||||
ShowLevelFilterMenu();
|
||||
}
|
||||
|
||||
// Time Range Filter Button
|
||||
if (GUILayout.Button(new GUIContent("⏱", "Time range filter"), EditorStyles.toolbarButton, GUILayout.Width(25)))
|
||||
{
|
||||
ShowTimeRangeWindow();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// Log count
|
||||
var filteredCount = GetFilteredLogs().Count();
|
||||
GUILayout.Label($"{filteredCount} / {allLogs.Count} logs", GUILayout.Width(100));
|
||||
// Search box
|
||||
GUILayout.Label("Search:", GUILayout.Width(50));
|
||||
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
@@ -158,135 +187,55 @@ namespace Editor
|
||||
ExportLogs();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Space(5);
|
||||
|
||||
// Search box
|
||||
GUILayout.Label("Search:", GUILayout.Width(50));
|
||||
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(200));
|
||||
// Log count
|
||||
var filteredCount = GetFilteredLogs().Count();
|
||||
GUILayout.Label($"{filteredCount}/{allLogs.Count}", GUILayout.Width(80));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawFilters()
|
||||
private void ShowClassFilterMenu()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
// Class Filters
|
||||
showClassFilter = EditorGUILayout.Foldout(showClassFilter, $"Class Filters ({selectedClassFilters.Count} selected)", true);
|
||||
if (showClassFilter)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(50)))
|
||||
ClassFilterWindow.ShowWindow(this, activeClassTags, selectedClassFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
selectedClassFilters = new HashSet<string>(activeClassTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(50)))
|
||||
selectedClassFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowMethodFilterMenu()
|
||||
{
|
||||
MethodFilterWindow.ShowWindow(this, activeMethodTags, selectedMethodFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
selectedClassFilters.Clear();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
foreach (var tag in activeClassTags.OrderBy(t => t))
|
||||
selectedMethodFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowLevelFilterMenu()
|
||||
{
|
||||
LevelFilterWindow.ShowWindow(this, selectedLevelFilters,
|
||||
(newSelection) =>
|
||||
{
|
||||
bool isSelected = selectedClassFilters.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection && !isSelected)
|
||||
selectedClassFilters.Add(tag);
|
||||
else if (!newSelection && isSelected)
|
||||
selectedClassFilters.Remove(tag);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Method Filters
|
||||
showMethodFilter = EditorGUILayout.Foldout(showMethodFilter, $"Method Filters ({selectedMethodFilters.Count} selected)", true);
|
||||
if (showMethodFilter)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(50)))
|
||||
selectedLevelFilters = newSelection;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void ShowTimeRangeWindow()
|
||||
{
|
||||
TimeRangeFilterWindow.ShowWindow(this, enableTimeFilter, minTimestamp, maxTimestamp, currentMaxTimestamp,
|
||||
(enabled, min, max) =>
|
||||
{
|
||||
selectedMethodFilters = new HashSet<string>(activeMethodTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(50)))
|
||||
{
|
||||
selectedMethodFilters.Clear();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
foreach (var tag in activeMethodTags.OrderBy(t => t))
|
||||
{
|
||||
bool isSelected = selectedMethodFilters.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection && !isSelected)
|
||||
selectedMethodFilters.Add(tag);
|
||||
else if (!newSelection && isSelected)
|
||||
selectedMethodFilters.Remove(tag);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Log Level Filters
|
||||
showLevelFilter = EditorGUILayout.Foldout(showLevelFilter, "Log Level Filters", true);
|
||||
if (showLevelFilter)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||
{
|
||||
bool isSelected = selectedLevelFilters.Contains(level);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
||||
|
||||
if (newSelection && !isSelected)
|
||||
selectedLevelFilters.Add(level);
|
||||
else if (!newSelection && isSelected)
|
||||
selectedLevelFilters.Remove(level);
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Time Range Filter
|
||||
showTimeFilter = EditorGUILayout.Foldout(showTimeFilter, "Time Range Filter", true);
|
||||
if (showTimeFilter)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
||||
|
||||
if (enableTimeFilter && currentMaxTimestamp > 0)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.MinMaxSlider(
|
||||
ref minTimestamp,
|
||||
ref maxTimestamp,
|
||||
0,
|
||||
currentMaxTimestamp);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
enableTimeFilter = enabled;
|
||||
minTimestamp = min;
|
||||
maxTimestamp = max;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawLogEntries()
|
||||
@@ -508,5 +457,373 @@ namespace Editor
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for class filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class ClassFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<string> availableTags;
|
||||
private HashSet<string> selectedTags;
|
||||
private System.Action<HashSet<string>> onApply;
|
||||
private Vector2 scrollPosition;
|
||||
private string searchFilter = "";
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||
System.Action<HashSet<string>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<ClassFilterWindow>();
|
||||
window.titleContent = new GUIContent("Class Filter");
|
||||
window.availableTags = available;
|
||||
window.selectedTags = new HashSet<string>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
if (availableTags != null)
|
||||
selectedTags = new HashSet<string>(availableTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedTags.Clear();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Scrollable list of toggles
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
if (availableTags != null && availableTags.Count > 0)
|
||||
{
|
||||
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||
? availableTags.OrderBy(t => t)
|
||||
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||
|
||||
foreach (var tag in filteredTags)
|
||||
{
|
||||
bool isSelected = selectedTags.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedTags.Add(tag);
|
||||
else
|
||||
selectedTags.Remove(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No classes available", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedTags);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for method filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class MethodFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<string> availableTags;
|
||||
private HashSet<string> selectedTags;
|
||||
private System.Action<HashSet<string>> onApply;
|
||||
private Vector2 scrollPosition;
|
||||
private string searchFilter = "";
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||
System.Action<HashSet<string>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<MethodFilterWindow>();
|
||||
window.titleContent = new GUIContent("Method Filter");
|
||||
window.availableTags = available;
|
||||
window.selectedTags = new HashSet<string>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
if (availableTags != null)
|
||||
selectedTags = new HashSet<string>(availableTags);
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedTags.Clear();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Scrollable list of toggles
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
if (availableTags != null && availableTags.Count > 0)
|
||||
{
|
||||
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||
? availableTags.OrderBy(t => t)
|
||||
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||
|
||||
foreach (var tag in filteredTags)
|
||||
{
|
||||
bool isSelected = selectedTags.Contains(tag);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedTags.Add(tag);
|
||||
else
|
||||
selectedTags.Remove(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No methods available", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedTags);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persistent popup window for log level filtering with multi-selection support
|
||||
/// </summary>
|
||||
public class LevelFilterWindow : EditorWindow
|
||||
{
|
||||
private HashSet<LogLevel> selectedLevels;
|
||||
private System.Action<HashSet<LogLevel>> onApply;
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, HashSet<LogLevel> selected,
|
||||
System.Action<HashSet<LogLevel>> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<LevelFilterWindow>();
|
||||
window.titleContent = new GUIContent("Log Level Filter");
|
||||
window.selectedLevels = new HashSet<LogLevel>(selected);
|
||||
window.onApply = applyCallback;
|
||||
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 250, 180);
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Control buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||
{
|
||||
selectedLevels = new HashSet<LogLevel>
|
||||
{
|
||||
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||
};
|
||||
}
|
||||
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||
{
|
||||
selectedLevels.Clear();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Log level toggles
|
||||
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||
{
|
||||
bool isSelected = selectedLevels.Contains(level);
|
||||
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
||||
|
||||
if (newSelection != isSelected)
|
||||
{
|
||||
if (newSelection)
|
||||
selectedLevels.Add(level);
|
||||
else
|
||||
selectedLevels.Remove(level);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Apply/Close buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(selectedLevels);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Popup window for configuring time range filters
|
||||
/// </summary>
|
||||
public class TimeRangeFilterWindow : EditorWindow
|
||||
{
|
||||
private bool enableTimeFilter;
|
||||
private float minTimestamp;
|
||||
private float maxTimestamp;
|
||||
private float currentMaxTimestamp;
|
||||
private System.Action<bool, float, float> onApply;
|
||||
|
||||
public static void ShowWindow(CustomLogWindow parent, bool enabled, float min, float max, float currentMax,
|
||||
System.Action<bool, float, float> applyCallback)
|
||||
{
|
||||
var window = CreateInstance<TimeRangeFilterWindow>();
|
||||
window.titleContent = new GUIContent("Time Range Filter");
|
||||
window.enableTimeFilter = enabled;
|
||||
window.minTimestamp = min;
|
||||
window.maxTimestamp = max;
|
||||
window.currentMaxTimestamp = currentMax;
|
||||
window.onApply = applyCallback;
|
||||
|
||||
// Position near the parent window
|
||||
var parentRect = parent.position;
|
||||
window.position = new Rect(parentRect.x + 100, parentRect.y + 100, 350, 120);
|
||||
|
||||
window.ShowUtility();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
if (enableTimeFilter && currentMaxTimestamp > 0)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.MinMaxSlider(
|
||||
ref minTimestamp,
|
||||
ref maxTimestamp,
|
||||
0,
|
||||
currentMaxTimestamp);
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Reset Range"))
|
||||
{
|
||||
minTimestamp = 0;
|
||||
maxTimestamp = currentMaxTimestamp;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if (enableTimeFilter)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No logs available for time filtering", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||
{
|
||||
onApply?.Invoke(enableTimeFilter, minTimestamp, maxTimestamp);
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Cancel", GUILayout.Width(80)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
120
docs/custom_log_console.md
Normal file
120
docs/custom_log_console.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Custom Log Console
|
||||
|
||||
## Overview
|
||||
|
||||
A centralized logging system with an advanced filtering console that automatically tags log entries with class and method names. Provides powerful filtering capabilities beyond Unity's default console, including multi-select filters, text search, time-range filtering, and log export.
|
||||
|
||||
## Using the Logging System in Code
|
||||
|
||||
All logging automatically captures the calling class and method name using `CallerMemberName` and `CallerFilePath` attributes. Simply call the static logging methods:
|
||||
|
||||
```csharp
|
||||
using Core;
|
||||
|
||||
public class MyClass : ManagedBehaviour
|
||||
{
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
Logging.Debug("Initialization complete");
|
||||
Logging.Info("Player spawned at position");
|
||||
Logging.Warning("Missing configuration, using defaults");
|
||||
Logging.Error("Failed to load required asset");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output format:**
|
||||
```
|
||||
[ClassName][MethodName] Your message
|
||||
```
|
||||
|
||||
**Available methods:**
|
||||
- `Logging.Debug(string message)` - Detailed diagnostic information
|
||||
- `Logging.Info(string message)` - General informational messages
|
||||
- `Logging.Warning(string message)` - Non-critical issues
|
||||
- `Logging.Error(string message)` - Critical errors
|
||||
|
||||
**Note:** All logs are broadcast via the `Logging.OnLogEntryAdded` event and stored in a central buffer accessible via `Logging.GetRecentLogs()`.
|
||||
|
||||
---
|
||||
|
||||
## Opening the Console Window
|
||||
|
||||
**Menu:** `AppleHills > Custom Log Console`
|
||||
|
||||
You can open multiple independent console instances with different filter configurations to monitor separate systems simultaneously
|
||||
|
||||
---
|
||||
|
||||
## Console Interface
|
||||
|
||||

|
||||
|
||||
### Toolbar Controls
|
||||
|
||||
#### 🔵 Basic Controls (Blue outline)
|
||||
- **Clear** - Clears all log entries and resets tag lists
|
||||
- **Auto-scroll** - Automatically scrolls to newest entries when enabled
|
||||
|
||||
#### Filter Buttons (Persistent Popups)
|
||||
|
||||
All filter buttons open persistent popup windows that remain open during multi-selection. Changes apply when you click "Apply" or dismiss with "Close".
|
||||
|
||||
- **🔴 Classes Filter (Red outline)**
|
||||
- Multi-select which classes to display
|
||||
- Includes search box for quick filtering
|
||||
- All/None quick actions
|
||||
|
||||
- **🟢 Methods Filter (Green outline)**
|
||||
- Multi-select which methods to display
|
||||
- Includes search box for quick filtering
|
||||
- All/None quick actions
|
||||
|
||||
- **🟡 Levels Filter (Yellow outline)**
|
||||
- Toggle Debug, Info, Warning, Error levels
|
||||
- All/None quick actions
|
||||
|
||||
- **⏱ Time Filter**
|
||||
- Opens utility window with MinMaxSlider
|
||||
- Filter logs by timestamp range
|
||||
- Enable/disable toggle with reset option
|
||||
|
||||
#### Search & Export
|
||||
- **Search** - Full-text search across class names, method names, and message content
|
||||
- **Export** - Save filtered logs to .txt file with timestamp
|
||||
- **Count** - Shows `filtered/total` log count
|
||||
|
||||
---
|
||||
|
||||
## Visual Indicators
|
||||
|
||||
**Color Coding:**
|
||||
- White: Debug/Info (normal operation)
|
||||
- Yellow: Warning (non-critical issues)
|
||||
- Red: Error (critical failures)
|
||||
|
||||
**Alternating Rows:** Light/dark grey backgrounds improve readability for dense log output.
|
||||
|
||||
---
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Event Broadcasting:**
|
||||
```csharp
|
||||
Logging.OnLogEntryAdded += (LogEntry entry) => { /* handle */ };
|
||||
```
|
||||
|
||||
**Manual Log Retrieval:**
|
||||
```csharp
|
||||
List<LogEntry> recentLogs = Logging.GetRecentLogs();
|
||||
```
|
||||
|
||||
**LogEntry Structure:**
|
||||
- `ClassName` - Captured from calling file path
|
||||
- `MethodName` - Captured from `CallerMemberName`
|
||||
- `Message` - User-provided message text
|
||||
- `Level` - Debug/Info/Warning/Error enum
|
||||
- `Timestamp` - Time.realtimeSinceStartup
|
||||
- `FullFormattedMessage` - Complete formatted string
|
||||
|
||||
---
|
||||
BIN
docs/media/custom_console.png
Normal file
BIN
docs/media/custom_console.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Reference in New Issue
Block a user