Analytics
Dopple uses a “log writer” approach for emitting events and handling analytics data. In a nutshell, you provide a custom log writer function to handle event data however you need, and Dopple will call that log writer function whenever an event occurs.
For example, if a user updates a product’s configuration or launches into AR mode, Dopple will call your log writer function, passing along relevant data about the event to your custom function.
const dopple = new DoppleXR({ /* ... */ });
// Your custom log writer function; automatically called whenever an event is emittedfunction customLogWriter(data) { // Parse and handle the event data as needed console.log(JSON.parse(data));
return true;};
// Register your log writerdopple.addLogWriter(customLogWriter);
Creating a Basic Log Writer
Section titled “Creating a Basic Log Writer”To create a custom log writer to handle your events, first define the log writer’s function. This custom log writer function can contain any logic needed for handling event data, such as sending them to your analytics platform of choice.
function customLogWriter(data) { // Parse and handle the data as needed here const dataObject = JSON.parse(data); console.log(dataObject);
// Return `true` to allow the data to propagate to other log writers return true;};
data
will be a stringified JSON object with the following interface:
interface DataObj { context: { logLevel: number; // The level code for the type of log, e.g. 30 = INFO, 50 = ERROR owner: string; project: string; version: string; workspace: string; event?: string; // Included automatically for default events, e.g. `LOADED` namespace?: string; // The `logNamespace` value (if provided when initializing Dopple) sessionId?: string; // ID for the current user's session (if provided when initializing Dopple) }; time: number; message: string;}
Registering the Log Writer
Section titled “Registering the Log Writer”Once created, you can register your custom log writer function with Dopple using dopple.addLogWriter()
.
dopple.addLogWriter(customLogWriter);
Logging Events
Section titled “Logging Events”Once a log writer function has been registered, it will be called each time any event gets triggered. Dopple will emit certain default events, or you can emit a log event manually by calling the method for one of the log levels below, such as dopple.logger.info()
.
Default Events
Section titled “Default Events”By default, Dopple will log events anytime certain actions occur, such as when the product finishes loading or when the product’s selection (configuration) is updated.
All default Dopple events are listed below:
Event Name | Emitted When |
---|---|
AR_INITIATED | dopple.startAR() is called. |
CAMERA_UPDATE | The camera’s view or position have changed, such as by rotating or zooming. |
CANVAS_RESIZE | The Dopple canvas’s size has changed. |
IN_VIEWPORT | The Dopple canvas comes into view on the user’s screen. |
INIT | The dopple instance has begun initializing. |
LOADED | The scene has finished loading its initial assets. |
LOADED_MATRIX | The product’s matrix has finished loading. |
OUT_OF_VIEWPORT | The Dopple canvas goes out of view of the user’s screen. |
SELECTION_UPDATED | dopple.updateSelection() has finished resolving; the new selection has been applied. |
SESSION_ENDED | The Dopple instance has been destroyed by calling dopple[Symbol.dispose]() |
SNAPSHOT_TAKEN | dopple.takeSnapshot() is called. |
TEXTURE_UPSCALED | Higher-resolution textures have finished loading in and been applied. |
UPDATE_SELECTION | dopple.updateSelection() is called. |
All default events contain the same data
object structure mentioned earlier, with a few additions for certain events:
LOADED
has theduration
for how long it took to load.CAMERA_UPDATE
has the new target and position that the camera was updated toSELECTION_UPDATED
andUPDATE_SELECTION
have the old and current product selection (i.e. current configuration) objects.
Custom Events
Section titled “Custom Events”Custom events can be manually triggered at any time by calling the logger’s built-in methods, such as info()
or warn()
.
All logger methods take two arguments:
- A
context
object, with any custom properties of your choice that will be appended to the context object in the data sent to your log writer function. - An optional
message
string to be sent along with the context data to your log writer function.
dopple.logger.info( // Context object (containing any data about your event) { event: "YOUR_EVENT_NAME", foo: "bar" }, // Message (include any custom description or message with your event; optional) "Custom event message");
Then, you can access this data, along with any custom properties you include, from inside your log writer function/
function customLogWriter(data) { const { context, message } = JSON.parse(data);
console.log(context?.event); // "YOUR_EVENT_NAME" console.log(context?.foo); // "bar" console.log(message); // "Custom event message"
return true;};
context
Section titled “context”An object containing properties and data to be sent to the logger. The context
object is flexible; it can contain any custom properties with custom values you need.
message
Section titled “message”An optional string included alongside the context
object in the logger. For simple use cases, you may skip including any context
object and instead only pass a message to the logger.
dopple.logger.info("My simple message");
Log Levels and Methods
Section titled “Log Levels and Methods”Each log’s context
includes a logLevel
number, which corresponds with the log method that was used.
For most standard events with Dopple, it is recommended to use dopple.logger.info()
; however, other methods exist in case they are needed for more fine-grained event or error logging in rare cases.
Level | Method | Description |
---|---|---|
10 | trace() | For verbose, detailed diagnostic info, typically for debugging. |
20 | debug() | For logging regular debugging info. |
30 | info() | For standard event logging (recommended). |
40 | warn() | For any abnormal conditions that aren’t outright failures. |
50 | error() | For recoverable failures. |
60 | fatal() | For extreme, unrecoverable failures. |
For example, calling dopple.logger.warn("Be careful")
will pass the following data to your log writer function:
{ content: { logLevel: 40, // ... }, message: "Be careful"}
Log Namespace and Session ID
Section titled “Log Namespace and Session ID”In more complex scenarios where you may be logging events across multiple product instances (such as if you have a variety of products, or are testing a product across development/staging/production environments), it may be handy to “group” events by their instance.
Dopple lets you do this in two ways: by specifying either a logNamespace
when you first initialize your product’s instance, a sessionId
, or both.
const dopple = new DoppleXR({ // ... logNamespace: "my-custom-log-namespace", sessionId: window.crypto.randomUUID()});
logNamespace
— ideal for creating a namespace or “group” for the event to fall within.sessionId
— typically used for keeping track of a user’s session.
If specified, the logNamespace
and sessionId
values will be included with the context object in each event so that your custom log writer function can handle different cases as needed.
function customLogWriter(data) { const { context } = JSON.parse(data);
console.log(context.logNamespace); // "my-custom-log-namespace" console.log(context.sessionId); // "a1a1a1a1-b2b2-c3c3-d4d4-e5e5e5e5e5e5"};
Google Analytics
Section titled “Google Analytics”To begin integrating with Google Analytics:
-
Add the gtag script to your page.
<head><!-- Replace with your Measurement ID below --><script src="https://www.googletagmanager.com/gtag/js?id=G-ABCDE12345" async></script></head> -
Initialize Google Tag Manager with your Measurement ID.
function gtag(...args) {window.dataLayer.push(args);}window.gtag = gtag;gtag("js", new Date());gtag("config", "G-ABCDE12345"); // Replace with your Measurement ID -
Define the events you want to capture and send to Google Analytics.
const EVENT_NAMES = {// Default events from Dopple (automatically emitted)LOADED: "dopple_configurator_loaded",IN_VIEWPORT: "dopple_canvas_shown",SNAPSHOT_TAKEN: "dopple_snapshot_taken",AR_INITIATED: "dopple_ar_initiated",SELECTION_UPDATED: "dopple_property_changed",// Custom events (add any custom events here as needed)ADD_TO_CART: "dopple_add_to_cart"} -
Create a log writer function to handle sending Dopple events to Google Analytics.
function logGAEvent(data) {const { context, message, time } = JSON.parse(data);// Exit early in case gtag or the event aren't properly setif (!window.gtag || !context?.event) {return false;}const eventData = {dopple_session_id: context?.sessionId || window.crypto.randomUUID(),timestamp: time};// Exit early if this event isn't one of the ones listed above to be capturedif (!(context.event in EVENT_NAMES)) {return false;}// Get the display name for the current event to be used in the Google Analytics dashboardconst eventName = EVENT_NAMES[context.event];// Handle sending any newly updated properties and options to Google Analyticsif (context.event === "SELECTION_UPDATED") {const oldSel = context.oldSelection ?? {};const newSel = context.selection ?? {};for (const key in newSel) {if (key in oldSel && newSel[key] !== oldSel[key]) {window.gtag("event", eventName, {...eventData,property_name: key,selected_option: newSel[key]});break;}}// Exit but allow other log writers to fire for this "SELECTION_UPDATED" eventreturn true;}// Handle including extra data with camera, snapshot, and AR eventsswitch (context.event) {case "CAMERA_UPDATE":// Include the camera's new target and position data in the event dataObject.assign(eventData, {target: context.target,position: context.position});break;case "SNAPSHOT_TAKEN":case "AR_INITIATED":// Include the product's current configuration in the event dataeventData.full_config_object = context.selection;break;}// Send the event and its payload data to Google Analyticswindow.gtag("event", eventName, eventData);// Exit and prevent other log writers from firing for these eventsreturn false;} -
Register the Google Analytics log writer function.
dopple.addLogWriter(logGAEvent); -
Add any event listeners or triggers for your custom events as needed.
For example, if your page features an Add To Cart button, you may call
dopple.logger.info()
with your customADD_TO_CART
event whenever that button is clicked.<div id="dopple-container"></div><button id="add-to-cart">Add To Cart</button>const addToCartButton = document.getElementById("add-to-cart");addToCartBtn?.addEventListener("click", () => {dopple.logger.info({ event: "ADD_TO_CART" },"User added the product to the cart",);});` -
That’s it! For reference, the full example code snippet is below.
<head><!-- Replace with your Measurement ID below --><script src="https://www.googletagmanager.com/gtag/js?id=G-ABCDE12345" async></script></head><body><div id="dopple-container"></div><button id="add-to-cart">Add To Cart</button></body>import { DoppleXR } from "https://builds.dopple.io/packages/dopple-sdk@latest/dopple-sdk.js";// Initialize Doppleconst dopple = new DoppleXR({container: document.getElementById("dopple-container"),owner: "my-org",workspace: "my-workspace",projectName: "My Project",productVersion: "1",105 collapsed linesselection: {},logNamespace: "my-custom-log-namespace",sessionId: window.crypto.randomUUID()});// Set up gtagfunction gtag(...args) {window.dataLayer.push(args);}window.gtag = gtag;gtag("js", new Date());gtag("config", "G-ABCDE12345"); // Replace with your Measurement IDconst EVENT_NAMES = {// Default events from Dopple (automatically emitted)LOADED: "dopple_configurator_loaded",IN_VIEWPORT: "dopple_canvas_shown",SNAPSHOT_TAKEN: "dopple_snapshot_taken",AR_INITIATED: "dopple_ar_initiated",SELECTION_UPDATED: "dopple_property_changed",// Custom events (add any custom events here as needed)ADD_TO_CART: "dopple_add_to_cart"}// Custom log writer functionfunction logGAEvent(data) {const { context, message, time } = JSON.parse(data);// Exit early in case gtag or the event aren't properly setif (!window.gtag || !context?.event) {return false;}const eventData = {dopple_session_id: context?.sessionId || window.crypto.randomUUID(),timestamp: time};// Exit early if this event isn't one of the ones listed above to be capturedif (!(context.event in EVENT_NAMES)) {return false;}// Get the display name for the current event to be used in the Google Analytics dashboardconst eventName = EVENT_NAMES[context.event];// Handle sending any newly updated properties and options to Google Analyticsif (context.event === "SELECTION_UPDATED") {const oldSel = context.oldSelection ?? {};const newSel = context.selection ?? {};for (const key in newSel) {if (key in oldSel && newSel[key] !== oldSel[key]) {window.gtag("event", eventName, {...eventData,property_name: key,selected_option: newSel[key]});break;}}// Exit but allow other log writers to fire for this "SELECTION_UPDATED" eventreturn true;}// Handle including extra data with camera, snapshot, and AR eventsswitch (context.event) {case "CAMERA_UPDATE":// Include the camera's new target and position data in the event dataObject.assign(eventData, {target: context.target,position: context.position});break;case "SNAPSHOT_TAKEN":case "AR_INITIATED":// Include the product's current configuration in the event dataeventData.full_config_object = context.selection;break;}// Send the event and its payload data to Google Analyticswindow.gtag("event", eventName, eventData);// Exit and prevent other log writers from firing for these eventsreturn false;}// Register the custom log writer functiondopple.addLogWriter(logGAEvent);// Load the 3D productawait dopple.load();dopple.run();// Send a custom event when Add To Cart is clickedconst addToCartButton = document.getElementById("add-to-cart");addToCartBtn?.addEventListener("click", () => {dopple.logger.info({ event: "ADD_TO_CART" },"User added the product to the cart",);});`
Full Code Example
Section titled “Full Code Example”<html> <head> <script type="module"> import { DoppleXR } from "https://builds.dopple.io/packages/dopple-sdk@latest/dopple-sdk.js";
// Initialize Dopple const dopple = new DoppleXR({ container: document.getElementById("dopple-container"), owner: "my-org", workspace: "my-workspace", projectName: "My Project", productVersion: "1", selection: {}, logNamespace: "my-custom-log-namespace", sessionId: window.crypto.randomUUID() });
// Custom log writer function function customLogWriter(data) { const dataObject = JSON.parse(data);
switch (dataObject?.context?.logLevel) { // Handle any errors case 50: console.error(dataObject?.message); break; // Handle any warnings case 40: console.warn(dataObject?.message); break; // Send any regular/successful info logs to your analytics platform case 30: sendToAnalytics(dataObject); }
return true; };
// Register the custom log writer function dopple.addLogWriter(customLogWriter);
// Example of adding a custom event to the Add To Cart button const addToCartButton = document.querySelector("#add-to-cart"); addToCartButton.addEventListener("click", () => { dopple.logger.info( { event: "ADD_TO_CART", // Custom name for the event currency: "USD", // Include metadata for the currency in the log's context object }, "User added the product to their cart" ); });
// Dummy function to send data to your analytics platform, such as Google Analytics function sendToAnalytics(dataObj) { const { context, message } = dataObj;
// Examples of how to handle unique context data for different events switch (context?.event) { case "LOADED": console.log(`Product loaded in ${context?.duration}ms`); break; case "SELECTION_UPDATED": console.log("New selection:", context?.selection); break; case "ADD_TO_CART": console.log(`${message} (Currency: ${context?.currency})`); break; } // Include any additional logic to send the data to your backend platform here... }
// Load the 3D product await dopple.load(); dopple.run(); </script> </head> <body> <div id="dopple-container"></div> <button id="add-to-cart">Add To Cart</button> </body></html>