DebugConsole console clicking

This amazing script will allow us to have a console anywhere outside the Editor. Freaking awesome!

But while in the editor, for all messages it does bring, we can’t any longer click on them to bring the file and line, like it still happens on unplanned errors and warnings. That’s because instead of using print or debug.log we use it:

DebugConsole.Log("Hello World");

So the Console will now bring something like this:

Hello World
UnityEngine.Debug:Log(Object)
DebugConsole:Log(String) (at Assets/scripts/Plugin/debugging/DebugConsole.cs:294)
Test:Start() (at Assets/Test.cs:7)

And, when we click on it, it will open DebugConsole.cs at line 294 instead of Test at line 7, as anyone would wish.

Is there some way to “fix” this?

I got annoyed about this problem as well today. I found an internal method to allow jumps in any file and line in the standard unity log:

string file = "Asset\MyTestFile.cs";
int line = 100;

mUnityLog = typeof(UnityEngine.Debug).GetMethod("LogPlayerBuildError", BindingFlags.NonPublic | BindingFlags.Static);
mUnityLog.Invoke(null, new object[] { message.ToString(), file, line, 0 });

Related problems: only works in the editor and it will be shown as Error like the Debug.LogError method.

I use it like this in my logger:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using UnityEngine;

public class TLog
{
    public static readonly string TAG_ERROR = "error";
    public static readonly string TAG_WARNING = "warning";
    public static readonly string TAG_INFO = "info";

    public static readonly bool doLog = true;
    public static readonly bool doFilter = true;
    public static readonly String[] visibleTags = new String[] { TAG_ERROR, TAG_WARNING, TAG_INFO};

    public static readonly bool showParameters = true;


    private static MethodBase mUnityLog;

    static TLog()
    {
        mUnityLog = typeof(UnityEngine.Debug).GetMethod("LogPlayerBuildError", BindingFlags.NonPublic | BindingFlags.Static);
                
    }


    public static void Log(string msg, params string[] tags)
    {
        LogArray(msg, tags);
    }
    public static void LogArray(string msg, string[] tags)
    {
        //quit if no log is required
        if (doLog == false)
            return;
        //filter needed? if yes -> check if one of the tags is in our filter list
        if (doFilter && visibleTags.Intersect(tags).Any() == false)
            return;
        if (mUnityLog == null)
        {
            //this happens outside of the editor mode
            //log the normal way and ignore everything that isn't an error
            if (tags != null && tags.Contains(TAG_ERROR))
            {
                UnityEngine.Debug.LogError(msg);
            }
            return;
        }

        StringBuilder message = new StringBuilder();

        StackTrace stackTrace = new StackTrace(true);
        StackFrame[] stackFrames = stackTrace.GetFrames();
        int line = 0;
        string file = "";
        int col = 0;

        //no tags? just print plain
        if (tags == null || tags.Length == 0)
        {
            message.Append(msg);
        }
        else
        {
            //print tags
            message.Append("<color=green><size=7>");
            for (int k = 0; k < tags.Length; k++)
            {
                message.Append(tags[k]);
                if(k + 1 < tags.Length)
                    message.Append("|");
            }
            message.Append("</size></color>");

            //print the color tag dependend
            if (tags.Contains(TAG_ERROR))
            {
                message.Append("<color=red>");
                message.Append(msg);
                message.Append("</color>");
            }
            else if (tags.Contains(TAG_WARNING))
            {
                message.Append("<b><color=yellow>");
                message.Append(msg);
                message.Append("</color></b>");
            }
            else
            {
                message.Append(msg);
            }
        }
        message.Append("

");

        bool foundStart = false;
        //look for the first method call in the stack that isn't from this class.
        //save the first one to jump into it later and add all further lines to the log
        for (int i = 0; i < stackFrames.Length; i++)
        {
            MethodBase mb = stackFrames*.GetMethod();*

if (foundStart == false && mb.DeclaringType != typeof(TLog))
{
file = FormatFileName(stackFrames*.GetFileName());*
line = stackFrames*.GetFileLineNumber();*
col = stackFrames*.GetFileColumnNumber();*
foundStart = true;
}

if(foundStart)
{
message.Append(“<color=blue>”);
message.Append(mb.DeclaringType.FullName);
message.Append(“:”);
message.Append(mb.Name);
message.Append(“(”);
if (showParameters)
{
ParameterInfo[] paramters = mb.GetParameters();
for (int k = 0; k < paramters.Length; k++)
{
message.Append(paramters[k].ParameterType.Name);
if (k + 1 < paramters.Length)
message.Append(", ");
}
}

message.Append(“)”);
message.Append(“”);

message.Append(" (at ");
//the first stack message is found now we add the other stack frames to the log
message.Append(FormatFileName(stackFrames*.GetFileName()));*
message.Append(“:”);
message.Append(stackFrames*.GetFileLineNumber());*
message.Append(“)”);
message.Append("
");

}
}
mUnityLog.Invoke(null, new object[] { message.ToString(), file, line, col });
}

private static string FormatFileName(String file)
{
//remove everything of the absolute path that is before the Assetfolder
//using the destination of the Assetfolder to get the right length (not ideal)
return file.Remove(0, Application.dataPath.Length - “Assets”.Length);
}
}

I’ve been working on something like this. I can open the external editor at the correct spot, but I am searching for something that will let me set the console double click goto position.

Here is my code for that which gets called by my internal debugging system. If you found out how let me know or if any one else has any tips.

