Unity的日志控制:
日志系统打开,但是只打印错误日志。这样其他级别的日志就不会有打印消耗。
UnityEngine.Debug.unityLogger.logEnabled = true;
Debug.unityLogger.filterLogType = LogType.Error;
但是还是有字符串拼接的消耗。
Conditional属性是一个C#特性,它允许你根据预处理器指令的定义来有条件地执行方法。例如下面的代码:
[Conditional("OPEN_MAIN_LOG_LOGWARNING")]
public static void Log(object message,Object context)
{
UnityEngine.Debug.Log(message,context);
}
如果没有OPEN_MAIN_LOG_LOGWARNING宏,编译的时候,所有调用Log的方法都会从Dll里去除。因此就没有了字符串拼接的消耗。
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditorInternal;
using UnityEngine;
///
/// 日志重定向相关的实用函数。
///
internal static class LogRedirection
{
private static readonly Regex LogRegex = new Regex(@" \(at (.+)\:(\d+)\)\r?\n");
[OnOpenAsset(0)]
private static bool OnOpenAsset(int instanceId, int line)
{
var scriptComponent = EditorUtility.InstanceIDToObject(instanceId);
string selectedStackTrace = GetSelectedStackTrace();
if (string.IsNullOrEmpty(selectedStackTrace))
{
return false;
}
//是ILRuntime下日志
if (selectedStackTrace.Contains("ILRuntime StackTrace"))
{
//如果跳转的不是我们的GalaDebugger,证明打开的不是第一行的url,让系统跳转
if (!scriptComponent.name.Contains("GalaDebugger"))
{
return false;
}
Match match = LogRegex.Match(selectedStackTrace);
if (!match.Success)
{
return false;
}
InternalEditorUtility.OpenFileAtLineExternal(match.Groups[1].Value, int.Parse(match.Groups[2].Value));
return true;
}
else
{
//操作本质就是
//1,拿到堆栈 取得堆栈内容判断 找出正确的打开行
//2,每一个Groups实际上就是一行
//3, Groups[1]取得正好是代码路径
//4 当然自己根据实际的封装路径来搜寻正确的路径
//5,因为目前日志内容带有url属性,可以直接跳转了,因此只要跳转的路径不是默认的路径(也就是第一个路径日志),都按其系统跳转
if (!selectedStackTrace.Contains("GalaDebugger:"))
{
return false;
}
Match match = LogRegex.Match(selectedStackTrace);
if (!match.Success)
{
return false;
}
//此时找到了第一个,如果跳转的不是第一个,证明我们手动选中了其它url ---注意这里存在一个缺陷,如果选中的跳转url正好和默认路径的line一致,则会仍然跳转日志打印处
//理论上可以判断脚本的instanceId
int targetLine = int.Parse(match.Groups[2].Value);
if (targetLine != line)
{
return false;
}
if (!match.Groups[1].Value.Contains("GalaDebugger.cs"))
{
return false;
}
match = match.NextMatch();
if (!match.Success)
{
return false;
}
if (match.Groups[1].Value.Contains("GalaDebugger.cs"))
{
match = match.NextMatch();
if (!match.Success)
{
return false;
}
}
InternalEditorUtility.OpenFileAtLineExternal(
Path.Combine(Application.dataPath, match.Groups[1].Value.Substring(7)),
int.Parse(match.Groups[2].Value));
return true;
}
}
private static string GetSelectedStackTrace()
{
Assembly editorWindowAssembly = typeof(EditorWindow).Assembly;
if (editorWindowAssembly == null)
{
return null;
}
System.Type consoleWindowType = editorWindowAssembly.GetType("UnityEditor.ConsoleWindow");
if (consoleWindowType == null)
{
return null;
}
FieldInfo consoleWindowFieldInfo =
consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
if (consoleWindowFieldInfo == null)
{
return null;
}
EditorWindow consoleWindow = consoleWindowFieldInfo.GetValue(null) as EditorWindow;
if (consoleWindow == null)
{
return null;
}
if (consoleWindow != EditorWindow.focusedWindow)
{
return null;
}
//目的就是要拿到当前选中的文本
FieldInfo activeTextFieldInfo =
consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
if (activeTextFieldInfo == null)
{
return null;
}
return (string)activeTextFieldInfo.GetValue(consoleWindow);
}
}
unsafe static void CLRRedirection(ILRuntime.Runtime.Enviorment.AppDomain appdomain)
{
Type debugType = typeof(DebugEX);
var logMethod = debugType.GetMethod("Log", new[] { typeof(object) });
appdomain.RegisterCLRMethodRedirection(logMethod, Log);
var logWarningMethod = debugType.GetMethod("LogWarning", new[] { typeof(object) });
appdomain.RegisterCLRMethodRedirection(logWarningMethod, LogWarning);
var logErrorMethod = debugType.GetMethod("LogError", new[] { typeof(object) });
appdomain.RegisterCLRMethodRedirection(logErrorMethod, LogError);
}
///
/// DebugEX.Log 实现
///
///
///
///
///
///
///
unsafe static StackObject* Log(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
__intp.Free(ptr_of_this_method);
var stacktrace = __domain.DebugService.GetStackTrace(__intp);
DebugEX.Log(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
return __ret;
}
///
/// DebugEX.LogError 实现
///
///
///
///
///
///
///
unsafe static StackObject* LogError(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
__intp.Free(ptr_of_this_method);
var stacktrace = __domain.DebugService.GetStackTrace(__intp);
DebugEX.LogError(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
return __ret;
}
///
/// DebugEX.LogWarning 实现
///
///
///
///
///
///
///
unsafe static StackObject* LogWarning(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack,
CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
object message = typeof(object).CheckCLRTypes(StackObject.ToObject(ptr_of_this_method, __domain, __mStack));
__intp.Free(ptr_of_this_method);
var stacktrace = __domain.DebugService.GetStackTrace(__intp);
DebugEX.LogWarning(message + "\n\n==========ILRuntime StackTrace==========\n" + stacktrace);
return __ret;
}