Logging
Ubiq has the ability to record, forward and store logs or events. Ubiq itself generates logs, and custom components can create them too.
For example, the logging system could be used to record the answers to a questionnaire, or the direction of a user’s gaze, and forward them to an experimentor.
This guide shows how to set up and log some simple data in the Hello World scene.
Log Flow
Log events (such as answering a question) are generated by Event Loggers with a simple call, e.g. logger.Log("MyEvent")
. These events are received by the Log Manager. Log Managers can send these events over the network to a Log Collector in the same Room. LogManager
and LogCollector
are Components beloning to a NetworkScene
.
There can be many Event Loggers in an application. There should be one LogManager
for each NetworkScene
. There should be at most one active LogCollector
in a Room. The LogCollector
can be on any Peer. For example, it could be on the computer of an experimentor.
Creating a Questionnaire Button
Log events can come from any source. In this guide, they will be generated when a user presses a button.
Create a new Button
in the scene. Below, a new GameObject
was added to the Main Menu. Create a new script, ButtonLogger
, and add it to the Button as well.
The script for ButtonLogger
is below.
using Ubiq.Logging;
using UnityEngine;
using UnityEngine.UI;
public class ButtonLogger : MonoBehaviour
{
UserEventLogger events;
// Start is called before the first frame update
void Start()
{
events = new UserEventLogger(this);
GetComponent< Button >().onClick.AddListener(OnButtonClicked);
}
void OnButtonClicked()
{
events.Log("Button Pressed");
}
}
First, a UserEventLogger
is declared. This is the object that will be used to emit log events. It is declared in the class but initialiesd in Start()
. This is because it has to find the local Log Manager to communicate with, which can’t be done until the scene initialisation begins.
Event Loggers
There are two types of event: User
and Application
. Application
events are meant to record how the application itself is working (application code), while User
events (user code) are created by the appliation, but for another purpose, e.g. to record study data or usage metrics.
Any code can create either type of event. The difference is only in how they are intended to be used. (The two types of event end up in different log files.)
The Event Logger created in the ButtonLogger
script will generate User
events.
Log Manager
The default NetworkScene Prefab already contains a LogManager, so there is no need to add this.
Second, a callback is registered with the Button’s OnClick
event. Finally, when this is raised by the user clicking the button, a log event (“Button Pressed”) is emitted.
Start the Scene and look at the Log Manager in the Inspector. As the Button is clicked the memory usage of the manager will increase, indicating that the Button is generating events.
Log Collector
The events will remain in the LogManager
until they are retrieved. This is done with a LogCollector
.
Add a LogCollector
underneath the LogManager
.
Start the Scene and click the button a few times. Then click Start Collection. The Entries count will increase, and opening the log folder will reveal a User log, with a number of Button Pressed events.
[{"ticks":637684411200317851,"event":"Button Pressed"},
{"ticks":637684411201982783,"event":"Button Pressed"},
{"ticks":637684411203585606,"event":"Button Pressed"},
{"ticks":637684411205105584,"event":"Button Pressed"},
{"ticks":637684411206535592,"event":"Button Pressed"},
{"ticks":637684411208055613,"event":"Button Pressed"},
{"ticks":637684411209415586,"event":"Button Pressed"},
{"ticks":637684411211005613,"event":"Button Pressed"},
{"ticks":637684411212455585,"event":"Button Pressed"}
Logging Arguments
The EventLogger::Log()
method can take a number of arguments in addition to the event name.
Add a new member to the ButtonLogger
, AnswerName
, and pass it in as an argument.
public string Answer;
void OnButtonClicked()
{
events.Log("Button Pressed", Answer);
}
The value of Answer can be set up in the inspector. Duplicate the Button and set two different values of Answer for each.
Now, when looking at the log after pressing the buttons it will show the value of Answer as well.
[{"ticks":637684423675170428,"event":"Button Pressed","arg1":"Yes"},
{"ticks":637684423676660455,"event":"Button Pressed","arg1":"Yes"},
{"ticks":637684423680253262,"event":"Button Pressed","arg1":"No"},
{"ticks":637684423681853281,"event":"Button Pressed","arg1":"No"}
Practically any variable that can be turned into a string can be logged this way.
Collecting from a Distributed Experiment
So far the LogCollector
has just collected from the local LogManager
.
Create a Build of the Hello World application and run it, then press the buttons a few times.
Note that so far, the application has not even joined a room. This is OK because the LogManager
will hold all logs until they are requested.
Next, press Play to load the *Hello World Scene in the Editor.
Now have both Peers join the same room (new or old, in any order). When both have joined, the Avatars of the other Peer should be visible in each.
In the Editor, navigate to the LogCollector
and click on Start Collection in the Inspector.
The Entries count will increase, and a User Log file will appear in the default Logs Folder, containing the answers entered in the Standalone Build.
Considerations
To find out more about the logging, see the Logging section in the Advanced topics.
Log events can be generated from user actions, but also other external events, or at a regular frequency (e.g. to log the Transform
of dynamic objects)
In this example, the LogCollector
was in the scene that went into the Standalone Build, but this is not necessary. Only one Peer needs a LogCollector
to collect logs from all Peers. The LogCollector
does not have to be next to a LogManager
. The LogCollector
does not even need a LogManager
in the NetworkScene
. If the collection is not started, a LogCollector
has no effect.
Log Collectors can even be added at runtime. To see this, re-create the Standalone Build, but with the LogCollector
removed. Follow the steps above, but when in the Editor after joining the Room, add a new LogCollector
at runtime, then click Start Collection. The logs will be collected exactly as before.
Collection can also be started programmatically, in addition to clicking Start Collection. This allows experiment code to start collection other ways, including in Standalone builds.