Files
2026-02-26 15:01:46 +07:00

227 lines
8.0 KiB
C#

using System;
using System.IO;
using UnityEngine;
namespace BrewMonster
{
/// <summary>
/// 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
/// </summary>
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";
/// <summary>
/// Auto-initializes when play mode starts.
/// Creates the SessionFileLogger GameObject automatically.
/// This ensures a fresh log file is created for each play session.
/// </summary>
[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<SessionFileLogger>();
}
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();
}
/// <summary>
/// Initializes a new log file for the current play session.
/// Overwrites old log files and creates a new one with timestamp in filename.
/// </summary>
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}");
}
}
/// <summary>
/// Deletes old log files with the same prefix to keep only the current session file.
/// </summary>
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}");
}
}
/// <summary>
/// Writes a log message to the file.
/// Called by BMLogger when Log, LogError, or LogWarning is called.
/// </summary>
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}");
}
}
}
/// <summary>
/// Cleans up the log file and removes BMLogger callback.
/// </summary>
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}");
}
}
}
/// <summary>
/// Gets the current log file path (for debugging/inspection).
/// </summary>
public static string GetLogFilePath()
{
return s_Instance != null ? s_Instance.m_LogFilePath : null;
}
}
}