Game Engine Analytics
Overview
SparkLogs has specialized support for ingesting, processing, aggregating, and analyzing metrics from games. The scale and flexibility of SparkLogs makes it a great choice for analytics, allowing you to focus on building your game and not on building and maintaining your own analytics infrastructure.
Capturing analytics events allows you to understand how players are behaving so you can optimize for various outcomes (daily activity, playtime, progression, in-app purchases and ads) and for balance (resource grants and sinks), etc.
Competing analytics solutions are often very expensive (10x) and have field, cardinality, and event count limits. SparkLogs delivers a cost-effective solution that supports unlimited fields, unlimited values, and can effortlessly scale to petabytes, supporting games with 100s of millions of daily active users (DAU) and beyond. We also provide automatic user geolocation (country), currency conversion for purchase events, and auto extracts structured fields from your unstructured logs.
Open-source plugins are available for popular game engines to make it easy to capture and send analytics events and backend logs from your games.
Our rich analytics data model lets you capture key events, including including session start and end, design, progression, real money purchases, ads, resource grants and sinks, and error or other textual events.
Once configured, daily aggregate analytics snapshots are automatically generated and retained for 7 years (paid plan) or 3 months (free plan), so you can analyze daily active users (DAU), average session and ad statistics, progression funnels, and other key game metrics.
Direct access to your aggregate and granular analytics data is available, to make it easy to visualize trends on your own dashboards and analyze with your own ML models.
Free SparkLogs accounts are available to ingest up to 25 GB/month. That's enough free quota for over 1 million analytics sessions of data every month (assuming each session records 16 events and each event is about 1.5 KiB).
Setup and Configuration
First activate game engine analytics within your SparkLogs workspace. Login to the app, go to Workspace Settings, and click the Enable Game Engine Analytics button.
This will activate automatic daily aggregate analytics snapshots.
Next, determine how you will ingest game engine analytics events into SparkLogs.
Data Ingestion and Plugins
You can generate and ingest analytics events into SparkLogs using one of our open-source plugins for supported game engines:
- Unreal Engine plugin: This plugin supports Unreal Engine 4 and 5, and captures analytics events and/or backend logs.
If you're using another game engine that does not yet have a supported plugin, contact us to request support for your game engine.
Also, even without a plugin you can record analytics events from your player's game clients and get the benefits of the custom analytics analysis that SparkLogs performs (e.g., daily snapshot summarization of analytics data, etc.), provided that you ingest data into SparkLogs with the same schema for analytics events.
There are a number of options to ingest analytics events:
- Work with us to adapt a plugin's source code to your game engine. We'd like to develop plugins for other popular game engines, so chat with us on discord to discuss.
- Submit analytics JSON events using any of our APIs (such as the HTTP+JSON API). The schema of submitted events must match known analytics event types. See also the example JSON data to understand how these events are structured in practice. Make sure to batch your events (details below).
- Generate JSON data for analytics events as described above, but instead of submitting directly via API, send the data to a log forwarding agent of your choice (e.g., append JSON data to a local NDJSON file and configure fluentbit to read this file, batch, and submit the data to SparkLogs).
If you submit analytics events data directly via one of the APIs, you must batch your events rather than sending them one at a time. We recommend batching analytics events at the client-side and sending all events every 12 seconds and immediately at the end of a game session.
If you are using a forwarding agent such as fluentbit to send analytics events (e.g., from a local NDJSON file that you append to), make sure to configure it to batch events so that events are not sent more often than every 12 seconds. For example, for fluentbit, set the service flush setting to 12 (seconds).
In addition to sending game engine analytics data from your player's game clients, if you want to ship logs from your backend game servers to SparkLogs, some of our plugins have built-in support for log shipping. For other platforms, you can log your game engine output to a (rotating) file and then use any log forwarding agent to ship your logs.
Whether using a plugin or an API, you will need authentication information for the agent(s) you want to use to ingest the data. You can setup the organizations and agents you want to use for your ingested data in the SparkLogs app. You can optionally configure different authentication credentials for the different build configurations (client, editor, server) of your game or use the same auth for all. You may want to use a different SparkLogs organization for each game you develop, and also have suborganizations for development (editor) and production environments.
Confirming Data Flow
After configuring your plugin and adding logic to record analytics events, you can confirm that the data is flowing correctly. Play a test game session so that some analytics events will be generated and transmitted. Note that analytics events are typically transmitted to SparkLogs at the end of a session or when the game engine exits.
You can then use Explore UI with the following LQL query to show analytics events:
g_analytics.type!
Conceptual Model
All analytics events happen within the context of an analytics session, which marks the beginning and end of when a player is actively playing the game. Sessions are globally identified by a unique random session ID. Each analytics event is associated with a session and is of a particular type (session start/end, purchase, ad, resource, progression, design, log).
Individual analytics events are ingested and stored individually to preserve detail (see data model), where in SparkLogs you can explore and export your data using LQL in the Explore UI. If you're using a private-cloud service plan you'll have direct access to your data in your own Google Cloud BigQuery dataset, and you can perform your own analysis directly using BigQuery. You should make sure to use a BigQuery Enterprise reservation that auto-scales from zero slots to optimize your BigQuery query costs.
Aggregate analytics snapshots are also automatically computed once a day shortly after midnight UTC, allowing you to visualize and analyze trends such as daily active users, playtime statistics, player revenue, and other key metrics.
User Segmentation
You can optionally store one or more "user tags" with each analytics event to help segment your users. For example, you could include an A/B testing experiment ID as a user tag, or tags identifying whether or not a user is free-to-play or has made purchases. There is no limit to the number of unique tag values you can use.
Refer to the documentation for your specific game engine plugin how to set user tags.
User tags are stored in the user_tags and user_tags_array fields of the g_analytics object in each analytics event.
In addition to segmentation, you can group users into cohorts based on first installed date, first purchased date, or other characteristics. Use either the daily snapshot table (recommended) or the materialized view with granular event data.
Types of Analytics Events
The following types of analytics events are supported:
- Session Start: A player is actively playing the game.
- Session End: A player has stopped playing the game.
- Purchase: Represents real money purchases to obtain a certain status, item, or resource in the game. If the purchase granted certain amounts of virtual currencies, you may want to also record one or more resource source events.
- Ad: Represents one or more ad impressions and a corresponding action taken by the player.
- Resource: Represents the granting (resource source) or using up (resource sink) of a virtual currency (gems, lives, etc.).
- Progression: Represents starting, failing, or completing a certain part of a game.
- Design: A design event is any in-game event you wish to record that does not fit into the other categories.
- Log: A log event is a textual event with a severity that you want to record and associate with a session, such as an error causing a game crash, or any other information that is not captured by the other event types.
Each analytics event includes certain common fields (see analytics data model). You can also optionally provide a textual reason why a given event occurred, and you can also provide unlimited custom JSON fields (including complex values such as objects or arrays) with additional information. See also the example JSON data for each event type.
Session Start Event
Marks the beginning of a session. This event does not have an event_id field, and there are no additional event fields beyond common fields.
Session End Event
Marks the end of a session. This event does not have an event_id field.
Additional event fields beyond common fields:
| Field Name | Type | Description |
|---|---|---|
| session_ended | Timestamp | The timestamp when the session ended. |
| session_duration_secs | Numeric | The duration of the session in seconds, from the time the session started to the time it ended. |
Purchase Event
Marks the purchase of a certain item or resource in the game using real-world currency. This event has the following additional fields:
This event takes as input an item category and item ID to identify what was purchased, as well as the amount of real-world currency spent on the purchase.
If the purchase granted certain amounts of virtual currencies, you should also record one or more resource source events.
The generated event ID is derived as follows: <item_category>:<item_id>
Additional event fields beyond common fields:
| Field Name | Type | Description |
|---|---|---|
| item_category | String | Optional. The category of the item that was purchased (e.g., "starter_packs", "individual_resource"). |
| item_id | String | The unique identifier for the item that was purchased. (e.g. "pack_1", "pink_gems", "lives") |
| currency | String | The currency used for the purchase (e.g., "USD", "EUR"). This is the ISO 4217 currency code. Over 200 are supported. |
| amount | Numeric | The amount of real-world currency spent on the purchase (for example one cent would be 0.01). |
| amount_converted | Numeric | The amount converted into your preferred target currency (e.g., USD) using the average exchange rate at the time of event ingestion. Will only be present if the source currency is supported and the conversion is successful. |
| amount_converted_currency | String | The ISO 4217 currency code of the target currency used for conversion. Will only be present if the source currency is supported and the conversion is successful. |
| transaction_num | Numeric | The number of the transaction since the game was first installed, starting with 1. |
| first_purchased | Timestamp | The timestamp when the first purchase was made since the game was installed. |
You can customize plugin settings to change the target currency. See the currency conversion documentation for details.
Ad Event
Marks one or more ad impressions and a corresponding action taken by the player. You can choose to either record one event for each ad impression, or you can aggregate multiple ad impressions into a single event.
This event takes as input the ad provider, ad placement ID, and ad type, as well as the action taken by the player. Optionally you can record revenue from the ad, if known.
The generated event ID is derived as follows: <ad_provider>:<ad_placement>:<ad_type>:<ad_action>
Additional event fields beyond common fields:
| Field Name | Type | Description |
|---|---|---|
| ad_provider | String | A string identifying the ad provider. For example, unity, admob, etc. or use unknown |
| ad_placement | String | An ID representing where the ad was placed within you app. For example, end_of_level, treasure_chest, etc. |
| ad_type | String | Optional. The type of ad that was shown. One of interstitial, video, rewarded_video, banner, native, playable, app_open, offerwall, cross_promo, other, or any custom string. |
| ad_action | String | Optional. The action taken by the player in response to the ad. One of completed, skipped, clicked, rewarded, failed_to_show, other, or any custom string. |
| ad_fail_reason | String | Optional. The reason the ad failed to show, if applicable. One of no_fill, offline, network_error, invalid_request, other, or any custom string. |
| revenue | Numeric | Optional. The total revenue generated from these ad impression(s), if known. Use null or 0.0 if unknown. |
| currency | String | Optional. The ISO 4217 code of the currency used for the ad revenue, if known (e.g., USD). |
| duration_secs | Numeric | Optional. The duration of the ad in seconds, if applicable. If this event represents multiple impressions, use the average duration. |
| count | Numeric | Optional. The number of ad impressions recorded in this event. Defaults to 1 if not specified. |
Based on the currency conversion logic, it will also calculate the revenue_converted and revenue_converted_currency fields if applicable.
Resource Event
Marks the granting (resource source) or using up (resource sink) of a virtual currency (gems, lives, etc.). This event takes as input the flow type, an optional item category and item ID to identify what was purchased, and the type and amount of virtual currency granted or used.
Be thoughtful about the balance between recording every resource source/sink event as it happens (which could be very often) vs aggregating the information and recording it only at key points (e.g. when losing a life or ending a level). The system can handle sending frequent events, but this could generate a lot more data and could thus cost more at large scale. Be especially thoughtful when you have 100s of thousands of users or more.
If a resource was granted as the result of a purchase using real money, then make sure to also record a corresponding purchase event as well with the same item category and item ID.
The generated event ID is derived as follows: <flow_type>:<virtual_currency>:<item_category>:<item_id>
Additional event fields beyond common fields:
| Field Name | Type | Description |
|---|---|---|
| flow_type | String | Either "Source" or "Sink". |
| virtual_currency | String | The name of the virtual currency (e.g., "pink_gems", "lives"). |
| item_category | String | Optional. The category of the item associated with the resource being granted or used. e.g., "currency_purchase" or "pickup" |
| item_id | String | Optional. The ID of the item associated with the resource being granted or used. e.g., "100_pink_gems_pack" or "ad_item" |
| amount | Numeric | The amount of virtual currency granted or used. This will be enforced to be positive for sources and negative for sinks. |
Progression Event
Marks the starting, failing, or completing of a certain part of a game. Progression events form a hierarchy and can have up to N arbitrary tiers (world, region, level, segment, etc.). Note that you do not have to record a start event for a given event ID, you can just record failure or success events as you wish.
There is no fixed number of tiers for progression events. There are convenience functions to generate progression events with 1-5 tiers.
While there is no limit on depth or cardinality of the combination of tier values, be smart about how many total possible values of unique progression event IDs you create based on your planned analysis.
It will automatically compute and record the number of attempts it took to complete a given event successfully.
Progression events may optionally be associated with a numeric value as well (e.g., score).
The generated event ID is derived as follows: <status>:<tier1>:...:<tierN>
Additional event fields beyond common fields:
| Field Name | Type | Description |
|---|---|---|
| status | String | One of Started, Failed, or Completed. |
| tiers | String | The flattened value of all tiers, each separated by : (e.g., addonpack:trial_master_sword) |
| tiers_array | Array of String | The JSON array of all tier values. (e.g., ["addonpack", "trial_master_sword"]) |
| tier1 .. tierN | String | The value of the Nth tier. (e.g., could have a tier2 field with value trial_master_sword) |
| value | Numeric | The optional numeric value associated with the event (e.g., score achieved for the attempt) |
| attempt | Numeric | The number of attempts for this given event ID before successfully completing it. Starts at 1. |
Design Event
Marks any custom event in your game, identified by a given event ID. Event ID is a colon delimited string that forms an event hierarchy of arbitrary depth
(e.g., addonpack:quest:trial_master_sword:unlocked or achievements:single_player:beat_game).
While there is no limit on depth or cardinality of event ID, be smart about how many categories you create based on your planned analysis.
Design events may optionally be associated with a numeric value as well.
Additional event fields beyond common fields (including event ID):
| Field Name | Type | Description |
|---|---|---|
| value | Numeric | Optional numeric value associated with the event. |
Log Event
Records a severity and textual event that is associated with an analytics session. For example, if the game encounters a fatal error that stops gameplay, you could record the internal technical details of the failure.
Log events are a way you can explicitly record especially critical log messages on game clients, without having to capture all logs from a client. You would typically record error events, but you can record a log event of any severity.
Log events have the standard common analytics fields that records information about the current session. It also stores information in the following standard fields in the root of the JSON object so that these events have a similar schema as regular log events:
| Field Name | Type | Description |
|---|---|---|
| severity | Severity | One of Trace, Debug, Info, Notice, Warn, Error, Critical, Fatal, Alert, Panic, Emergency. |
| message | String | The textual message (could be one or more lines of text). This will be processed by AutoExtract. |
Analytics Data Model
Each analytics event is stored as a JSON object that contains a root-level JSON object field g_analytics that contains all
data for the analytics event. In this g_analytics object there are fields common to all event types, as well as fields
specific to each type of event.
Each event will also contain the standard fields (for
example, source with the hostname of the sending machine, and client_ip_location with the two-letter ISO country code
associated with the client's IP address), and any other standard fields that might be added by your game engine specific plugin
(e.g., the Unreal Engine plugin generates a game_instance_id).
Analytics Common Fields
All analytics events will have the following common fields inside of the root-level g_analytics JSON object:
| Field Name | Type | Description |
|---|---|---|
| type | String | One of session_start, session_end, purchase, ad, resource, progression, design, log. |
| reason | String | A textual reason for the event (only present if provided for a given event). |
| session_id | String | A randomly generated unique ID for the current session. 50 character alphanumeric. |
| session_num | Numeric | The chronological number of the session since the game was installed, beginning with 1. |
| session_started | Timestamp | The timestamp when the current session started. |
| session_type | String | Specifies the build configuration of the game engine for this session. One of client, server, editor. |
| app_id | String | The unique identifier for the game as configured in the plugin settings. |
| user_tags | String | A colon-separated list of user tags associated with this user, in alphabetical order (e.g., free_to_play:from_cross_promo). |
| user_tags_array | Array of String | A JSON string array of user tags associated with this user, in alphabetical order (e.g., ["free_to_play", "from_cross_promo"]). |
| user_id | String | 32-char ID for the user. Based on plugin settings is either generated once on first-run or is idvf (iOS) or Android ID. |
| player_id | String | 32-char hash combining app_id and user_id. Should be unique for this game for this install. |
| first_installed | Timestamp | The timestamp when the game was first run. |
| meta.platform | String | The platform on which the game is running (e.g., ios, android, windows, linux). |
| meta.os_version | String | The major and minor operating system version (e.g., 10.0). |
| meta.sdk_version | String | The version of the sparklogs plugin that generated the event (e.g., unreal-plugin-1.0.1). |
| meta.engine_version | String | The version of the game engine used by the game (e.g., unreal-4.27.2). |
| meta.build | String | The build identifier for the game as returned by the game engine or can be customized. |
| meta.device_make | String | The make of the device running the game. |
| meta.device_model | String | The model of the device running the game. |
| meta.connection_type | String | The type of network connection as returned by the game engine. One of None, AirplaneMode, Cell, WiFi, Ethernet. |
Additionally, all purchase, ad, resource, progression, and design events will have the following common fields:
| Field Name | Type | Description |
|---|---|---|
| event_ids | Array of String | A JSON string array with the event hierarchy from higher-level to lower-level. For example, ["level", "highrise", "begin"] |
| event_id | String | The flattened form of event_ids, with each event ID separated by : -- for example, level:highrise:begin. |
Note that event_id does not contain the event type. If you want a truly unique event ID, combine the type and event_id fields.
There are no cardinality limits on the possible values for event_id (or things used to calculate event_id such as item category and item ID), but in general be thoughtful about what values you use and how you will design your analysis.
Example LQL Queries
Because all analytics events contain these fields, the following LQL query will match all analytics events:
g_analytics.type!
You can use LQL to filter on specific parts of the event ID. For example, to find all "level" events, you could use the query:
g_analytics.event_ids[0]=level
Or to find all events that mention the "highrise" level name in any part of the event hierarchy, you could use:
g_analytics.event_ids=highrise
This matches any event where any member of the event_ids array matches exactly the string "highrise". Looser matching on the flattened string field also works just fine:
g_analytics.event_id: highrise
Note that LQL queries are case-insensitive and use the search index by default, and so will search for whole terms (see details).
You can search for partial terms (substring in a word) by surrounding a search term with * (e.g., *highrise*).
Example JSON Data
This section provides JSON for examples of each type of analytics event. This is just for reference, as your game engine plugin will automatically generate the appropriate JSON for you when you record events using the plugin.
- Session Start
- Session End
- Purchase
- Ad
- Resource
- Progression
- Design
- Log
{
"timestamp": "2025-07-15T20:32:53.450Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "session_start",
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:38:09.857Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "session_end",
"reason": "automatically ended at app exit",
"session_ended": "2025-07-15T20:38:09.857Z",
"session_duration_secs": 316.407,
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:33:15Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "purchase",
"reason": "new_user_5min_special_offer",
"event_id": "starter_packs:100_pink_gems_pack",
"event_ids": ["starter_packs", "100_pink_gems_pack"],
"item_category": "starter_packs",
"item_id": "100_pink_gems_pack",
"currency": "EUR",
"amount": 5.00,
"amount_usd": 5.8122375,
"amount_converted": 5.8122375,
"amount_converted_currency": "USD",
"transaction_num": 1,
"first_purchased": "2025-07-15T20:33:15Z",
"custom": {
"offer_id": "offer_12345",
"discount": 0.25,
"view_secs": 17,
"ios_receipt": "abcd1234"
},
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:33:16Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "ad",
"event_id": "admob:end_of_level_chest:rewarded_video:failed_to_show",
"event_ids": ["admob", "end_of_level_chest", "rewarded_video", "failed_to_show"],
"ad_provider": "admob",
"ad_placement": "end_of_level_chest",
"ad_type": "rewarded_video",
"ad_action": "failed_to_show",
"ad_fail_reason": "offline",
"duration_secs": 0.2,
"count": 1,
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:33:15Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "resource",
"reason": "new_user_5min_special_offer",
"event_id": "Source:pink_gems:currency_purchase",
"event_ids": ["Source", "pink_gems", "currency_purchase"],
"flow_type": "Source",
"virtual_currency": "pink_gems",
"item_category": "currency_purchase",
"item_id": "100_pink_gems_pack",
"amount": 100,
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:34:45Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "progression",
"reason": "failed on floor 23",
"event_id": "Failed:addonpack:trial_master_sword:middle_floors",
"event_ids": ["Failed", "addonpack", "trial_master_sword", "middle_floors"],
"status": "Failed",
"tiers": "addonpack:trial_master_sword:middle_floors",
"tiers_array": ["addonpack", "trial_master_sword", "middle_floors"],
"tier1": "addonpack",
"tier2": "trial_master_sword",
"tier3": "middle_floors",
"value": 23,
"attempt": 15,
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:37:37Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"g_analytics": {
"type": "design",
"event_id": "achievements:single_player:beat_game",
"event_ids": ["achievements", "single_player", "beat_game"],
"value": 1688350,
"custom": {
"difficulty": "hard",
"time_taken_secs": 7801,
"score": 1688350,
},
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
{
"timestamp": "2025-07-15T20:38:00Z",
"app": "MyGame",
"hostname": "my-computer",
"pid": 12345,
"game_instance_id": "amqm3opzx4wa8i6ma9j7tzd2",
"severity": "error",
"message": "LogStreaming: Error: Failed to load package '/Game/Maps/LevelEndCredits'",
"g_analytics": {
"type": "log",
"app_id": "ExampleGame",
"user_tags": "free_to_play:from_cross_promo",
"user_tags_array": ["free_to_play", "from_cross_promo"],
"user_id": "A3A3E023442EF421D6C3DCBDBCDC0D3D",
"player_id": "057D1F32934EA2240598BCC9590DAC4C",
"session_id": "bofba3k7w4og4byc73u2lvxyn9f75wy0bfp2ndwmny3m7sn0os",
"session_num": 5,
"first_installed": "2025-07-04T15:45:05.581Z",
"session_started": "2025-07-15T20:32:53.450Z",
"session_type": "client",
"meta": {
"build": "++UE4+Release-4.27-CL-18319896",
"platform": "windows",
"os_version": 10,
"device_make": "AuthenticAMD",
"device_model": "AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics",
"sdk_version": "unreal-plugin-1.0.1",
"engine_version": "unreal-4.27.2"
}
}
}
Analytics Daily Snapshots
In addition to ingesting and querying real-time individual analytics events, SparkLogs will automatically generate daily aggregate snapshots of analytics data. The data is aggregated by day (UTC) and by org_id, app_id, user_id, player_id. Data is generated daily between 00:30 and 03:00 UTC and performs calculations for each day not yet previously aggregated (up to 90 days in the past).
These snapshots are stored in two tables: (workspace_id)_g_analytics_snapshot_users
and (workspace_id)_g_analytics_snapshot_events
and are an efficient way to store and query aggregate analytics data over longer time periods. With this aggregate data you can
calculate key trends such as daily active users (DAU), monthly active users (MAU), retention, revenue, and progression.
Aggregate snapshots are retained for 7 years (paid plan) or 3 months (free plan).
Users Daily Snapshot Table
The (workspace_id)_g_analytics_snapshot_users table contains daily aggregate snapshots of analytics data for
each unique organization, app, and user. Each row represents a single user's activity for a specific day (UTC),
including counts of events, sessions, purchases, ad interactions, and other key metrics. This table is designed
to efficiently support queries for user engagement, retention, monetization, and other trends over time.
You can compute several different kinds of cohorts by grouping on the appropriate field:
- User segmentation based on the
user_tagsfield - Retention cohorts based on the
first_installedfield (useTIMESTAMP_TRUNCto truncate to day, week, month, or year as desired to form a cohort) - Conversion cohorts based on the
first_purchasedfield (similarly useTIMESTAMP_TRUNCas desired) - Location cohorts based on the
client_ip_locationfield (two digit country code) - Platform cohorts based on hardware or software characteristics (e.g.,
build,platform, ordevice_modelfields)
You can calculate statistics for numeric fields that aggregate values for session duration.
This table also allows you to capture and save custom field values from your session_begin and session_end events.
For each user, it will randomly choose custom field data from one of the user's session_begin events on that day and
store it in the field session_begin_custom within the JSON field data_json. Similarly, this will happen for
the session_end_custom field, capturing custom field data from one of a given user's session_end events.
| Field Name | Type | Description |
|---|---|---|
| t_day | Timestamp | The day (UTC) that this snapshot corresponds to. Used for partitioning. |
| org_id | String | Globally unique organization ID. Used for clustering. |
| app_id | String | The analytics app ID. |
| user_id | String | Unique ID of the analytics user. Used for clustering. |
| player_id | String | Unique ID of the analytics player (hash of app_id:user_id). |
| user_tags | String | A colon-separated list of user tags associated with this user, in alphabetical order. If different user tags were used for a given user within a period it will non-deterministically use one of the observed values. |
| event_count | Integer | Number of events for this org:app:user on this day. |
| session_count | Integer | Number of sessions completed (session_end event) for this org:app:user on this day. |
| session_duration_secs_sum | Numeric | Sum of all durations of sessions (seconds) for this org:app:user on this day. |
| session_duration_secs_sum_sq | Numeric | Sum of squares of durations of sessions (seconds) for this org:app:user on this day. Useful for calculating variance and standard deviation. |
| session_duration_secs_min | Numeric | Minimum duration of sessions (seconds) for this org:app:user on this day. |
| session_duration_secs_max | Numeric | Maximum duration of sessions (seconds) for this org:app:user on this day. |
| session_num_min | Integer | Minimum session number for this org:app:user on this day. |
| session_num_max | Integer | Maximum session number for this org:app:user on this day. |
| resource_event_count | Integer | Number of resource events for this org:app:user on this day. |
| progression_started_count | Integer | Number of progression events where status is Started for this org:app:user on this day. |
| progression_failed_count | Integer | Number of progression events where status is Failed for this org:app:user on this day. |
| progression_completed_count | Integer | Number of progression events where status is Completed for this org:app:user on this day. |
| design_event_count | Integer | Number of design events for this org:app:user on this day. |
| first_purchased | Timestamp | Timestamp of user's first purchase; will be NULL if user has never made a purchase. |
| first_purchased_day | Timestamp | Timestamp of user's first purchase truncated to UTC day; will be NULL if user has never made a purchase. |
| is_converting_today | Boolean | Whether the org:app:user made their first purchase on this day. |
| purchase_count | Integer | Number of purchase events for this org:app:user on this day. |
| purchase_amount_converted | Numeric | Total amount of purchases for this org:app:user on this day in the converted currency. |
| purchase_amount_converted_currency | String | The ISO 4217 currency code of the target currency used for converted purchase amounts. |
| transaction_num_min | Integer | Minimum transaction number for this org:app:user on this day or NULL. |
| transaction_num_max | Integer | Maximum transaction number for this org:app:user on this day or NULL. |
| ad_shown_count | Integer | Number of ad impressions shown to this org:app:user on this day (excludes ads with action failed_to_show). |
| ad_completed_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad_action is completed. |
| ad_skipped_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad_action is skipped. |
| ad_rewarded_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad_action is rewarded. |
| ad_clicked_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad_action is clicked. |
| ad_failed_to_show_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad_action is failed_to_show. |
| ad_revenue_converted_count | Integer | Number of ad impressions shown to this org:app:user on this day where ad revenue_converted was > 0. |
| ad_revenue_converted | Numeric | Total amount of ad revenue for org:app:user on this day in the converted currency. |
| ad_revenue_converted_currency | String | The ISO 4217 currency code of the target currency used for converted ad revenue amounts. |
| log_num_debug | Integer | Number of debug log entries for this org:app:user on this day. |
| log_num_info | Integer | Number of info log entries for this org:app:user on this day. |
| log_num_warn | Integer | Number of warning log entries for this org:app:user on this day. |
| log_num_error | Integer | Number of error log entries for this org:app:user on this day. |
| log_num_fatal | Integer | Number of fatal log entries for this org:app:user on this day. |
| first_installed | Timestamp | Timestamp when the user first installed the app. |
| first_installed_day | Timestamp | Timestamp when the user first installed the app truncated to UTC day. |
| engine_version | String | Game engine version. |
| sdk_version | String | The version of the app plugin used to send analytics data. |
| build | String | The app build version. |
| platform | String | The platform on which the game is running (e.g., ios, android, windows, linux). |
| os_version | String | The major and minor operating system version (e.g., 10.0). |
| device_make | String | The make of the device running the app. |
| device_model | String | The model of the device running the app. |
| connection_type | String | The type of network connection as returned by the game engine. One of None, AirplaneMode, Cell, WiFi, Ethernet. |
| client_ip_location | String | The two-letter ISO country code associated with the client's IP address. |
| data_json | JSON | Other custom fields for this org:app:user on this day, including: session_end_custom (custom fields sent with any one of the session_end events) and session_begin_custom (custom fields sent with any one of the session_begin events). |
Events Daily Snapshot Table
The (workspace_id)_g_analytics_snapshot_events table provides daily aggregate snapshots of analytics data for each
unique organization, app, user, event type, and event ID. Each row represents a summary of all events of a specific
event type and ID for a given user for a given app in a given organization on a given day (UTC). Summary information
includes counts, sums, and other statistics for various fields.
Different event types will populate different fields in this table:
purchaseevents populateamount_converted_*andtransaction_num_*fields. (Theamount_fields are also populate but can represent a mix of currencies, we recommend using the converted amounts instead.) Theevent_idindicates the item category and item ID.adevents populaterevenue_converted_*,count_*, andduration_secs_*fields. Theevent_idindicates the ad provider, placement, type, and action.resourceevents populateamount_*fields. Theevent_idindicates if it is aSourceorSinkevent and for what virtual currency, item category, and item ID.progressionevents populateattempt_*fields and if they were populated in the original events, thevalue_*fields. Theevent_idindicates the status (Started,Failed, orCompleted) and tiers.designevents populate thevalue_*fields if they were populated in the original events. Theevent_idindicates the custom event ID that represents the design event hierarchy.
You can calculate statistics for numeric fields that aggregate values for value, attempt, transaction number, amount, converted amount, converted revenue, count, and duration_secs.
Note that you can retrieve a summary of the number of log analytics events per user per day from the
(workspace_id)_g_analytics_snapshot_users table. Log events by severity are not summarized in this table.
| Field Name | Type | Description |
|---|---|---|
| t_day | Timestamp | The day (UTC) that this snapshot corresponds to (partitioned). |
| org_id | String | Globally unique organization ID (clustered). |
| app_id | String | The analytics app ID. |
| user_id | String | Unique ID of the analytics user (clustered). |
| player_id | String | Unique ID of the analytics player (hash of app_id:user_id). |
| user_tags | String | A colon-separated list of user tags associated with this user, in alphabetical order. If different user tags were used for a given user within a period it will non-deterministically use one of the observed values. |
| event_type | String | The class of event, such as purchase, ad, resource, progression, design. |
| event_id | String | The flattened ID of the event, with each part separated by :. |
| event_id_1 | String | The first part of the event ID or NULL. |
| event_id_2 | String | The second part of the event ID or NULL. |
| event_id_3 | String | The third part of the event ID or NULL. |
| event_id_4 | String | The fourth part of the event ID or NULL. |
| event_id_5 | String | The fifth part of the event ID or NULL. |
| event_id_6 | String | The sixth part of the event ID or NULL. |
| event_id_7 | String | The seventh part of the event ID or NULL. |
| event_count | Integer | Number of events with this type:event_id for this org:app:user on this day. |
| value_count | Integer | Count of rows with non-null value fields for this type:event_id. |
| value_sum | Numeric | Sum of all value fields for this type:event_id. |
| value_sum_sq | Numeric | Sum of squares of value fields for this type:event_id. |
| value_min | Numeric | Minimum value field for this type:event_id. |
| value_max | Numeric | Maximum value field for this type:event_id. |
| attempt_count | Integer | Count of rows with non-null attempt fields for this type:event_id. |
| attempt_sum | Numeric | Sum of all attempt fields for this type:event_id. |
| attempt_sum_sq | Numeric | Sum of squares of attempt fields for this type:event_id. |
| attempt_min | Integer | Minimum attempt field for this type:event_id. |
| attempt_max | Integer | Maximum attempt field for this type:event_id. |
| transaction_num_count | Integer | Count of rows with non-null transaction_num fields for this type:event_id. |
| transaction_num_sum | Numeric | Sum of all transaction_num fields for this type:event_id. |
| transaction_num_sum_sq | Numeric | Sum of squares of transaction_num fields for this type:event_id. |
| transaction_num_min | Integer | Minimum transaction_num field for this type:event_id. |
| transaction_num_max | Integer | Maximum transaction_num field for this type:event_id. |
| amount_count | Integer | Count of rows with non-null amount fields. |
| amount_sum | Numeric | Sum of all amount fields. |
| amount_sum_sq | Numeric | Sum of squares of amount fields. |
| amount_min | Numeric | Minimum amount field. |
| amount_max | Numeric | Maximum amount field. |
| amount_converted_currency | String | ISO 4217 currency code for the target currency of converted amounts. Undefined if the target currency changed during the period. |
| amount_converted_count | Integer | Count of rows with non-null amount_converted fields. |
| amount_converted_sum | Numeric | Sum of all amount_converted fields. |
| amount_converted_sum_sq | Numeric | Sum of squares of amount_converted fields. |
| amount_converted_min | Numeric | Minimum amount_converted field. |
| amount_converted_max | Numeric | Maximum amount_converted field. |
| revenue_converted_currency | String | ISO 4217 currency code for the target currency of converted revenue amounts. Undefined if the target currency changed during the period. |
| revenue_converted_count | Integer | Count of rows with non-null revenue_converted fields. |
| revenue_converted_sum | Numeric | Sum of all revenue_converted fields. |
| revenue_converted_sum_sq | Numeric | Sum of squares of revenue_converted fields. |
| revenue_converted_min | Numeric | Minimum revenue_converted field. |
| revenue_converted_max | Numeric | Maximum revenue_converted field. |
| count_count | Integer | Count of rows with non-null count fields. |
| count_sum | Numeric | Sum of all count fields. |
| count_sum_sq | Numeric | Sum of squares of count fields. |
| count_min | Integer | Minimum count field. |
| count_max | Integer | Maximum count field. |
| duration_secs_count | Integer | Count of rows with non-null duration_secs fields. |
| duration_secs_sum | Numeric | Sum of all duration_secs fields. |
| duration_secs_sum_sq | Numeric | Sum of squares of duration_secs fields. |
| duration_secs_min | Numeric | Minimum duration_secs field. |
| duration_secs_max | Numeric | Maximum duration_secs field. |
| data_json | JSON | Currently NULL. Reserved for future use. |
Analytics Materialized Views
g_analytics view
To make it convenient and efficient to query analytics data, SparkLogs generates a materialized view named (workspace_id)_g_analytics that
contains only analytics events and flattens the g_analytics JSON object into top-level fields. The view is
partitioned over the t field and clustered over (t, event_type, event_id).
The most common fields for each supported analytics event type are included in this view.
On the private cloud service plans you can directly access this materialized view in your BigQuery project. The schema is as follows:
| Field Name | Type | Description |
|---|---|---|
| org_id | String | The ID of the organization that owns the data. |
| agent_id | String | The ID of the agent that ingested the data. |
| source | String | The hostname of the machine sending the data. |
| ingested_t | Timestamp | The timestamp when the data was ingested. |
| event_index | Integer | The index of the ingested event within the batch of events submitted in one ingestion request. This allows to reconstruct the exact order of log events even if their timestamps are exactly the same. |
| t | Timestamp | The timestamp of the event, as indicated by the client. This is typically the time when the event was generated in the game. |
| severity | Severity | (log events) The severity level of the log message. 1-24. Has the same meaning as SeverityNumber in the OpenTelemetry spec. (1=trace, 5=debug, 9=info, 13=warn, 17=error, 21=fatal) |
| app | String | The name of the application that generated the event. |
| app_id | String | The unique identifier for the game as configured in the plugin settings. |
| user_tags | String | A colon-separated list of user tags associated with this user, in alphabetical order. |
| user_id | String | 50-char ID for the user. Based on plugin settings is either generated once on first-run or is idvf (iOS) or Android ID. |
| player_id | String | 32-char hash combining app_id and user_id. Should be unique for this game. |
| game_instance_id | String | The unique identifier for the game instance, as generated by the plugin. This is typically a randomly generated ID that is unique for the lifetime of the game engine process. |
| session_id | String | A randomly generated unique ID for the current session. 50 character alphanumeric. |
| session_type | String | Specifies the build configuration of the game engine for this session. One of client, server, editor. |
| session_started | Timestamp | The timestamp when the current session started. |
| session_ended | Timestamp | (session end events) The timestamp when the session ended. |
| session_duration_secs | Numeric | (session end events) The duration of the session in seconds, from the time the session started to the time it ended. |
| session_num | Numeric | The chronological number of the session since the game was installed, beginning with 1. |
| event_type | String | One of session_start, session_end, purchase, ad, resource, progression, design, log. |
| event_id | String | (purchase, ad, resource, progression, and design events) The flattened form of event_ids, with each event ID separated by : -- for example, level:highrise:begin |
event_id_(n) | String | Represents the nth value in the event_ids array, if any, up to event_id_7. For example, event_id_1=level or event_id_3=begin |
| status | String | (progression events) One of Started, Failed, or Completed. |
| value | Numeric | (progression events) The optional numeric value associated with the event (e.g., score achieved for the attempt) |
| attempt | Numeric | (progression events) The number of attempts for this given event ID before successfully completing it. Starts at 1. |
| flow_type | String | (resource events) Either "Source" or "Sink". |
| virtual_currency | String | (resource events) The name of the virtual currency (e.g., "pink_gems", "lives"). |
| amount | Numeric | (resource and purchase events) For resource events, the amount of virtual currency granted or used. This will be enforced to be positive for sources and negative for sinks. For purchase events, the amount of real-world currency spent on the purchase (for example one cent would be 0.01). |
| amount_converted | Numeric | (purchase events) The amount converted into your preferred target currency (e.g., USD) using the average exchange rate at the time of event ingestion. Will only be present if the source currency is supported and the conversion is successful. |
| amount_converted_currency | String | (purchase events) The ISO 4217 currency code of the target currency used for conversion. Will only be present if the source currency is supported and the conversion is successful. |
| currency | String | (purchase and ad events) The currency used for the purchase amount or ad revenue (e.g., "USD", "EUR"). This is the ISO 4217 currency code. Over 200 are supported. |
| transaction_num | Numeric | (purchase events) The number of the transaction since the game was first installed, starting with 1. |
| first_purchased | Timestamp | (purchase events) The timestamp when the first purchase was made since the game was installed. |
| ad_provider | String | (ad events) A string identifying the ad provider. For example, unity, admob, etc. or use unknown |
| ad_placement | String | (ad events) An ID representing where the ad was placed within you app. For example, end_of_level, treasure_chest, etc. |
| ad_type | String | (ad events) Optional. The type of ad that was shown. One of interstitial, video, rewarded_video, banner, native, playable, app_open, offerwall, cross_promo, other, or any custom string. |
| ad_action | String | (ad events) Optional. The action taken by the player in response to the ad. One of completed, skipped, clicked, rewarded, failed_to_show, other, or any custom string. |
| ad_fail_reason | String | (ad events) Optional. The reason the ad failed to show, if applicable. One of no_fill, offline, network_error, invalid_request, other, or any custom string. |
| revenue | Numeric | (ad events) Optional. The total revenue generated from these ad impression(s), if known. |
| revenue_converted | Numeric | (ad events) Optional. The ad revenue converted into your preferred target currency (e.g., USD) using the average exchange rate at the time of event ingestion. Will only be present if the source currency is supported and the conversion is successful. |
| revenue_converted_currency | String | (ad events) Optional. The ISO 4217 currency code of the target currency used for conversion of ad revenue. Will only be present if the source currency is supported and the conversion is successful. |
| duration_secs | Numeric | (ad events) Optional. The duration of the ad in seconds, if applicable. If this event represents multiple impressions, use the average duration. |
| count | Numeric | (ad events) Optional. The number of ad impressions recorded in this event. Defaults to 1 if not specified. |
| log_message | String | (log events) The textual message of the analytics log event. |
| reason | String | A textual reason for the event (only present if provided for a given event). |
| first_installed | Timestamp | The timestamp when the game was first run. |
| engine_version | String | The version of the game engine used by the game (e.g., unreal-4.27.2). |
| sdk_version | String | The version of the sparklogs plugin that generated the event (e.g., unreal-plugin-1.0.1). |
| build | String | The build identifier for the game as returned by the game engine or can be customized. |
| platform | String | The platform on which the game is running (e.g., ios, android, windows, linux). |
| os_version | String | The major and minor operating system version (e.g., 10.0). |
| device_make | String | The make of the device running the game. |
| device_model | String | The model of the device running the game. |
| connection_type | String | The type of network connection as returned by the game engine. One of None, AirplaneMode, Cell, WiFi, Ethernet. |
| client_ip_location | String | The two-letter ISO country code associated with the client's IP address. |
| data_json | JSON | The full JSON data from the original event, including all custom data (will be stored in $.g_analytics.custom). |
Any fields that are not relevant for a given event type will be NULL. Note that the original array forms of certain data (e.g., event_ids) are
preserved in the JSON data in data_json, so you can query the array forms if needed.
g_analytics_event_ids view
Additionally, a second materialized view is generated (workspace_id)_g_analytics_event_ids that contains one row per organization and event ID per hour.
This makes it efficient to get a distinct list of event IDs used in your game, as well as summary statistics per event ID per hour per organization.
The view is partitioned over t_hour and clustered over (org_id, event_type, event_id).
You can calculate statistics for numeric fields that aggregate values for session duration, session number, value, attempt, transaction number, amount, converted amount, converted revenue, count, and duration_secs.
Table schema:
| Field Name | Type | Description |
|---|---|---|
| t_hour | Timestamp | The timestamp of the hour containing the aggregated event data. |
| org_id | String | The ID of the organization that owns the data. |
| event_type | String | One of session_start, session_end, purchase, ad, resource, progression, design, log. |
| event_id | String | (purchase, ad, resource, progression, and design events) The flattened form of event_ids, with each event ID separated by : -- for example, level:highrise:begin |
event_id_(n) | String | Represents the nth value in the event_ids array, if any, up to event_id_7. For example, event_id_1=level or event_id_3=begin |
| event_count | Integer | Number of events within this hour. |
| session_count | Integer | Count of sessions |
| session_duration_secs_sum | Float | Sum of all session durations in seconds |
| session_duration_secs_sum_sq | Float | Sum of squared session durations |
| session_duration_secs_min | Float | Minimum session duration in seconds |
| session_duration_secs_max | Float | Maximum session duration in seconds |
| session_num_min | Integer | Minimum session number |
| session_num_max | Integer | Maximum session number |
| value_count | Integer | Count of rows with non-null values |
| value_sum | Float | Sum of all values |
| value_sum_sq | Float | Sum of squared of all values |
| value_min | Float | Minimum value |
| value_max | Float | Maximum value |
| attempt_count | Integer | Count of rows with non-null attempts |
| attempt_sum | Float | Sum of all attempt numbers |
| attempt_sum_sq | Float | Sum of squared of all attempt numbers |
| attempt_min | Integer | Minimum attempt number |
| attempt_max | Integer | Maximum attempt number |
| transaction_num_count | Integer | Count of rows with non-null transaction numbers |
| transaction_num_sum | Float | Sum of all transaction numbers |
| transaction_num_sum_sq | Float | Sum of squared of all transaction numbers |
| transaction_num_min | Integer | Minimum transaction number |
| transaction_num_max | Integer | Maximum transaction number |
| amount_count | Integer | Count of rows with non-null amounts |
| amount_sum | Float | Sum of all amounts |
| amount_sum_sq | Float | Sum of squared of amounts |
| amount_min | Float | Minimum amount |
| amount_max | Float | Maximum amount |
| amount_converted_currency | String | ISO 4217 currency code for converted amounts (undefined if target currency was changed during the period) |
| amount_converted_count | Integer | Count of rows with non-null converted amounts |
| amount_converted_sum | Float | Sum of all converted amounts |
| amount_converted_sum_sq | Float | Sum of squared converted amounts |
| amount_converted_min | Float | Minimum converted amount |
| amount_converted_max | Float | Maximum converted amount |
| revenue_converted_currency | String | ISO 4217 currency code for converted revenue amounts (undefined if target currency was changed during the period) |
| revenue_converted_count | Integer | Count of rows with non-null converted revenue amounts |
| revenue_converted_sum | Float | Sum of all converted revenue amounts |
| revenue_converted_sum_sq | Float | Sum of squared of converted revenue amounts |
| revenue_converted_min | Float | Minimum converted revenue amount |
| revenue_converted_max | Float | Maximum converted revenue amount |
| count_count | Integer | Count of rows with non-null count values |
| count_sum | Float | Sum of all count values |
| count_sum_sq | Float | Sum of squared of count values |
| count_min | Integer | Minimum count value |
| count_max | Integer | Maximum count value |
| duration_secs_count | Integer | Count of rows with non-null duration_secs values |
| duration_secs_sum | Float | Sum of all duration_secs values |
| duration_secs_sum_sq | Float | Sum of squared of duration_secs values |
| duration_secs_min | Float | Minimum duration_secs value |
| duration_secs_max | Float | Maximum duration_secs value |
| log_num_debug | Integer | Number of debug log entries in period |
| log_num_info | Integer | Number of info log entries in period |
| log_num_warn | Integer | Number of warning log entries in period |
| log_num_error | Integer | Number of error log entries in period |
| log_num_fatal | Integer | Number of fatal log entries in period |
Calculating Statistics
The snapshot tables and event IDs materialized view store certain numeric fields that aggregate counts, sums, and sums of squares for a given period. Storing the data in this way allows you to reconstruct averages and standard deviations even when aggregating across multiple periods:
- Average:
mean = SUM(field_sum) / SUM(field_count) - Standard Deviation:
stddev = SQRT((SUM(field_sum_sq) - ((SUM(field_sum) * SUM(field_sum)) / SUM(field_count))) / (SUM(field_count) - 1))
Dashboards and Visualizations
With the private cloud service plans, you can connect your aggregate and granular analytics data to Looker Studio in your own Google Cloud project.
Simply connect Looker Studio to the BigQuery project that holds your SparkLogs data and add dashboard widgets with appropriate queries.
We recommend having your dashboards query the analytics snapshots tables for performance and query cost-optimization.
For large-scale environments, we recommend that you setup a BigQuery Enterprise reservation that auto-scales from zero slots. This will optimize your BigQuery query costs and performance. Note that this does not require a long-term BigQuery commitment.
Querying Custom Data
With the private cloud service plans, you have direct access to your data in your own BigQuery project, including aggregate data and individual events. Individual events store any custom JSON data you sent with each analytics event.
To query and filter on custom data in individual events, use the BigQuery JSON functions
(e.g., JSON_QUERY and LAX_STRING) on the data_json field in the analytics materialized view that was automatically created for you.
For example, to filter on a custom field named offer_id, you can use:
LAX_STRING(JSON_QUERY(data_json, '$.g_analytics.custom.offer_id'))
Use LAX_STRING, LAX_INT64, LAX_FLOAT64, or LAX_BOOL to convert the JSON value to a native SQL type
(or returns NULL if the JSON value is incompatible with the desired type).
Note that any timestamps in your custom data will be stored in the JSON as the number of microseconds since the Unix epoch.
You can convert these to a native SQL timestamp type using the TIMESTAMP_MICROS SQL function.
For example:
TIMESTAMP_MICROS(LAX_INT64(JSON_QUERY(event_json, '$.g_analytics.custom.first_subscribed')))
Use the TIMESTAMP_TRUNC SQL function
to calculate cohorts from a timestamp. For example, to calculate a monthly cohort value from from the standard first_installed field:
TIMESTAMP_TRUNC(session_started, MONTH)
You can also do this with custom fields. For example, to calculate a weekly cohort value from the custom timestamp
field first_subscribed do:
TIMESTAMP_TRUNC(TIMESTAMP_MICROS(LAX_INT64(JSON_QUERY(event_json, '$.g_analytics.custom.first_subscribed'))), WEEK)
Additional Resources
Please share your questions, ideas, and feedback with us in our discord community. SparkLogs is under rapid development and we value your input. The game engine plugins are open source and we welcome bug reports and pull requests.