using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using UnityEngine; namespace Core { /// /// Centralized logging system with automatic class/method tagging and editor integration. /// Broadcasts log entries to custom editor windows for filtering and analysis. /// public static class Logging { /// /// Event fired when a new log entry is added. Subscribe to this in editor windows. /// public static event Action OnLogEntryAdded; // Store recent logs for late-subscriber editor windows (e.g., windows opened after play mode started) private static readonly List RecentLogs = new List(); private const int MaxStoredLogs = 5000; // Prevent memory bloat /// /// Get all recent logs. Used by editor windows when they first open. /// public static IReadOnlyList GetRecentLogs() => RecentLogs; /// /// Clear all stored logs. Useful for editor windows "Clear" button. /// public static void ClearLogs() { RecentLogs.Clear(); } [System.Diagnostics.Conditional("ENABLE_LOG")] public static void Debug(string message, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "") { LogInternal(LogLevel.Debug, message, filePath, memberName); } [System.Diagnostics.Conditional("ENABLE_LOG")] public static void Info(string message, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "") { LogInternal(LogLevel.Info, message, filePath, memberName); } [System.Diagnostics.Conditional("ENABLE_LOG")] public static void Warning(string message, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "") { LogInternal(LogLevel.Warning, message, filePath, memberName); } public static void Error(string message, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "") { LogInternal(LogLevel.Error, message, filePath, memberName); } private static void LogInternal(LogLevel level, string message, string filePath, string memberName) { string className = Path.GetFileNameWithoutExtension(filePath); string formattedMessage = $"[{className}][{memberName}] {message}"; // Create log entry var entry = new LogEntry(className, memberName, message, level, Time.realtimeSinceStartup); // Store for late subscribers RecentLogs.Add(entry); if (RecentLogs.Count > MaxStoredLogs) RecentLogs.RemoveAt(0); // Broadcast to editor windows (editor-only, won't fire in builds) OnLogEntryAdded?.Invoke(entry); // Also log to Unity console switch (level) { case LogLevel.Debug: case LogLevel.Info: UnityEngine.Debug.Log(formattedMessage); break; case LogLevel.Warning: UnityEngine.Debug.LogWarning(formattedMessage); break; case LogLevel.Error: UnityEngine.Debug.LogError(formattedMessage); break; } } } /// /// Represents a single log entry with class, method, message, level, and timestamp. /// public class LogEntry { public string ClassName { get; } public string MethodName { get; } public string Message { get; } public LogLevel Level { get; } public float Timestamp { get; } public LogEntry(string className, string methodName, string message, LogLevel level, float timestamp) { ClassName = className; MethodName = methodName; Message = message; Level = level; Timestamp = timestamp; } /// /// Formatted message with class and method tags. /// Format: [ClassName][MethodName] Message /// public string FormattedMessage => $"[{ClassName}][{MethodName}] {Message}"; /// /// Full formatted message with timestamp and level. /// Format: [12.34s][Debug][ClassName][MethodName] Message /// public string FullFormattedMessage => $"[{Timestamp:F2}s][{Level}]{FormattedMessage}"; } /// /// Log severity levels. /// public enum LogLevel { Debug, Info, Warning, Error } }