Created
July 19, 2025 06:08
-
-
Save seobyeongky/bea8044a30dedc40e30dcb7d097900bb to your computer and use it in GitHub Desktop.
DebugUtils
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Text; | |
using UnityEditor; | |
using UnityEngine; | |
using Debug = UnityEngine.Debug; | |
public class DebugUtils1 | |
{ | |
public static string Indent = ""; | |
protected static StringBuilder sbShared = new StringBuilder(); | |
public static string GetHierarchyPath(GameObject go) | |
{ | |
string str = go.name; | |
while (go.transform.parent != null) | |
{ | |
go = go.transform.parent.gameObject; | |
str = $"{go.name}/" + str; | |
} | |
return str; | |
} | |
static bool fileLogInitialized = false; | |
/// <summary> | |
/// log to log.txt file (at cwd) | |
/// </summary> | |
/// <param name="msg"></param> | |
public static void FileLog(string msg) | |
{ | |
StreamWriter sw = File.AppendText("log.txt"); | |
if (!fileLogInitialized) | |
{ | |
// File.WriteAllText("log.txt", ""); // 초기화 => 불필요한듯? | |
sw.WriteLine("------------------NewDomain----------------------"); | |
fileLogInitialized = true; | |
} | |
// var fs = new FileStream("log.txt", FileMode.OpenOrCreate); | |
sw.WriteLine(msg); | |
sw.Close(); | |
sw.Dispose(); | |
// fs.Close(); | |
} | |
static int _indentLevel = 0; | |
static int indentLevel | |
{ | |
get => _indentLevel; | |
set | |
{ | |
_indentLevel = value; | |
sbShared.Clear(); | |
for (int i = 0; i < 2 * indentLevel; i++) | |
sbShared.Append(' '); | |
Indent = sbShared.ToString(); | |
} | |
} | |
protected static Transform lastT = null; | |
public static Stack<Section> sectionStack = new(); | |
[HideInCallstack] | |
public static void SectionBegin(string msg) | |
{ | |
Debug.Log(Indent + GetColoredSectorString(msg)); | |
sectionMaker.NewSection(true); | |
} | |
[HideInCallstack] | |
public static void SectionBeginNoHighlight(string msg) | |
{ | |
Debug.Log(Indent + msg); | |
sectionMaker.NewSection(true); | |
} | |
public static void SectionEnd() | |
{ | |
Section.Exit(); | |
} | |
[HideInCallstack] | |
public static Section EnterSection(string msg, bool active = true) | |
{ | |
if (active) | |
{ | |
Debug.Log(Indent + GetColoredSectorString(msg)); | |
return sectionMaker.NewSection(active); | |
} | |
return null; | |
} | |
// public static Section EnterSectionNoHighlight(string msg, bool active = true) | |
// { | |
// if (active) | |
// Debug.Log(Indent + msg); | |
// | |
// return sectionMaker.NewSection(active); | |
// } | |
static StringBuilder sbCached = new(); | |
// 태그 손실 방지 | |
internal static string GetColoredSectorString(string name) | |
{ | |
var lines = name.Split('\n'); | |
sbCached.Clear(); | |
sbCached.Append("<color=\"#fcfcfc\">"); | |
sbCached.Append(lines[0]); | |
sbCached.Append("</color>"); | |
for (int i = 1; i < lines.Length; i++) | |
{ | |
sbCached.Append('\n'); | |
sbCached.Append(lines[i]); | |
} | |
return sbCached.ToString(); | |
} | |
internal interface ISectionMaker | |
{ | |
Section NewSection(bool active); | |
} | |
internal static ISectionMaker sectionMaker = new Section.Maker(); | |
public class Section : IDisposable | |
{ | |
public Transform T; | |
bool active; | |
Section(bool active = true) | |
{ | |
this.active = active; | |
if (active) | |
Enter(this); | |
} | |
static void Enter(Section section) | |
{ | |
sectionStack.Push(section); | |
indentLevel++; | |
} | |
public void Dispose() | |
{ | |
if (active) | |
Exit(); | |
} | |
public static void Exit() | |
{ | |
if (sectionStack.Count == 0) | |
{ | |
Debug.LogError($"stack empty"); | |
return; | |
} | |
var poppedSection = sectionStack.Pop(); | |
if (poppedSection.T != null) | |
{ | |
lastT = null; | |
} | |
indentLevel--; | |
} | |
public class Maker : ISectionMaker | |
{ | |
Section ISectionMaker.NewSection(bool active) | |
{ | |
return new Section(active); | |
} | |
} | |
} | |
interface ITimeLogMaker | |
{ | |
TimeLogThing NewLog(bool active); | |
} | |
static ITimeLogMaker timeLogMaker = new TimeLogThing.Maker(); | |
[HideInCallstack] | |
public static TimeLogThing EnterTimeLog(string msg, bool active = true) | |
{ | |
if (active) | |
SectionBeginNoHighlight("Begin " + msg); | |
return timeLogMaker.NewLog(active); | |
} | |
public class TimeLogThing : IDisposable | |
{ | |
Stopwatch sw = new(); | |
bool active; | |
TimeLogThing(bool active) | |
{ | |
this.active = active; | |
if (active) | |
sw.Start(); | |
} | |
public void Dispose() | |
{ | |
if (active) | |
{ | |
SectionEnd(); | |
Debug.Log(Indent + $"End ... {sw.ElapsedMilliseconds}ms"); | |
sw.Stop(); | |
} | |
} | |
public class Maker : ITimeLogMaker | |
{ | |
TimeLogThing ITimeLogMaker.NewLog(bool active) | |
{ | |
return new TimeLogThing(active); | |
} | |
} | |
} | |
/// <summary> | |
/// ConsoleHelper tag붙을 때 쓰는용 | |
/// </summary> | |
/// <param name="c"></param> | |
/// <returns></returns> | |
public static string ColorString(Color c) | |
{ | |
if (c == Color.red) | |
return "red"; | |
else if (c == Color.blue) | |
return "blue"; | |
else if (c == Color.green) | |
return "green"; | |
else if (c == Color.black) | |
return "black"; | |
else if (c == Color.grey) | |
return "grey"; | |
return "#" + ColorUtility.ToHtmlStringRGB(c); | |
} | |
static Stack<string> additionalStack = new(); | |
public static void AdditionalStackPush(string msg) | |
{ | |
additionalStack.Push(msg); | |
} | |
public static void AdditionalStackPop() | |
{ | |
additionalStack.Pop(); | |
} | |
// static StringBuilder sbCached = new(); | |
/// <summary> | |
/// callstack에 부가적으로 필요한 정보들 (BT Node) | |
/// </summary> | |
/// <returns></returns> | |
public static string AdditionalStack | |
{ | |
get | |
{ | |
if (additionalStack.Count == 0) | |
return ""; | |
sbCached.Clear(); | |
var list = ListPoo<string>.Get(); | |
foreach (var item in additionalStack) | |
list.Add(item); | |
list.Reverse(); | |
for (int i = 0; i < list.Count; i++) | |
{ | |
sbCached.Append(list[i]); | |
if (i < list.Count - 1) | |
sbCached.Append('/'); | |
} | |
ListPoo<string>.Release(list); | |
return sbCached.ToString(); | |
} | |
} | |
/// <summary> | |
/// 괄호 버전 (없으면 빈 텍스트) | |
/// </summary> | |
public static string AdditionalStackWithBracket | |
{ | |
get | |
{ | |
var additionalStack = AdditionalStack; | |
if (additionalStack.Exists()) | |
return "(" + additionalStack + ")"; | |
else | |
return ""; | |
} | |
} | |
/// <summary> | |
/// 존재하는 argument들을 괄호로 표현해줌 (없으면 빈 문자열) | |
/// </summary> | |
/// <param name="args"></param> | |
/// <returns></returns> | |
public static string DumpAdditionalInfos(params string [] args) | |
{ | |
var sb = sbCached; | |
sb.Clear(); | |
var list = ListPoo<string>.Get(); | |
foreach (var arg in args) | |
{ | |
if (arg.Exists()) | |
list.Add(arg); | |
} | |
var ret = list.Count == 0 ? "" : list.Dump(); | |
ListPoo<string>.Release(list); | |
return ret; | |
} | |
} | |
public static class DebugExtensions1 | |
{ | |
static StringBuilder sbCached = new StringBuilder(); | |
/// <summary> | |
/// ConsoleHelper용 | |
/// </summary> | |
/// <param name="mat"></param> | |
/// <returns></returns> | |
public static string LogDump(this Matrix4x4 mat) | |
{ | |
sbCached.Clear(); | |
var sb = sbCached; | |
sb.Append("<MATRIX:"); | |
for (int i = 0; i < 16; i++) | |
{ | |
sb.Append(mat[i]); | |
if (i < 15) | |
sb.Append(','); | |
} | |
sb.Append('>'); | |
return sb.ToString(); | |
} | |
} | |
public static class DebugLogger | |
{ | |
public static string Indent => DebugUtils1.Indent; | |
[HideInCallstack] | |
public static DebugUtils1.Section LogEnterSection(string msg, bool active = true) | |
{ | |
if (active) | |
{ | |
Debug.Log(DebugUtils1.Indent + DebugUtils1.GetColoredSectorString(msg)); | |
return DebugUtils1.sectionMaker.NewSection(active); | |
} | |
return null; | |
} | |
[HideInCallstack] | |
public static void LogSectionBegin(string msg) | |
{ | |
Debug.Log(Indent + DebugUtils1.GetColoredSectorString(msg)); | |
DebugUtils1.sectionMaker.NewSection(true); | |
} | |
public static void LogSectionBeginNoHighlight(string msg) | |
{ | |
Debug.Log(Indent + msg); | |
DebugUtils1.sectionMaker.NewSection(true); | |
} | |
public static void SectionEnd() | |
{ | |
DebugUtils1.Section.Exit(); | |
} | |
[HideInCallstack] | |
public static void Log(string msg) | |
{ | |
Debug.Log(Indent + msg); | |
} | |
[HideInCallstack] | |
public static void Log(object o) | |
{ | |
Debug.Log(Indent + o); | |
} | |
[HideInCallstack] | |
public static void Log(object o, UnityEngine.Object context) | |
{ | |
Debug.Log(Indent + o, context); | |
} | |
[HideInCallstack] | |
public static void LogError(string msg) | |
{ | |
Debug.LogError(Indent + msg); | |
} | |
[HideInCallstack] | |
public static void LogError(string msg, UnityEngine.Object context) | |
{ | |
Debug.LogError(Indent + msg, context); | |
} | |
[HideInCallstack] | |
public static void LogWarning(string msg) | |
{ | |
Debug.LogWarning(Indent + msg); | |
} | |
[HideInCallstack] | |
public static void LogWarning(string msg, UnityEngine.Object context) | |
{ | |
Debug.LogWarning(Indent + msg, context); | |
} | |
//colorstring | |
public static string ColorString(Color c) | |
{ | |
return DebugUtils1.ColorString(c); | |
} | |
public static string AdditionalStack => DebugUtils1.AdditionalStack; | |
[HideInCallstack] | |
public static IDisposable EnterTimeLog(string p0, bool active = true) | |
{ | |
return DebugUtils1.EnterTimeLog(p0, active); | |
} | |
/// <summary> | |
/// 같은 내용이면 1초마다 한번씩만 출력하는 로그 | |
/// </summary> | |
/// <param name="msg"></param> | |
[HideInCallstack] | |
public static void LogThrottled(string msg) | |
{ | |
string key = msg; | |
float currentTime = Time.time; | |
if (logCache.TryGetValue(key, out float lastLogTime)) | |
{ | |
if (currentTime - lastLogTime < LOG_CACHE_DURATION) | |
return; // 1초 이내 중복 로그 무시 | |
} | |
logCache[key] = currentTime; | |
Debug.Log(Indent + msg); | |
} | |
/// <summary> | |
/// 같은 내용이면 1초마다 한번씩만 출력하는 에러 로그 | |
/// </summary> | |
/// <param name="msg"></param> | |
[HideInCallstack] | |
public static void LogErrorThrottled(string msg, UnityEngine.Object context = null) | |
{ | |
string key = "ERROR:" + msg; | |
float currentTime = Time.time; | |
if (logCache.TryGetValue(key, out float lastLogTime)) | |
{ | |
if (currentTime - lastLogTime < LOG_CACHE_DURATION) | |
return; | |
} | |
logCache[key] = currentTime; | |
Debug.LogError(Indent + msg, context); | |
} | |
/// <summary> | |
/// 같은 내용이면 1초마다 한번씩만 출력하는 경고 로그 | |
/// </summary> | |
/// <param name="msg"></param> | |
[HideInCallstack] | |
public static void LogWarningThrottled(string msg, UnityEngine.Object context = null) | |
{ | |
string key = "WARNING:" + msg; | |
float currentTime = Time.time; | |
if (logCache.TryGetValue(key, out float lastLogTime)) | |
{ | |
if (currentTime - lastLogTime < LOG_CACHE_DURATION) | |
return; | |
} | |
logCache[key] = currentTime; | |
Debug.LogWarning(Indent + msg, context); | |
} | |
/// <summary> | |
/// 로그 캐시 정리 (오래된 항목 제거) | |
/// </summary> | |
public static void CleanupLogCache() | |
{ | |
float currentTime = Time.time; | |
var keysToRemove = new List<string>(); | |
foreach (var kvp in logCache) | |
{ | |
if (currentTime - kvp.Value > LOG_CACHE_DURATION * 2) // 2초 후 캐시에서 제거 | |
{ | |
keysToRemove.Add(kvp.Key); | |
} | |
} | |
foreach (var key in keysToRemove) | |
{ | |
logCache.Remove(key); | |
} | |
} | |
// 로그 중복 방지를 위한 캐시 | |
private static Dictionary<string, float> logCache = new Dictionary<string, float>(); | |
private static readonly float LOG_CACHE_DURATION = 1f; // 1초 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment