> For the complete documentation index, see [llms.txt](https://developers-apps-in-toss.toss.im/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-en/unity/add-features/sentry-integration.md).

# Sentry integration

[Sentry Unity SDK](https://docs.sentry.io/platforms/unity/)When you install it, the SDK automatically attaches the Apps in Toss platform context to crash and error events and sends them. This document explains how to turn on that integration and exactly what values are attached automatically.

In projects that have not installed the Sentry SDK, the integration code **is not compiled at all.** Since there is no runtime overhead or compile error, you do not need to read this document if you are not using it.

### Installation

`AIT > Install Sentry SDK` When you click the menu, Package Manager installs the Sentry Unity SDK. If it is already installed, the menu is disabled.

To add it manually `Packages/manifest.json`into

{% code collapsedlinecount="10" %}

```json
{
  "dependencies": {
    "io.sentry.unity": "https://github.com/getsentry/unity.git#4.1.0"
  }
}
```

{% endcode %}

The minimum required version is `io.sentry.unity` **4.0.0**is. The version installed by the menu is **4.1.0**is.

After installation `Tools > Sentry`and enter the DSN, and you're done. There is nothing to configure for the AIT integration itself. You can find the DSN in the Sentry project's `Settings > Client Keys (DSN)`The entered value is checked in `Assets/Resources/Sentry/SentryOptions.asset`and saved there.

### Automatically attached context

#### Tag

| Tag                    | Source                          | Example                 |
| ---------------------- | ------------------------------- | ----------------------- |
| `ait.sdk_version`      | `AITVersion.FullVersion`        | `2.4.7`                 |
| `ait.unity_version`    | `Application.unityVersion`      | `6000.3.3f1`            |
| `ait.commit_hash`      | `AITVersion.CommitHash`         | `9d42c0b`               |
| `ait.current_scene`    | current active scene            | `MainMenu`              |
| `ait.device_id`        | `AIT.GetDeviceId`               | `abc123...`             |
| `ait.platform_os`      | `AIT.GetPlatformOS`             | `iOS`, `Android`        |
| `ait.locale`           | `AIT.GetLocale`                 | `ko-KR`                 |
| `ait.toss_app_version` | `AIT.GetTossAppVersion`         | `5.80.0`                |
| `ait.environment`      | `AIT.GetOperationalEnvironment` | `production`, `staging` |
| `ait.deployment_id`    | `AIT.EnvGetDeploymentId`        | `deploy-xyz`            |

The first four are set synchronously and immediately, `ait.device_id` the following six are filled by asynchronously calling the platform API.

`ait.commit_hash`is not set at all in builds where the commit hash cannot be determined. **is not set at all.** If the remaining platform tags cannot obtain a value, no tag is attached — there is a difference between having no value and `unavailable`being entered as the value as the string

`ait.current_scene`is updated every time a scene is loaded, so it points to the scene at the time the event occurs.

#### User

Only when the device ID is obtained `User.Id`is set to that value. If the device ID cannot be obtained, `User`it is left untouched.

#### Context object

`apps_in_toss` a custom context with the name

{% code collapsedlinecount="10" %}

```json
{
  "sdk_version": "2.4.7",
  "unity_version": "6000.3.3f1",
  "device_id": "abc123...",
  "platform_os": "iOS",
  "locale": "ko-KR",
  "toss_app_version": "5.80.0",
  "environment": "production",
  "deployment_id": "deploy-xyz"
}
```

{% endcode %}

Unlike tags, this object **also fills in items whose values could not be obtained `unavailable` with strings.** This is so that you can read from the event which API failed immediately. The commit hash that is not present in tags is also not here, and the one that is only in tags, `current_scene`is not here either.

#### Breadcrumb

A breadcrumb is recorded every time a scene is loaded.

| Field    | Value                                          |
| -------- | ---------------------------------------------- |
| message  | `Scene loaded: MainMenu`                       |
| category | `scene`                                        |
| level    | `Info`                                         |
| data     | `scene_name`, `scene_build_index`, `load_mode` |

### Analytics integration

`AITSentryAnalytics`is a wrapper that also records Analytics API calls as Sentry breadcrumbs. `AIT.AnalyticsScreen`If you call this instead of calling it directly, the same call is also left as context for the Sentry event.

{% code collapsedlinecount="10" %}

```csharp
using AppsInToss.Sentry;

// AIT.AnalyticsScreen call + Sentry breadcrumb recording
await AITSentryAnalytics.TrackScreen(new { screen_name = "MainMenu" });
await AITSentryAnalytics.TrackImpression(new { item_id = "banner_1" });
await AITSentryAnalytics.TrackClick(new { button = "start" });
```

{% endcode %}

To automatically record a screen on every scene transition, turn on one flag.

{% code collapsedlinecount="10" %}

```csharp
AITSentryAnalytics.AutoScreenTrackingEnabled = true;
```

{% endcode %}

When enabled `SceneManager.sceneLoaded`in `TrackScreen(new { screen_name = scene name })`is called automatically. In this case, when a single scene is loaded, there are **two** breadcrumbs left — the `scene` breadcrumb above and the `analytics` breadcrumb here.

The cumulative call count is `ait_analytics` also attached to the event as a context object.

| Field                                               | Description                                                       |
| --------------------------------------------------- | ----------------------------------------------------------------- |
| `screen_count` / `impression_count` / `click_count` | Cumulative call count by type                                     |
| `last_screen`                                       | The name of the last scene recorded as a screen (if none, `none`) |
| `auto_tracking`                                     | `AutoScreenTrackingEnabled` Current value                         |

> **Note**: `AIT` Like the main API, the return type varies depending on the Unity version. In Unity 6 and later, `Awaitable`, and below that, `Task`is used. For details, see [API usage patterns](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/api-usage-patterns).

### CI environment variables

#### Inject DSN at build time

WebGL is a browser sandbox, so it cannot read environment variables at runtime. So `AITSentryDsnInjector`reads the environment variables during the build preprocessing stage `SentryOptions.asset`and bakes them in.

| Variable             | Purpose                                                      | Example                     |
| -------------------- | ------------------------------------------------------------ | --------------------------- |
| `SENTRY_DSN`         | DSN. If this value is missing, injection is skipped entirely | `https://key@sentry.io/123` |
| `SENTRY_ENVIRONMENT` | Force environment assignment (optional)                      | `production`, `staging`     |
| `SENTRY_RELEASE`     | Force release assignment (optional)                          | `my-app@1.0.0`              |

Injection **only on WebGL builds** works, and `SentryOptions.asset`if it already exists, it is skipped to protect the user's settings. In other words, this path actually creates the file only in CI checkouts where there is no asset.

#### Automatic derivation of environment and release

`SENTRY_ENVIRONMENT` / `SENTRY_RELEASE`If you do not provide `AITSentryReleaseResolver`derives the two values from the SDK version. Sentry's `environment`/`release`are initialization-time-only options, so they cannot be changed at runtime scope; there is no way other than baking them in at build time.

| SDK version | environment                             | release                        |
| ----------- | --------------------------------------- | ------------------------------ |
| stable      | *(unset → Sentry default `production`)* | `apps-in-toss.unity@{version}` |
| prerelease  | `beta`                                  | `apps-in-toss.unity@{version}` |
| unknown     | *(unset)*                               | *(unset)*                      |

* **Purpose**: Errors in beta pilot builds are `environment:beta`separated so they do not pollute stable triage, notifications, or release health. Stable builds do not set the environment, so the existing behavior does not change.
* **Priority**: if an explicit environment variable exists, **it always overrides automatic derivation.**
* **Release consistency**: the derived release uses the same rules as the Sentry release identifier created by the release workflow, so it matches release health and `Fixes` trailer-based auto-resolve linking.

If the SDK version cannot be determined, neither value is baked in and a warning is logged. In that case Sentry uses its default values, so if this was a prerelease build, events may flow into stable triage.

#### sentry-cli

These are the values used to upload debug symbols and source maps. They are read by the CLI, not by the SDK.

| Variable            | Purpose                           | Example                        |
| ------------------- | --------------------------------- | ------------------------------ |
| `SENTRY_AUTH_TOKEN` | API authentication token          | `sntrys_...`                   |
| `SENTRY_ORG`        | organization slug                 | `my-org`                       |
| `SENTRY_PROJECT`    | project slug                      | `unity-game`                   |
| `SENTRY_URL`        | Self-hosted Sentry URL (optional) | `https://sentry.mycompany.com` |
| `SENTRY_LOG_LEVEL`  | CLI log level (optional)          | `info`, `debug`                |

{% code collapsedlinecount="10" %}

```yaml
env:
  SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
  SENTRY_ORG: my-org
  SENTRY_PROJECT: unity-game
```

{% endcode %}

### How it works

#### Conditional compilation

The Sentry integration assembly is compiled only when `AIT_SENTRY_AVAILABLE` define exists.

1. `io.sentry.unity` When 4.0.0 or later is installed, `versionDefines`automatically enables `AIT_SENTRY_AVAILABLE`it.
2. `AppsInToss.Sentry`and `AppsInToss.Sentry.Editor`the `defineConstraints`require this define.
3. If the Sentry SDK is not present, both assemblies are completely excluded from compilation.

#### Automatic initialization

`[RuntimeInitializeOnLoadMethod(AfterSceneLoad)]`initializes it. Since both the Sentry SDK and the SDK body initialize in `BeforeSceneLoad`, the later `AfterSceneLoad`is the first point at which both sides can be accessed safely.

1. `Check whether Sentry is enabled with`— if it is off, stop here
2. Set version and commit hash tags
3. Subscribe to scene load events
4. Asynchronously call the platform API to collect the remaining context
5. Initialize Analytics integration

Step 4 is fire-and-forget, so each API fails independently. Even if one fails, the remaining context is still attached normally.

#### IL2CPP stripping protection

Three layers of protection keep the integration code from disappearing entirely in WebGL (IL2CPP) builds.

| Protection mechanism             | Role                                                          |
| -------------------------------- | ------------------------------------------------------------- |
| `[assembly: AlwaysLinkAssembly]` | Prevents the assembly itself from being removed by the linker |
| `[Preserve]`                     | Preserves individual types and methods                        |
| `link.xml`                       | Declares that all types in the assembly are preserved         |

`AlwaysLinkAssembly`is the key point. Since no other assemblies reference this assembly, without this attribute the IL2CPP linker judges it to be an 'unused assembly' and removes it entirely.

#### Stack trace precision in Unity 6 and later

When you build WebGL on Unity 6 or later, `AITSentryBuildProcessor`enables C# file and line information in IL2CPP stack traces.

{% code collapsedlinecount="10" %}

```csharp
PlayerSettings.SetIl2CppStacktraceInformation(WebGL, MethodFileLineNumber)
```

{% endcode %}

Thanks to this, you can see crash locations in Sentry as exact source lines. This API does not exist in Unity 2021.3/2022.3, so it is skipped automatically, and even if the setting fails, the build continues.

### Troubleshooting

#### Events are not sent

If you see the following log in the Console, the Sentry SDK itself is disabled. This is not an AIT integration issue, but a DSN issue.

{% code collapsedlinecount="10" %}

```
[AITSentry] Sentry SDK is disabled. Skipping AIT context integration. (Check DSN setting: Tools > Sentry)
```

{% endcode %}

If it was attached correctly, this log appears.

{% code collapsedlinecount="10" %}

```
[AITSentry] Initialized - AIT context is automatically added to Sentry events.
```

{% endcode %}

If this is a CI build, in the build log `Created SentryOptions.asset`look for the line starting with it. Right below it, the masked DSN and the automatically derived Environment and Release are printed together. If this line is missing, `SENTRY_DSN`it was empty or the asset already existed, so injection was skipped.

#### No AIT tags in IL2CPP builds

This means the integration code was removed by stripping. The preservation declarations are already shipped together with the SDK as `Runtime/Sentry/link.xml`so you do not need to add them manually. **Even so, if the tags are still missing, the build cache is usually the cause.** Delete

`Library/Bee/artifacts/WebGL/`and do a clean build. Cached results do not reflect `link.xml` changes.

If you have separately modified the stripping settings on the project side, `Assets/link.xml`you can reinforce them by adding the same declarations there.

{% code collapsedlinecount="10" %}

```xml
<linker>
    <assembly fullname="AppsInToss.Sentry" preserve="all"/>
</linker>
```

{% endcode %}

#### Some context is unavailable

This is the case when a platform API call fails. Without retrying, `unavailable`it is finalized as

In Mock bridge environments, some APIs are unsupported, so it is normal for this value to appear. In that case, that item is not present in the tags at all, and `apps_in_toss` only in the context `unavailable`remains as

### Related documents

* [SDK event logging](https://developers-apps-in-toss.toss.im/documentation/unity/add-features/metrics) — runtime events automatically collected by the SDK
* [Getting Started](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/getting-started) — SDK Installation and Basic Setup
* [Troubleshooting](https://developers-apps-in-toss.toss.im/documentation/unity/first-steps/faq) — General Troubleshooting
* [Sentry Unity SDK Documentation](https://docs.sentry.io/platforms/unity/) — Sentry Official Documentation


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-en/unity/add-features/sentry-integration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
