Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save seobyeongky/bea8044a30dedc40e30dcb7d097900bb to your computer and use it in GitHub Desktop.
Save seobyeongky/bea8044a30dedc40e30dcb7d097900bb to your computer and use it in GitHub Desktop.
DebugUtils
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