Unreal Engine Plugin for Logs & Analytics
Overview
An open-source plugin is available to ingest game engine analytics and/or logs data from Unreal Engine games. Key use cases:
- Logs: Ingest and aggregate log data from backend game servers as well as editor environments or dev builds. (Collecting log data from client builds is possible but not recommended, use analytics to understand behavior of your players in production builds.)
- Analytics: Collect analytic events of various kinds, including session start and end, design, progression, real money purchases, resource grants and sinks, and error or other textual events. A textual reason and custom JSON fields can optionally be associated with each analytics event.
- If the feature is enabled for your SparkLogs workspace, daily snapshots of game engine analytics events will automatically be computed, so you can analyze daily active users (DAU), average session statistics, progression funnels, and other key game metrics.
The plugin is open source under a permissive open-source license, and can be used either with the SparkLogs Cloud or any HTTPS endpoint of your choice (e.g., a vector.dev HTTP Server source). Contributions are welcome!
Join the discord to ask questions, provide feedback, or collaborate on enhancements.
Free accounts are available with SparkLogs 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 32 events and each event is about 0.75 KiB).
The SparkLogs Cloud provides a turn-key solution that scales effortlessly with your game to 100s of millions of active users, auto extracts structured fields from your unstructured logs, and also provides daily analytics snapshots, user geolocation (country), currency conversion for analytics purchase events, and other features. SparkLogs has no cardinality limits and has no limits on the size and number of custom JSON fields for your logs and analytics events.
Plugin Features
- Ease of Use: Once configured, the plugin automatically captures logs and analytics events, including built-in analytics events generated by the Unreal Engine. You can record analytics events either from Blueprints or in C++ code using thread-safe static methods. The plugin does not require preconfiguration of any field values or schemas.
- Performance: Keeping overhead low and not interfering with game performance is top priority. The plugin is stress tested to require no more than 0.1% of CPU even at data volumes 10x higher than average. Additionally, in an extreme event where 10s of megabytes of data are generated every second (should never happen in production under normal circumstances), the plugin will process only a fixed amount of work per second -- this is guaranteed to keep CPU overhead low while queuing data. Transmitted data is efficiently compressed to reduce network bandwidth by ~8x.
- Scale: Automatically scales from 0 to 100s of millions of daily active users. Schemaless with no limits on custom fields, and no cardinality limits.
- Reliability: Logs and analytics data are persistently queued until receipt is acknowledged. If a player's local clock is inaccurate, the SparkLogs Cloud will automatically adjust for clock skew.
- Resiliency: If the game engine crashes, it will attempt to flush to disk any pending logs and events, and then when the game engine is restarted, it will resend any queued data. It will also detect any previously open game engine analytics session that was not closed properly (e.g., due to a crash) and will automatically close that session with a session end time of the last known analytics event for that session.
- Security: Data is transmitted over HTTPS and requests are authenticated.
- Flexibility: The plugin supports desktops and mobile platforms. The plugin can be configured to send data to the SparkLogs Cloud or any HTTPS endpoint of your choice.
- Gradual Roll Out: For games with large user bases, you may want to gradually roll out use of the plugin. To do this, you can configure the plugin to only activate a certain percentage of the time, or you can completely control when data is shipped based on custom code. You can also use this plugin in parallel with your existing analytics plugin to ease the transition.
Compatibility
Tested to be compatible with Unreal Engine 4.27 through 5.6 for Windows, Mac, Linux, iOS, and Android. The plugin is likely compatible with slightly older and newer engine versions but such support is untested.
Pull requests to add support for other platforms are welcome. The plugin currently requires multithreading support so adding support for single-threaded platforms would be a little more work but possible, so contact us if this is important to you.
Setup and Configuration
Download or clone the plugin code. To install the plugin:
- For a single project:
- First, make sure your project has C++ active and is building correctly. (Add a blank C++ class if your project is currently Blueprint-only and compile it in Visual Studio.)
- Then, copy the downloaded plugin files into a
sparklogs
subdirectory of your project'sPlugins
directory. When correctly placed thesparklogs.uplugin
file should be in thePlugins/sparklogs/
directory. - Then with the UE editor closed, in Windows explorer right click on
the
.uproject
file for your project and selectGenerate Visual Studio project files
. - Then open the generated Visual Studio solution and build the project. This will build the plugin.
- You can now re-open your project in the UE editor, and the plugin should be available.
- For incorporating into engine builds: copy the downloaded plugin files into a
sparklogs
subdirectory of theEnginePluginsRuntimeAnalytics
subfolder of the engine source and rebuild the engine.
With the plugin now compiled in and available, make sure it shows up in your plugins list in the Unreal Editor
for your project under Edit -> Plugins
and that it's checked as enabled. You can then edit the plugin settings
by going to Edit -> Project Settings -> Plugins -> SparkLogs
.
For data ingestion to the SparkLogs Cloud, 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) 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.
When ingesting data to your own HTTPS endpoint setup your HTTPS server to expect and check HTTP Bearer auth as appropriate.
To configure the plugin, either use the Unreal Engine plugin editor menu (Edit menu, Project Settings, Plugins, SparkLogs)
or edit DefaultEngine.ini
in the [/Script/sparklogs.SparkLogsRuntimeSettings]
section (refer to the USparkLogsRuntimeSettings
class code in the plugin for value names).
General Configuration
For any build configuration (client, server, editor) where you want to send data, configure the data destination and authentication:
- If you're using the SparkLogs cloud, then configure the cloud region, agent ID, and agent auth token.
- If you're sending data to your own HTTP endpoint, then configure the
Custom HTTP Endpoint URI
andAuthorization Header Value
as appropriate.
Note that if you don't want to put authentication information in the DefaultEngine.ini
file, you can also
configure the plugin to disable automatic starting of the shipping engine and then call StartShippingEngine
with explicit credentials.
Analytics Configuration
Specify a value for the Analytics Game ID
setting that uniquely identifies this game from other games you've shipped.
Customize other analytics settings as desired.
You should also activate the plugin to receive Unreal Engine analytics events. To do this, edit DefaultEngine.ini
and set:
[Analytics]
ProviderModuleName=SparkLogs
If you are already using another analytics plugin, we recommend running both plugins in parallel for a while.
Keep your existing analytics plugin installed and configured, and then use the AnalyticsMulticast
plugin to
forward all analytics events to all plugins, including SparkLogs:
[Analytics]
ProviderModuleName=AnalyticsMulticast
ProviderModuleNames=SparkLogs,...
If you have properly configured analytics, at game startup you should see a log message similar to:
LogPluginSparkLogs: Analytics collection is active. GameID='...' UserID='...' PlayerID='...'
With the plugin now configured, you should add logic to record analytics events.
Note that while raw analytics events appear immediately in SparkLogs once received, game engine clients will batch and only send analytics events at the end of a session, after 15 minutes, when the game engine exits, or after 128 KiB of data is queued (whichever comes first). Also, daily analytics snapshots are computed once per day shortly after midnight UTC, and include all data up to the previous day.
Logs Configuration
By default, the plugin is configured to capture logs for server and editor build configurations.
This can be a convenient way to capture logs from your game servers or development environments.
If you want more control over when logs are captured, you can disable automatic starting of the
shipping engine and then manually call FsparklogsModule::GetModule().StartShippingEngine(...)
when appropriate, and set the OverrideCollectLogs
and/or OverrideCollectAnalytics
parameters
as appropriate.
Once properly configured, logs will automatically be captured and shipped to your configured destination. Ingested log data is available immediately for querying in SparkLogs.
Unless you know exactly what you're doing, do not enable collection of logs for client builds. If you have a large number of users (100k+) it could collect and ingest a large amount of data, and you will either quickly reach your quota and all data ingestion will shutoff, or if your quota is large you could incur higher costs than expected. For client build configurations, use the analytics feature to capture specific actionable events (session, design, progression, purchase, resource, etc).
Common Fields for Logs and Analytics
The plugin adds standard fields to all logs and analytics events:
Field Name | Type | Description |
---|---|---|
timestamp | Timestamp | The timestamp when the event was generated. |
app | String | The value of FApp::GetProjectName() . |
hostname | String | The hostname of the machine. |
pid | Numeric | The operating system specific process ID of the game. |
game_instance_id | String | A random ID uniquely identifying this run of the game engine. (Changes every time the game engine starts.) Search on this field to easily find all logs generated by the same game engine process from start to finish. |
client_ip_location | String | This will be added automatically by the SparkLogs Cloud. The two digit ISO country code of the IP address of the machine running the game, if available. |
You can also customize additional attribute values to include in all events if you manually start the shipping engine
when calling FsparklogsModule::GetModule().StartShippingEngine(...)
and passing AdditionalAttributes
.
Additionally, for logs events, a severity field is added based on the severity associated with a given Unreal Engine log message.
Analytics Overview
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 for balance (resource grants and sinks), etc.
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, resource, progression, design, log).
You can use Blueprints or C++ code to manage sessions and record analytics events.
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.
Daily aggregate analytics snapshots are also automatically computed once a day shortly after midnight UTC, allowing you to analyze trends such as daily active users, playtime statistics, player revenue, and other key metrics.
Session Management
You should call the appropriate functions to start and end analytics sessions at the appropriate time (e.g., when gameplay actually begins after exiting the main menu). An analytics session will automatically be started if you attempt to record any analytics event and a session is not already active. Any active analytics session will end if the game engine exits.
Also, on mobile game sessions will automatically end when the app goes to the background, and will automatically resume when the app comes back to the foreground. This behavior can be customized by editing the plugin configuration.
If the game crashes, then on next game startup, the plugin will detect that a previous analytics session was not properly ended and will mark it as ended, using the time of last known recorded analytics event as the time that the previous session ended.
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 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.
- 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. |
amount | Numeric | The amount of real-world currency spent on the purchase. For example one cent would be 0.01. |
amount_usd | Numeric | The amount converted into 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. |
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 the common fields for all data
(e.g., timestamp
, hostname
) as well as the 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.
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, 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. |
game_id | String | The unique identifier for the game as configured in the plugin settings. |
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 game_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 FApp::GetBuildVersion or can be customized (call SetBuildInfo ). |
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 FPlatformMisc::GetNetworkConnectionType() . Only known on mobile. One of None, AirplaneMode, Cell, WiFi, Ethernet. |
Example LQL Queries
Because all analytics events contain these fields, the following LQL query will match all analytics events:
g_analytics.type!
Additionally, all purchase, 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.
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
Example JSON Data
This section provides JSON for examples of each type of analytics event. This is just for reference, as the Unreal Engine plugin will automatically generate the appropriate JSON for you when you record events using the plugin.
- Session Start
- Session End
- Purchase
- 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",
"game_id": "ExampleGame",
"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",
"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,
"game_id": "ExampleGame",
"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",
"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,
"custom": {
"offer_id": "offer_12345",
"discount": 0.25,
"view_secs": 17,
"ios_receipt": "abcd1234"
},
"game_id": "ExampleGame",
"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",
"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,
"game_id": "ExampleGame",
"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",
"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,
"game_id": "ExampleGame",
"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",
"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,
},
"game_id": "ExampleGame",
"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",
"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",
"game_id": "ExampleGame",
"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",
"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"
}
}
}
How to Record Analytics Events
To record analytics events in Unreal Engine, you can use either Blueprints or C++ code.
For Blueprints, search for SparkLogs
functions and invoke any of the Record*
functions to record
an event of the appropriate type.
For C++ code, in your project's *.Build.cs
file, make sure the sparklogs
module is included
in the PublicDependencyModuleNames
list:
PublicDependencyModuleNames.AddRange(
new string[] {
...
"sparklogs",
...
}
);
At the top of the file that needs to invoke analytics logic include the appropriate header:
#include "sparklogs.h"
You can use the static methods in UsparklogsAnalytics
to record analytics events. For example:
UsparklogsAnalytics::AddProgression2(EsparklogsAnalyticsProgressionStatus::Start, TEXT("Levels"), TEXT("Tutorial"), TEXT("welcome_screen_popup"));
These static methods have overloaded definitions that optionally allow you to specify a "reason" for the event (in the example above, the reason is "welcome_screen_popup") and/or specify a custom JSON object with fields to include with the event.
Confirming Data Flow
When running your game from editor mode, analytics events will be transmitted within a few seconds and then will be available immediately within SparkLogs for querying. Also when playing the game in editor mode, by default the plugin will also log information about each analytics event that is recorded. You can use the following LQL query to show only analytics events:
g_analytics.type!
For shipping builds, analytics events are batched and transmitted only after 15 minutes, when the analytics session ends, when the game engine exits, or when 128 KiB of data is queued (whichever comes first). Transmitted events are available immediately in SparkLogs for querying.
Recording Events on Backend Servers
On server-side sessions, you may want to record analytics events on behalf of a certain client and
associate these events with that client's analytics session. To do this, the client should transmit
the information in the FSparkLogsAnalyticsSessionDescriptor
struct (obtained from calling
UsparklogsAnalytics::GetSessionDescriptor
) to the server. Then the
server can pass this struct to the various UsparklogsAnalytics::Add*
methods. The server should
not begin/end analytics sessions, but instead should pass an appropriate session descriptor that
specifies how an individual analytics event will be associated with the appropriate client session.
Recording Analytics From Other Game Engines
If you're using another game engine other than Unreal Engine, you can still 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 this 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 submitted 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 at the end of a game session. Alternatively, you could use the batching scheme of our Unreal Engine plugin, which is to send queued events only after 15 minutes, when 128 KiB data is queued, when the analytics session ends, or when the game engine exits (whichever comes first).
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 15 minutes. For example, for fluentbit, set the service flush setting to 900 (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, then log your game engine output to a (rotating) file and then use any log forwarding agent to ship your logs.