- Home /
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?
If you want to view logs on devices, I recommend you use this plugin: https://www.assetstore.unity3d.com/#!/content/44935
You can still use Debug.Log(), while you have a debug console on devices.
Screenshot:
Answer by Inhuman Games · Nov 24, 2013 at 06:14 PM
Our editor console replacement asset has this feature, see the second feature discussed in this video:
It looks great. And it also looks like it would work with DebugConsole
. But, just to make sure... Will it?
If I understand your issue correctly, when you click on a log entry in Unity's console window, you are taken to DebugConsole.cs ins$$anonymous$$d of the true source of the log - in your case, the call to DebugConsole.Log()? If I got you right, then yes, our replacement console will take you to the correct source of the log, just as you saw in the video. You just need to tell our console what wrapper function(s) to ignore. In your case, I think it's DebugConsole.Log, Add$$anonymous$$essage and maybe one or two other functions.
Awesome. So, your actual answer to my problem basically is "Don't know if Unity's console do or ever will support that. But you can roll your own panel and make it work. Here's ours implementation.", right? :D
Yeah, that's right. I don't think Unity's console currently supports exactly what you are asking for. It does support something close though. You can hook into calls to UnityEngine.Debug.Log() using Application.RegisterLogCallback(). You'd want to register DebugConsole.Log() or something that calls it. Then, in your app, you'd replace all your calls to DebugConsole.Log() with UnityEngine.Debug.Log(). Perhaps that will work for you. For me, this way did not work well with my project, which is part of why I rolled my own panel.
Answer by devluz · Sep 11, 2014 at 05:54 AM
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("\n");
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[i].GetMethod();
if (foundStart == false && mb.DeclaringType != typeof(TLog))
{
file = FormatFileName(stackFrames[i].GetFileName());
line = stackFrames[i].GetFileLineNumber();
col = stackFrames[i].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("</color>");
message.Append(" (at ");
//the first stack message is found now we add the other stack frames to the log
message.Append(FormatFileName(stackFrames[i].GetFileName()));
message.Append(":");
message.Append(stackFrames[i].GetFileLineNumber());
message.Append(")");
message.Append("\n");
}
}
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 logged in so I can say - Wow. This code actually parses stackTrace, and instructs Unity's built in console which file/line to open. No DLL necessary, no custom console window necessary. $$anonymous$$udos to you sir. Not sure how this will go for my project (since it shows up as error only), but it's nice to see it possible even though hacky ;-)
Answer by ianjosephfischer · Jan 12, 2013 at 09:28 AM
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[] {'\n' });
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], '\n'));
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;
}
So you can simulate the double click with scripting only. That's quite interesting, but maybe the solution to the question wouldn't even require this much scripting. Either way, I'm still clueless. :P
You don't need Reflection to call InternalEditorUtility.OpenFileAtLineExternal, just call it directly...
It was private back then, they must have changed it.
What I meant in my previous comment is that I'm still clueless on how to use your solution... By the way, if by "set the console double click goto position" you mean context, I don't know when this was implemented but it's there already. Take a look.
Answer by DannyLZS · Nov 25, 2013 at 11:20 AM
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
Yeah... I don't think it's as simple as you imply here. Debug
is a sealed class
so it can't be overridden, to begin with. And DLL? Since when "sticking a DLL" is something simple? I don't even know what you mean there. The DebugConsole
is mostly trying to do what you said already. $$anonymous$$aybe it would help if you could elaborate a lot more here.
I think you will find if you do this ...
public static class Debug
{
public static void Log(object message)
{
.... code here
}
}
You will in all intents and purposes be over-writing Debug.Log
Whenever you call Debug.Log from now on it will actually point to your own class.
Now if you log something and double click it it will go to this NEW class you have made. BUT ... if you build it out as a DLL it will bypass this and go to the correct file and line who sent it to this guy.
Just trying to point you in the right direction
Ok, so we can roll our own "Debug" implementation (which DebugConsole
already do). The new bits you bring here are "do it with the same name, so you have the risk of getting duplicated definition if you ever use same namespaces" (which, again, is precisely what DebugConsole
do, except without namespace issues) and "build it out as a DLL", which I got no clue how to do or why it would be helpful. If "build a DLL" is the right direction, I'd like to see more pointers, please! (Other than http://docs.unity3d.com/Documentation/$$anonymous$$anual/UsingDLL.html )
Why override? We have Application.LogCallback() (http://docs.unity3d.com/Documentation/ScriptReference/Application.LogCallback.html)!
Answer by iwHiteRabbiT · Mar 12, 2015 at 01:00 PM
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!