C# 什么';Unity3D有一个很好的全局异常处理策略吗?
我正在考虑做一些Unity3D脚本编写的东西,我想建立一个全局异常处理系统。这不是为了在游戏的发行版中运行,目的是捕获用户脚本和编辑器脚本中的异常,并确保它们被转发到数据库进行分析(还向相关开发人员发送电子邮件,以便他们能够修复其漏洞) 在一个普通的C#应用程序中,我会尝试一下主要方法。在WPF中,我会钩住一个或多个。团结一致 到目前为止,我能想到的最好的办法是:C# 什么';Unity3D有一个很好的全局异常处理策略吗?,c#,unity3d,C#,Unity3d,我正在考虑做一些Unity3D脚本编写的东西,我想建立一个全局异常处理系统。这不是为了在游戏的发行版中运行,目的是捕获用户脚本和编辑器脚本中的异常,并确保它们被转发到数据库进行分析(还向相关开发人员发送电子邮件,以便他们能够修复其漏洞) 在一个普通的C#应用程序中,我会尝试一下主要方法。在WPF中,我会钩住一个或多个。团结一致 到目前为止,我能想到的最好的办法是: using UnityEngine; using System.Collections; public abstract clas
using UnityEngine;
using System.Collections;
public abstract class BehaviourBase : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
try
{
performUpdate();
print("hello");
}
catch (System.Exception e)
{
print(e.ToString());
}
}
public abstract void performUpdate();
}
在其他脚本中,我派生BehaviorBase而不是MonoBehavior,并实现performUpdate()而不是Update()。我还没有为编辑器类实现一个并行版本,但我认为我必须在那里做同样的事情
然而,我不喜欢这个策略,因为我必须将它向后移植到我们从社区获取的任何脚本(并且我必须在团队中强制实施它)。编辑器脚本也没有一个可与MonoBehavior相比的入口点,因此我假设我必须实现向导、编辑器等的异常安全版本
我看到过关于使用捕获日志消息(而不是异常)的建议,但这让我感到不舒服,因为我需要解析调试日志字符串,而不是访问实际的异常和堆栈跟踪
所以。。。正确的做法是什么?您提到Application.RegisterLogCallback,您尝试过实现它吗?因为会传回堆栈跟踪、错误和错误类型(警告、错误等) 您在上面概述的策略将很难实施,因为单一行为不仅仅有。您必须处理OnTriggerEvent、OnCollisionEvent、OnGUI等。每一个都将其逻辑包装在异常处理程序中
嗯,异常处理在这里不是个好主意。如果不立即重新抛出异常,最终将以奇怪的方式传播这些错误。也许富依赖于酒吧,而酒吧依赖于巴兹。假设Baz抛出一个捕获并记录的异常。然后Bar抛出一个异常,因为它需要来自Baz的值不正确。最后,Foo抛出了一个异常,因为它从Bar中获取的值无效。我在这里找到了RegisterLogCallback的一个工作实现: 在我自己的实现中,我使用它来调用我自己的MessageBox.Show,而不是写入日志文件。我只是在每个场景中调用SetupExceptionHandling
static bool isExceptionHandlingSetup;
public static void SetupExceptionHandling()
{
if (!isExceptionHandlingSetup)
{
isExceptionHandlingSetup = true;
Application.RegisterLogCallback(HandleException);
}
}
static void HandleException(string condition, string stackTrace, LogType type)
{
if (type == LogType.Exception)
{
MessageBox.Show(condition + "\n" + stackTrace);
}
}
我现在也有错误处理程序通过这个例程给我发电子邮件,所以我总是知道我的应用程序何时崩溃,并尽可能多地获取详细信息
internal static void ReportCrash(string message, string stack)
{
//Debug.Log("Report Crash");
var errorMessage = new StringBuilder();
errorMessage.AppendLine("FreeCell Quest " + Application.platform);
errorMessage.AppendLine();
errorMessage.AppendLine(message);
errorMessage.AppendLine(stack);
//if (exception.InnerException != null) {
// errorMessage.Append("\n\n ***INNER EXCEPTION*** \n");
// errorMessage.Append(exception.InnerException.ToString());
//}
errorMessage.AppendFormat
(
"{0} {1} {2} {3}\n{4}, {5}, {6}, {7}x {8}\n{9}x{10} {11}dpi FullScreen {12}, {13}, {14} vmem: {15} Fill: {16} Max Texture: {17}\n\nScene {18}, Unity Version {19}, Ads Disabled {18}",
SystemInfo.deviceModel,
SystemInfo.deviceName,
SystemInfo.deviceType,
SystemInfo.deviceUniqueIdentifier,
SystemInfo.operatingSystem,
Localization.language,
SystemInfo.systemMemorySize,
SystemInfo.processorCount,
SystemInfo.processorType,
Screen.currentResolution.width,
Screen.currentResolution.height,
Screen.dpi,
Screen.fullScreen,
SystemInfo.graphicsDeviceName,
SystemInfo.graphicsDeviceVendor,
SystemInfo.graphicsMemorySize,
SystemInfo.graphicsPixelFillrate,
SystemInfo.maxTextureSize,
Application.loadedLevelName,
Application.unityVersion,
GameSettings.AdsDisabled
);
//if (Main.Player != null) {
// errorMessage.Append("\n\n ***PLAYER*** \n");
// errorMessage.Append(XamlServices.Save(Main.Player));
//}
try {
using (var client = new WebClient()) {
var arguments = new NameValueCollection();
//if (loginResult != null)
// arguments.Add("SessionId", loginResult.SessionId.ToString());
arguments.Add("report", errorMessage.ToString());
var result = Encoding.ASCII.GetString(client.UploadValues(serviceAddress + "/ReportCrash", arguments));
//Debug.Log(result);
}
} catch (WebException e) {
Debug.Log("Report Crash: " + e.ToString());
}
}
您可以使用一个名为的插件在出现未处理错误时接收调试日志、堆栈跟踪和屏幕捕获的电子邮件。屏幕捕获和堆栈跟踪通常足以找出错误的原因。对于顽固的秘密错误,您应该记录更多可疑数据,构建并再次等待错误。我希望这会有所帮助。在场景中创建一个空的游戏对象,并将此脚本附加到其中:
using UnityEngine;
public class ExceptionManager : MonoBehaviour
{
void Awake()
{
Application.logMessageReceived += HandleException;
DontDestroyOnLoad(gameObject);
}
void HandleException(string logString, string stackTrace, LogType type)
{
if (type == LogType.Exception)
{
//handle here
}
}
}
确保有一个实例
剩下的就看你了。您还可以将日志存储在文件系统、web服务器或云存储中
请注意,
DontDestroyOnLoad(gameObject)
通过防止在场景更改时将其销毁,使该gameObject持久化。Unity开发者只是没有为我们提供类似的工具。它们在框架内部到处捕获异常,并将它们作为字符串记录,从而为我们提供Application.logMessageReceived[Threaded]。因此,如果您需要发生异常,或者需要使用自己的处理(而不是unity)记录异常,我可以想到:
UnityEngine.ILogHandler
:
public interface ILogHandler
{
void LogFormat(LogType logType, Object context, string format, params object[] args);
void LogException(Exception exception, Object context);
}
Debug.logger
在unity 2017中被弃用),以便我们可以通过自己的机制var field = typeof(UnityEngine.Debug)
.GetField("s_Logger", BindingFlags.Static | BindingFlags.NonPublic);
field.SetValue(null, your_debug_logger);
如果感兴趣,您可以阅读流行的崩溃分析工具的工作原理。如果您查看crashlytics(android/ios的崩溃报告工具),您会发现它在内部使用
Application.logMessageReceived
和AppDomain.CurrentDomain.UnhandledException
事件来记录托管C#异常
如果您对unity框架捕获异常的示例感兴趣,您可以查看我的另一篇文章,在button click listener中测试它捕获异常
有关记录未处理异常的官方方法的一些摘要:
I.Application.logMessageReceived在主线程上发生异常时被激发。有几种方法可以实现:
Debug.LogException
Application.CallLogCallback
,从而触发Application.logMessageReceived
注意:当原始异常有内部异常时,StackTrace字符串将包含“rethrow”
二,
Application.logMessageReceivedThreaded
在任何线程上发生异常时都会触发,包括main(如中所述)注意:它必须是thread-s
new Thread(() =>
{
Thread.Sleep(10000);
object o = null;
o.ToString();
}).Start();