(“This Week in Glean” is a series of blog posts that the Glean Team at Mozilla is using to try to communicate better about our work. They could be release notes, documentation, hopes, dreams, or whatever: so long as it is inspired by Glean. You can find an index of all TWiG posts online.)
This is a special guest post by non-Glean-team member Daosheng Mu!
You might notice Firefox Reality PC Preview has been released in HTC’s Viveport store. That is a VR web browser that provides 2D overlay browsing alongside immersive content and supports web-based immersive experiences for PC-connected VR headsets. In order to easily deploy our product into the Viveport store, we take advantage of Unity to help make our application launcher. Also because of that, it brings us another challenge about how to use Mozilla’s existing telemetry system.
As we know, Glean SDK has provided language bindings for different programming language requirements that include Kotlin, Swift, and Python. However, when we are talking about supporting applications that use Unity as their development toolkit, there are no existing bindings available to help us achieve it. Unity allows users using a Python interpreter to embed Python scripts in a Unity project; however, due to Unity’s technology being based on the Mono framework, that is not the same as our familiar Python runtime for running Python scripts. So, the alternative way we need to find out is how to run Python on .Net Framework or exactly on Mono framework. If we are discussing possible approaches to run Python script in the main process, using IronPython is the only solution. However, it is only available for Python 2.7, and the Glean SDK Python language binding needs Python 3.6. Hence, we start our plans to develop a new Glean binding for C#.
The Glean team and I initialized the discussions about what are the requirements of running Glean in Unity to implement C# binding from Glean. We followed minimum viable product strategy and defined very simple goals to evaluate if the plan could be workable. Technically, we only need to send built-in and custom pings as the current Glean Python binding mechanism, and we are able to just use StringMetricType as our first metric in this experimental Unity project. Besides, we also notice .Net Frameworks have various versions, and it is essential to consider the compatibility with the Mono framework in Unity. Therefore, we decide Glean C# binding would be based on .Net Standard 2.0. Based on these efficient MVP goals and Glean team’s rapid production, we got our first alpha version of C# binding in a very short moment. I really appreciate Alessio, Mike, Travis, and other team members from the Glean team. Their hard work made it happen so quickly, and they were patient with my concerns and requirements.
How it works
In the beginning, it is worth it for us to explain how to integrate Glean into a Hello World C# application. We can choose either importing the C# bindings source code from glean-core/csharp or just building the csharp.sln from the Glean repository and then copy and paste the generated Glean.dll to your own project. Then, in your C# project’s Dependencies setting, add this Glean.dll. Aside from this, we also need to copy and paste glean_ffi.dll that is existing in the folder from pulling Glean after running `cargo build`. Lastly, add Serilog library into your project via NuGet. We can install it through NuGet Package Manager Console as below:
PM> Install-Package Serilog
PM> Install-Package Serilog.Sinks.Console
Defining pings and metrics
Before we start to write our program, let’s design our metrics first. Based on the current ability of Glean SDK C# language binding, we can create a custom ping and set a string type metric for this custom ping. Then, at the end of the program, we will submit this custom ping, this string metric would be collected and uploaded to our data server. The ping and metric description files are as below:
Testing and verifying it
Now, it is time for us to write our HelloWorld program.
As we can see, the code above is very straightforward. Although Glean parser in C# binding hasn’t been supported to assist us create metrics, we are still able to create these metrics in our program manually. One thing you might notice is
Thread.Sleep(1000); at the bottom part of the main(). It means pausing the current thread for 1000 milliseconds. Because this HelloWorld program is a console application, the application will quit once there are no other operations. We need this line to make sure the Glean ping uploader has enough time to finish its ping uploading before the main process is quit. We also can choose to replace it with
Console.ReadKey(); to let users quit the application instead of closing the application itself directly. The better solution here is to launch a separate process for Glean uploader; this is the current limitation of our C# binding implementation. We will work on this in the future. Regarding the extended code you can go to glean/samples/csharp to see other pieces, we also will use the same code at the Unity section.
After that, we would be interested in seeing if our pings are uploaded to a data server successfully. We can set the Glean debug view environment variable by inserting
set GLEAN_DEBUG_VIEW_TAG=samplepings in our command-line tool,
samplepings could be any you would like to be as a tag of pings. Then, run your executable file in the same command-line window. Finally, you can see this result from Glean Debug Ping Viewer , the result is as below:
It looks great now. But you might notice the os_version looks not right if you are running it on a Windows 10 platform. This is due to the fact that we are using
Environment.OSVersion to get the OS version, and it is not reliable. The follow up discussion please reference Bug 1653897. The solution is by adding a manifest file, then unmarking the lines of Windows 10 compatibility.
Your first Glean program in Unity
Now, I think we are good enough about the explanation of general C# program parts. Let’s move on the main point of this article, using Glean in Unity. Using Glean in Unity is a little bit different, but it is not too complicated. First of all, open the C# solution that your Unity project creates for you. Because Unity needs more dependencies when using Glean, we would let the Glean NuGet package help us install them in advance. As Glean NuGet package mentions, install Glean NuGet package by the below command:
Install-Package Mozilla.Telemetry.Glean -Version 0.0.2-alpha
Then, check your Packages folder, you could see there are some packages already downloaded into your folder.
However, the Unity project builder couldn’t be able to recognize this Packages folder, we need to move the libraries from these packages into the Unity Assets\Scripts\Plugins folder. You might be able to see these libraries have some runtime versions differently. The basic idea is that Unity is .Net Standard 2.0 compatible, so we can just grab them from lib\netstandard2.0 folders. In addition, Unity allows users to distribute their version to x86 and x86_64 platforms. Therefore, when moving Glean FFI library into this Plugins folder, we have to put Glean FFI library, glean_ffi.dll, x86 and x86_64 builds into Assets\Scripts\Plugins\x86 and Assets\Scripts\Plugins\x86_64 individually. Fortunately, in Unity, we don’t need to worry about the Windows compatibility manifest stuff, they already handle it for us.
Now, we can start to copy the same code to its main C# script.
We might notice we didn’t add
Thread.Sleep(1000); at the end of the
start() function. It is because in general, Unity is a Window application, the main approach to exit an Unity application is by closing its window. Hence, Glean ping uploader has enough time to finish its task. However, if your application needs to use a Unity specific API,
Application.Quit(), to close the program. Please make sure to make the main thread wait for a couple of seconds in order to preserve time for Glean ping uploader to finish its jobs. For more details about this example code, please go to GleanUnity to see its source code.
C# binding is still at its initial stage. Although we already support a few metric types, we still have lots of metric types and features that are unavailable compared to other bindings. We also look forward to providing a better solution of off-main process ping upload to resolve needing a main thread to wait for Glean ping uploader before the main process can quit. We will keep working on it!