
前一篇已经介绍了NebuLog客户端和服务端都通过MVC来实现的方法。这一篇介绍一下如何在Unity中通过SignalR发送Debug.Log的日志信息。
首先要提一下Unity、.Net Core、.NetStandard。截至本文撰写的时间,一般普遍认为Unity不支持.net core,当然也(还)不支持.Net Standard 2.1。前段时间度娘告诉我Unity 2020.2版本全面支持.Net 5了,害我高兴得想立马去下载2020.2了,后来TMD看到这个是愚人节消息。目前Unity可以下载的稳定版本只有2020.1.6f1,虽然2020.2beta版本已经可以下载了,但是官方并没有对.Net Core或者.Net 5的支持方面进行介绍。不过,实际上unity至少从2018版本起就能够运行.Net Core类库(是的,你没看错,Unity支持.Net Core!),但是由于微软众多.net Framework衍生出来的兄弟姐们七大姑八大姨的,搞得动不动就类库冲突,或者版本冲突。所以希望大微软尽快推出一统江山的.Net 5,结束目前这种混乱的局面,实为.NET开发者之福。
安装SignalR类库包
NebuLog因为采用了Microsoft.aspnetcore.SignalR库来进行客户端-服务端的双工通信,因此需要在Unity中调用SignalR。SignalR有基于.Net Framework 4.5和.Net Standard 2.0的不同版本,因为在MVC示例中我已经用了core版本,所以在unity中就希望继续沿用core版本。
给Unity安装SignalR支持库有最少两种方式,第一种就是把所需要的DLL拷贝到Unity的assetsplugins文件夹,第二种是通过一个开源的第三方插件NuGetForUnity来管理,具体可以参考这篇博文:
Unity3D 入门:如何管理 Unity 项目中的 NuGet 包?使用第三方 NuGet 包管理器--NuGetForUnityblog.csdn.net
利用NugetForUnity直接安装imady.NebuLog是一个不错的偷懒主意,但是实际测试并没有通过。原因可能是NugetForUnity并没有很好的解释各个插件的依赖性,因而智能化不够,大部分情况下还是要手工解决依赖问题。
事实上我使用了NugetForUnity安装SignalR后发现仍旧出现了冲突,最后只能下载了GitHub上的一个开源项目:
vackup/Unity3DSignalRgithub.com
然后把示例代码中plugins文件夹下的dll全部拷贝下来。
SignalR的版本问题
前段时间试图把示例代码从http://asp.net core 2.2版本升级到3.0版本,结果发现升级后不能用了,查了很久发现是Unity的API兼容仍旧只支持.Net Standard 2.0(我用的仍旧是Unity2019.4版本,没敢升级到2020,怕做了实验室兔子),因此相关的类库必须用支持.NetStandard 2.0的aspnetcore.signalR.client 1.1.0版本DLL。结果只能重新从以前的代码中翻出来几个SignalR的老版本DLL,重新覆盖PluginsSignalR中的文件,才把NebuLogUnitySample重新跑起来。
目前我在GitHub上的unity示例包含了上述dll文件,因此直接clone下来应该就能运行。如果不能运行,请手动检查plugins文件夹下面的dll版本,或者等待Unity支持.Net Core。。。。。。
获取Unity的Debug.Log信息
重点来了。因为unity没有采用http://Asp.Net Core那样的ILogging体系,所以并不会允许开发者注入自己的日志管理器来对接其运行期间产生的应用日志。但是Unity提供了一个日志回调接口Application.logMessageReceived,使得我们能够获取到unityEngine下DEBUG.LOG提交的日志信息(string格式的):
public static event LogCallback logMessageReceived;
因此我们只需要写一个HandleUnityLogs方法来处理Debug信息即可将日志按照我们需要的方式进行处理:
// 注册Debug.Log响应委托以获取日志消息
#if UNITY_4
Application.RegisterLogCallback(HandleUnityLogs);
#else
Application.logMessageReceived += logger.HandleUnityLogs;
#endif
引用imady.NebuLog的问题
上一篇中我们看到引用imady.NebuLog即可使用NebuLog相关的功能,但是在Unity中,引用imady.NebuLog却发生了错误,原因还是包的依赖性问题,发生错误的位置可能在NebuLogExceptionMiddleWare这块。
因为时间关系,去查找和解决这些问题还不如重新写一个logger。所以很简单地把代码拷贝过来,形成IUnityNebulog接口和UnityNebulogger实现(NebuLogUnitySample没有引用imady.NebuLog)。接口必须人为保证与NebuLogHub中的定义相同,因为unity的信息输出必须通过相应的hub接口接收和转发。
示例中UnityNebuLogger的构造器负责生成和启动nebulogHubConnection连接。这与MVC示例的方式也不同,在前一篇示例中我们不需要人工去进行HubConnection的连接,而是把这个工作交给了ILoggingProvider来完成,并由http://Asp.Net Core框架中的LoggerFactory来管理。而unity并没有这样的体系,因此我们需要在UnityNebulogger构造器中手动完成:
public App: MonoBehaviour
{
public void Awake()
{
logger = new UnityNebulogger();
......
}
}
HubConnection连接阶段不能被调用的问题
实际运行测试过程中又发现一个由于Unity运行机制产生的问题。
假如我们在unity应用启动阶段进行UnityNebuLogger的实例化工作,也就是进行了HubConnection的连接,但是经测试发现signalr连接完成可能要耗费数百毫秒的时间。在这段时间里,如果有其他unity GameObject上的脚本发出了logger请求,会出现什么问题?
我的测试结果是,如果是通过Debug.Log输出的日志,会被忽视;而如果用户直接调用了logger实例,则会导致unity死锁。
为了解决这个问题,在UnityNebuLogger中给hubconnection添加了一个连接完成后的事件,通知App收到事件后才向unity游戏对象添加可能调用logger的脚本,这样就保证了调用logger一定在连接完成之后才会进行:
public class UnityNebulogger :IUnityNebulog, IDisposable
{
public event EventHandler NebulogConnected;
public static string defaulNebulogHubUri = "http://192.168.0.20/NebuLogHub";
private HubConnection nebulogHubConnection;
public UnityNebulogger()
{
Action initiateNebulogHubConnection =
async () =>
{
await StartSignalRAsync(defaulNebulogHubUri);
};
initiateNebulogHubConnection.Invoke();
}
}
public class App : MonoBehaviour
{
public static IUnityNebulog logger;
public GameObject UnityNebuLogTestObject;
public Text hubStatusText;
public void Awake()
{
logger = new UnityNebulogger();
... ...
//注册到HubConnrvyion连接完成事件,进行业务模块加载
logger.NebulogConnected += (sender, args) =>
{
//==================================================================
//等待UnityNebuLogger初始化完成(HubConnection连接后)才加载业务逻辑
//否则可能引起HubConnection未连接就被调用,而导致进程锁死
UnityNebuLogTestObject.AddComponent<UnityNebuLogTest>();
//==================================================================
hubStatusText.text += "nSignalR HubConnection连接完成。";
Debug.Log("App initiation completed.");
};
//!!!此时不能立即向NebuLog输出信息,因为HubConnection尚未完成连接。
}
... ...
}
我也曾试图以同步方式调用signalR的hubConnection连接,但是发现并没有Start()的同步方法,只有StartAsync异步方法。
实际测试
实际运行可以看到,unity下并没有像http://asp.net core MVC那样获取到大量应用运行时的日志信息。但是用户Debug.Log输出的信息都被导向了NebuLog远端,并且发生错误时的Exception、用户通过throw抛出的异常也都能够被输出。

NebuLog示例的GitHub地址是:
imadyTech/NebuLoggithub.com
您可以下载后试运行一下,如果有任何指教或者改进意见请留言。