using System; using System.IO; using UnityEngine; namespace BrewMonster { /// /// Handles file logging for each Unity play session. /// Overwrites the same log file when play mode starts, with timestamp in filename to detect updates. /// Intercepts BMLogger calls and writes them to the session log file. /// /// Usage: /// - Automatically initializes when play mode starts /// - Call BMLogger.LogError("HIHIHIHI") and it will appear in the log file /// - Each play session overwrites the log file (old files are cleaned up) /// - Filename includes timestamp so you can see when it was last updated /// - Log files are saved in the "Logs" directory in your project root /// public class SessionFileLogger : MonoBehaviour { private static SessionFileLogger s_Instance = null; private string m_LogFilePath = null; private StreamWriter m_LogWriter = null; private readonly object m_LockObject = new object(); [Header("Log Settings")] [Tooltip("Directory where log files will be saved (relative to project root)")] public string logDirectory = "Logs"; [Tooltip("Log file name prefix")] public string logFileNamePrefix = "SessionLog"; /// /// Auto-initializes when play mode starts. /// Creates the SessionFileLogger GameObject automatically. /// This ensures a fresh log file is created for each play session. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void AutoInitialize() { // Clean up any existing instance (in case of play mode restart) if (s_Instance != null) { if (s_Instance.m_LogWriter != null) { s_Instance.Cleanup(); } if (s_Instance.gameObject != null) { DestroyImmediate(s_Instance.gameObject); } s_Instance = null; } // Create new instance for this play session GameObject loggerObj = new GameObject("SessionFileLogger"); s_Instance = loggerObj.AddComponent(); } private void Awake() { // Ensure only one instance exists if (s_Instance != null && s_Instance != this) { Destroy(this); return; } s_Instance = this; DontDestroyOnLoad(gameObject); InitializeLogFile(); } private void OnDestroy() { if (s_Instance == this) { Cleanup(); s_Instance = null; } } private void OnApplicationQuit() { Cleanup(); } /// /// Initializes a new log file for the current play session. /// Overwrites old log files and creates a new one with timestamp in filename. /// private void InitializeLogFile() { try { // Get project root directory string projectRoot = Application.dataPath.Replace("/Assets", "").Replace("\\Assets", ""); string logDir = Path.Combine(projectRoot, logDirectory); // Ensure directory exists if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } // Clean up old log files with the same prefix (keep only the latest) CleanupOldLogFiles(logDir); // Generate log file path with timestamp (so you can see when it was updated) string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string fileName = $"{logFileNamePrefix}_{timestamp}.txt"; m_LogFilePath = Path.Combine(logDir, fileName); // Create/overwrite the log file m_LogWriter = new StreamWriter(m_LogFilePath, false); m_LogWriter.WriteLine($"=== Play Session Log ==="); m_LogWriter.WriteLine($"Started: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); m_LogWriter.WriteLine($"Project: {Application.productName}"); m_LogWriter.WriteLine($"Unity Version: {Application.unityVersion}"); m_LogWriter.WriteLine($"=========================================="); m_LogWriter.WriteLine(); m_LogWriter.Flush(); // Set up BMLogger callback BMLogger.SetFileLogCallback(WriteLogMessage); Debug.Log($"[SessionFileLogger] Log file initialized: {m_LogFilePath}"); } catch (Exception ex) { Debug.LogError($"[SessionFileLogger] Failed to initialize log file: {ex.Message}"); } } /// /// Deletes old log files with the same prefix to keep only the current session file. /// private void CleanupOldLogFiles(string logDir) { try { if (!Directory.Exists(logDir)) return; string searchPattern = $"{logFileNamePrefix}_*.txt"; string[] oldFiles = Directory.GetFiles(logDir, searchPattern); foreach (string oldFile in oldFiles) { try { File.Delete(oldFile); } catch (Exception ex) { Debug.LogWarning($"[SessionFileLogger] Failed to delete old log file {oldFile}: {ex.Message}"); } } } catch (Exception ex) { Debug.LogWarning($"[SessionFileLogger] Error cleaning up old log files: {ex.Message}"); } } /// /// Writes a log message to the file. /// Called by BMLogger when Log, LogError, or LogWarning is called. /// private void WriteLogMessage(string message) { if (m_LogWriter == null || string.IsNullOrEmpty(m_LogFilePath)) return; lock (m_LockObject) { try { string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); m_LogWriter.WriteLine($"[{timestamp}] {message}"); m_LogWriter.Flush(); } catch (Exception ex) { Debug.LogError($"[SessionFileLogger] Failed to write log message: {ex.Message}"); } } } /// /// Cleans up the log file and removes BMLogger callback. /// private void Cleanup() { lock (m_LockObject) { try { if (m_LogWriter != null) { m_LogWriter.WriteLine(); m_LogWriter.WriteLine($"=========================================="); m_LogWriter.WriteLine($"Session ended: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); m_LogWriter.Flush(); m_LogWriter.Close(); m_LogWriter = null; } BMLogger.ClearFileLogCallback(); } catch (Exception ex) { Debug.LogError($"[SessionFileLogger] Error during cleanup: {ex.Message}"); } } } /// /// Gets the current log file path (for debugging/inspection). /// public static string GetLogFilePath() { return s_Instance != null ? s_Instance.m_LogFilePath : null; } } }