---
url: https://simpledatalogs.com/docs/sdks/unity.md
description: >-
  The simpledatalogs Unity package for games and apps; install by git URL or
  OpenUPM, configure, track game events, autocapture sessions, scenes and
  errors, the opt-out, the outbox and caps.
---

# Unity

The `com.xkdlabs.simpledatalogs` package tracks events from Unity games and apps. It has no dependencies, uses no reflection, and keeps a crash-safe outbox on disk, so an event is never lost to a crash or a killed app.

* Unity 2021.3 or newer.
* Android, iOS, desktop and WebGL players, on Mono and IL2CPP.

## Install

In Unity, open **Window → Package Manager**, click **+**, choose **Add package from git URL…** and enter:

```text
https://github.com/XKD-Labs/simpledatalogs-unity.git#v0.1.0
```

Or with [OpenUPM](https://openupm.com):

```sh
openupm add com.xkdlabs.simpledatalogs
```

The Package Manager also offers a **Quick start** sample with a ready-made MonoBehaviour.

::: info Early access
The package isn't on OpenUPM yet, and the `v0.1.0` tag comes with the first release. Until then, add the git URL without the tag:

```text
https://github.com/XKD-Labs/simpledatalogs-unity.git
```

:::

## Quick start

Configure once at launch, on the main thread, for example in `Awake` of a bootstrap object:

```csharp
using System.Collections.Generic;
using UnityEngine;
using XKDLabs.SimpleDataLogs;

public sealed class Analytics : MonoBehaviour
{
    private void Awake()
    {
        // The opt-out first, then identity and default properties, then the key.
        SimpleDataLogs.SetEnabled(userAllowsUsageStatistics);
        SimpleDataLogs.SetDeviceId(anonymousDeviceId);
        SimpleDataLogs.SetDefaultProperties(new Dictionary<string, object>
        {
            ["app_version"] = Application.version,
        });
        SimpleDataLogs.Configure("sdl_pub_…");
    }
}
```

Then track from anywhere, on any thread. It returns at once and never throws:

```csharp
SimpleDataLogs.Track("level_complete", new Dictionary<string, object> { ["level"] = 3, ["duration_s"] = 41, ["score"] = 1200 });
```

Use a **public key** (`sdl_pub_…`) in games. The SDK logs a warning if it sees a secret key.

## Events for games

```csharp
SimpleDataLogs.Track("level_start", new Dictionary<string, object> { ["level"] = 3 });
SimpleDataLogs.Track("purchase", new Dictionary<string, object> { ["sku"] = "gem_pack_small", ["price"] = 0.99, ["currency"] = "USD" });
SimpleDataLogs.Track("tutorial_step", new Dictionary<string, object> { ["step"] = 2 });

// A screen that is not its own scene, such as a menu panel.
SimpleDataLogs.TrackScreen("Shop");
```

Property values are strings, integers, floats, doubles or booleans. Anything else (null, lists, dictionaries, NaN, infinity) is dropped with a console line.

## Autocapture

One argument sends launches, sessions, scene views and errors without any `Track` call. It is off until you pass it.

```csharp
SimpleDataLogs.Configure("sdl_pub_…", autocapture: AutocaptureOptions.All);
```

| Option | What you get |
|---|---|
| `AppLaunches` | `app_launched` on every launch, with `is_first_launch` |
| `Sessions` | `session_start`, a `session_id` on every event, and `session_end` (`duration_s`) once the game has been away 30 minutes; pause and focus loss count as away |
| `SceneViews` | `screen_view` with `screen` = the scene name, each time a scene loads (additive loads too) |
| `Errors` | `app_error` (`message`, `stack`, `log_type`) for logged exceptions and errors; at most 5 per message and 50 per session |

`All` is all four; combine flags to pick, e.g. `AutocaptureOptions.Sessions | AutocaptureOptions.SceneViews`.

* Nothing starts on its own: nothing is sent before you call `Configure`.
* The opt-out applies: with `SetEnabled(false)`, nothing is captured.

## Active and background

Every event tracked now says whether someone was playing, so the [Sessions tab](/guide/web-analytics) can tell active sessions from passive ones:

* `app_state` is `active` while the game is unpaused and focused, else `background`.
* `engaged_ms` is the time in front since the previous event.
* Mark background work yourself with an `app_state` of `background`; your value wins.
* `Configure(key, reportsActivity: false)` sends neither.

## API

| Call | What it does |
|---|---|
| `Configure(ingestKey, endpoint, autocapture)` | Starts the SDK once; `autocapture` defaults to none, `endpoint` to `https://edge.simpledatalogs.com`. Later calls are ignored |
| `Track(eventName, properties)` | Records one event. `event_id` and `ts` are stamped now, when the event happens |
| `TrackScreen(screenName, properties)` | Records `screen_view` with a `screen` prop |
| `SetDeviceId(deviceId)` | Sets, or clears with `null`, the `device_id` on every later event |
| `SetDefaultProperties(properties)` | Replaces the properties merged into every event; the event's own properties win on a clash |
| `SetEnabled(isEnabled)` | The opt-out. `false` ignores new events and **deletes every unsent event**; `true` collects again |
| `Flush()` | Sends everything now. Undelivered events stay on disk |

## The opt-out

Call `SetEnabled` **before** `Configure`. A player who opted out then never sends a byte, and whatever an earlier launch left on disk is deleted instead of sent.

The SDK doesn't persist the choice. Your game's setting is the source of truth, so pass it on every launch.

## Privacy

* The SDK sends only what you track, plus the device id and session id when you set or enable them.
* It never reads `SystemInfo.deviceUniqueIdentifier` or advertising ids. Pick your own anonymous id; the sample keeps a random one in `PlayerPrefs`.
* Error autocapture sends log messages as they are, so keep personal data out of them, or leave `Errors` off.

## How it behaves

### Batching

Events go out in batches of 50, or 10 seconds after the first event of a partial batch (5 seconds in the Editor and development builds). Every request stays under 256 KB, and bodies of 1 KB or more are gzipped (except on WebGL).

### A crash-safe outbox

* Each event is appended to a journal on disk as it is tracked.
* Batches are sealed into files and deleted only after a `202`.
* The next launch resends anything left over, including a journal cut short by a crash.
* Files live in `Application.persistentDataPath/simpledatalogs/`.

### Lifecycle

A hidden `DontDestroyOnLoad` object ticks the SDK a few times a second (ignoring `Time.timeScale`) and flushes when the game pauses, loses focus or quits. Anything a flush can't deliver in time stays on disk for the next launch.

### Errors and offline

| Answer | What the SDK does |
|---|---|
| `202` | Deletes the batch file |
| `401` | Pauses sending and logs one configuration error; nothing is lost. Re-probes after 1 minute, then every 10 minutes, and on the next launch |
| `413` | Splits the batch in half and resends. A single event still too large is dropped, with a console line |
| `415` | Sets the batch aside as `.unsupported` and moves on |
| Anything else, a timeout, offline | Backs off with full jitter from 1 second to 5 minutes, honoring `Retry-After` |

### Caps

The outbox keeps at most **10,000 events, 10 MB or 500 batch files**. Past that, the oldest batches are dropped, with a console line.

No call throws into your game.

## Platform notes

* **IL2CPP and code stripping.** No reflection and a hand-written JSON encoder, so no `link.xml` is needed.
* **WebGL.** Requests go through UnityWebRequest, which uses the browser's networking, so there's no gzip or `User-Agent`. The outbox lives in the browser's IndexedDB-backed `persistentDataPath`.
* **Enter Play Mode Options.** With domain reload off, the SDK resets when play mode starts, so call `Configure` in each play session.

## Troubleshooting

Problems are logged to the Unity console with the prefix `[simpledatalogs]`. A `401` there means the key is wrong or not live yet; events are kept and sent once it works.

For local development, point it at a local ingest:

```csharp
SimpleDataLogs.Configure("sdl_pub_…", endpoint: "http://localhost:4001");
```

## Next steps

* [Unity quickstart](/quickstarts/unity): the shortest path.
* [Core concepts](/guide/concepts#deployments): one deployment per build, such as `steam` and `ios`.
* [Privacy for senders](/reference/privacy): device ids, not emails.
