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
}
}