	private static void OpenFile()
	{
	    Assembly assembly = Assembly.GetAssembly(typeof(UnityEditor.SceneView));
	    System.Type type = assembly.GetType("UnityEditorInternal.InternalEditorUtility");
		if(type == null)
		{
			Debug.Log("Failed to open source file");
			return;
		}

		string[] stackFrames = System.Environment.StackTrace.Split(new char[] {'

’ });
string callingFrame = stackFrames[4];

		string[] splitLog = callingFrame.Split(':');
		char drive = splitLog[0][splitLog[0].Length-1];
		string filePath = splitLog[1];
		splitLog[2] = StringManipulation.TrimStartString(
			splitLog[2], "line ");
		int lineNumber = int.Parse(StringManipulation.GetBeforeNextChar(
			splitLog[2], '

'));

		string fullDrive = drive + ":" + filePath;

		//Debug.Log(@fullDrive + " line #" + lineNumber );
		MethodInfo method = type.GetMethod("OpenFileAtLineExternal");
		method.Invoke(method, new object[] { @fullDrive, lineNumber });
	}

(Here is that “StringManipulation” method I use which I am sure you could have figured out if you are reading this question.

public static string GetBeforeNextChar(string sourceString, char trimed)
{
	string[] splits = sourceString.Split(trimed);
	return splits[0];
}
public static string TrimStartCharacter(string sourceString, char trimed)
{
	sourceString = sourceString.TrimStart(trimed);
	return sourceString;
}

Our editor console replacement asset has this feature, see the second feature discussed in this video:

Console Enhanced

Why don’t you just override the normal Debug.Log and make it do both you’re console log and the normal unity one at the same time. Then stick it in a DLL so that it goes to the correct line

I had the same problem, then I just made a simple DLL (with Mono) with this unique static class :

using System;
using UnityEngine;

namespace [MyNameSpace]
{
	public static class MyDebug
	{
		public static string TAG = "MyUnity";

		public static string CurrentClass
		{
			get {
				var st = new System.Diagnostics.StackTrace();

				var index = Mathf.Min(st.FrameCount - 1, 2);

				if (index < 0)
					return "{NoClass}";

				return "{" + st.GetFrame(index).GetMethod().DeclaringType.Name + "}";
			}
		}

		public static void Log(string Msg)
		{
			if (Debug.isDebugBuild)
				Debug.Log(string.Format("{0}:{1}:{2}", TAG, CurrentClass, Msg));
		}

		public static void LogWarning(string Msg)
		{
			Debug.LogWarning(string.Format("{0}:{1}:{2}", TAG, CurrentClass, Msg));
		}

		public static void LogError(string Msg)
		{
			Debug.LogError(string.Format("{0}:{1}:{2}", TAG, CurrentClass, Msg));
		}
	}
}

Then, back in Unity you just have to past your plugin.dll in your asset folder and in the code side:

using [MyNameSpace];

// In method
MyDebug.Log("My message log");

In the console you will see:
“MyUnity:{ClassName}:My message log”
And if you dbl click on it you will open the ClassName file at the right line!

It works like a charm!