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.

logflow

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.

logmanager

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.

button

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.

inspector

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.

explorer

 
[{"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.

inspector2

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.

avatar

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.

Edit this page on GitHub