본문 바로가기

Develop/Unity

[Unity] Custom Debug Log 사용 시 파일 연결하기

유니티에서 제공하는 Debug.Log를 커스터마이징 하고 싶을 때가 많다. 자체적으로 제공하는 콜백이나 로거를 커스텀하여 사용할 수도 있지만 Debug 클래스를 자체 제작하여 덮어 씌우는 방법을 많이 사용할 것이다.

public static class Debug {
	public static void Log(object message){
    	// ~~~~
        // CUSTOM!!!
        // ~~~~
        UnityEngine.Debug.Log(message);
    }
}

 

이때, 유니티 에디터의 콘솔에서 찍힌 로그를 더블 클릭할 경우 해당 로그가 시작한 부분, 즉 커스텀 로그를 찍은 부분의 파일이 열리는 것이 아니라 커스텀한 로그 파일 자체가 열리게 된다. (위의 코드의 경우 2번째 줄)

이를 피하기 위해 로그를 더블 클릭할 시 경로를 추적하여 실제 열리기를 바랐던 파일을 열 수 있다.

 

static string LoggerName = "CustomDebug";

[OnOpenAsset(0)]
static bool OnOpenAsset(int instance, int line) {
	string name = EditorUtility.InstanceIDToObject(instance).name;
	if (name != LoggerName) return false;

	string stack_trace = GetStackTrace();
	if(!string.IsNullOrEmpty(stack_trace)) {
		Match matches = Regex.Match(stack_trace, @"\(at(.+)\)", RegexOptions.IgnoreCase);
		if (matches.Success) {
			matches = matches.NextMatch();
			if (matches.Success) {
				string pathline;
				pathline = matches.Groups[1].Value;
				pathline = pathline.Replace(" ", "");

				int split_index = pathline.LastIndexOf(":");
				string path = pathline.Substring(0, split_index);
				line = Convert.ToInt32(pathline.Substring(split_index + 1));
				string fullpath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
				fullpath = fullpath + path;
				UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullpath, line);
			}
		}
		return true;
	}
	return false;
}

 

콘솔에서 더블클릭으로 에셋을 열 경우 유니티에서 제공한 OnOpenAsset Attribute가 붙은 함수가 불리게 된다. 이 이벤트가 불렸을 때, 부른 오브젝트를 추적하여 커스텀한 오브젝트가 아닐 경우 원래 동작으로 돌아간다.

커스텀 로그에서 불렀을 경우 스택을 추적하여 한 단계 위의 경로를 얻어낸다.

그 후 유니티에서 제공하는 OpenFileAtLineExternal 함수를 통해 실제 파일을 열 수 있다.

해당 함수는 Editor 스크립트에서만 동작한다.

 

스택을 추적하는 함수 GetStackTrace는 아래와 같이 구현한다.

static string GetStackTrace() {
	var assembly_unity_editor = Assembly.GetAssembly(typeof(EditorWindow));
	if (assembly_unity_editor == null) return null;

	var type_console_window = assembly_unity_editor.GetType("UnityEditor.ConsoleWindow");
	if (type_console_window == null) return null;
	var field_console_window = type_console_window.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
	if (field_console_window == null) return null;
	var instance_console_window = field_console_window.GetValue(null);
	if (instance_console_window == null) return null;

	if ((object)EditorWindow.focusedWindow == instance_console_window) {
		var type_list_view_state = assembly_unity_editor.GetType("UnityEditor.ListViewState");
		if (type_list_view_state == null) return null;

		var field_list_view = type_console_window.GetField("m_ListView", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field_list_view == null) return null;

		var value_list_view = field_list_view.GetValue(instance_console_window);
		if (value_list_view == null) return null;

		var field_active_text = type_console_window.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field_active_text == null) return null;

		string value_active_text = field_active_text.GetValue(instance_console_window).ToString();
		return value_active_text;
	}
	return null;
}

 

 

참고 및 출처 : Double-click the redirect script in Unity's Console window.