Telemetry & Tracing Configuration
📡 Ditto ID SDK: Telemetry & Tracing (OpenTelemetry)
📘 What this is, in plain terms
As the Ditto ID SDK runs, it can keep a short diary of the steps it takes —
starting up, setting up a session, talking to the server, signing a user in, and
so on. Each diary entry is called a span, and a related group of spans is a
trace. You can have the SDK send this diary to a monitoring service of your
choice so your team can see how the SDK is behaving in the field.
You don't need to change any code to turn this on, off, or point it somewhere.
It is all driven by configuration:
- The server decides the settings for you (it sends a
telemetry_configsobject),
and can change them at any time. - The app has a single master off-switch that always wins over the server.
The SDK reads these settings when it starts, and again whenever the server sends
new ones.
A few words you'll see below:
span / trace = the SDK's diary entries described above ·
collector = the monitoring service that receives them ·
OTLP/HTTP = the standard web format used to send them.
This telemetry diary is not the same as the SDK's log messages. For log
verbosity see RDNALoggingLevel and SDK Logs.
🎯 Quick start
- Send the diary to your monitoring service — have the server deliver a
telemetry_configsobject (in Agent Info, or in the create-session response) with:telemetry_export_mode: "network_only"telemetry_http_endpoint: "https://<your-collector-host>/v1/traces"
(the full web address of your collector's traces endpoint)
- Turn it off from the server:
telemetry_export_mode: "none"intelemetry_configs. - Turn it off from the app, for good: set
disableTrace: 1in the init
otelConfig(see below). While this is set, the server cannot switch it
back on.
⚙️ App-side options (otelConfig at initialize)
otelConfig at initialize)These are passed in the SDK's initialize options. They are optional — letting the
server configure telemetry is the preferred approach.
| Field | Type | What it does |
|---|---|---|
disableTrace | 0 / 1 | 0 = telemetry is allowed (this is the default). 1 = hard off — the SDK creates and sends nothing, and the server cannot override it. |
disableTraceis the only app-side telemetry option. Everything else — where
traces go, the collector address, and all tuning — is configured by the server
(see below).
🛰️ Server-side settings (the telemetry_configs object)
telemetry_configs object)The server is the main place telemetry is configured. It sends the keys below inside a
telemetry_configs object (see Where telemetry_configs comes from below for
the exact locations). Anything you leave out keeps its current value, and any number
that is out of range is quietly pulled back into range (clamped), not rejected.
| Key | Type | Default | Allowed range (clamped) | What it controls |
|---|---|---|---|---|
telemetry_export_mode | string | file_only | none | file_only | network_only | file_network | Where the diary goes (see Export modes). |
telemetry_http_endpoint | string | — | — | The full web address of your collector's traces endpoint. Send an empty string to clear an endpoint you set earlier. |
telemetry_http_export_max_batch_size | int | 512 | 64 – 2048 | How many spans are sent per upload. |
telemetry_max_queue_size | int | 2048 | 256 – 8192 | How many spans can wait in memory before being sent. |
telemetry_schedule_delay_ms | int | 5000 | 500 – 60000 | How often the SDK sends what it has, in milliseconds. |
telemetry_http_export_timeout_ms | int | 10000 | 1000 – 30000 | How long one upload may take before giving up, in milliseconds. |
telemetry_http_export_max_retries | int | 3 | 0 – 10 | How many times to retry a failed upload. |
telemetry_file_max_size_bytes | int64 | 2097152 (2 MiB) | 262144 – 67108864 (256 KiB – 64 MiB) | Size limit for each on-device diary file. Keep it comfortably above one upload's worth of spans — a file always holds whole batches, so a limit smaller than a single batch can't be honoured (hence the 256 KiB floor). (Used by file modes.) |
telemetry_file_max_files | int | 3 | 1 – 100 | How many diary files to rotate through. Total on-device buffer ≈ max_files × max_size_bytes; once full, the oldest file is overwritten. (Used by file modes.) |
No separate on/off switch. Whether tracing is actually running is worked out
automatically from the export mode and whether an endpoint is set (see
Is tracing on? below). There are also no server settings for customheaders
orsamplingin this release.
Where to configure it
For a BAU (NON-SaaS) deployment, telemetry is configured in the management console:
GM → Module Config Management → DC → BlaZe-Adapter →
sdk.app.settings
That console field already holds a JSON object of application settings. Add
telemetry_configs as one more entry alongside the settings already there — you are
editing the value of sdk.app.settings, so do not wrap it in another
sdk_app_settings object (the server adds that layer for you). Save, and the new settings
reach devices on their next create-session.
Example — the value to put in sdk.app.settings
sdk.app.settingsThe … line stands for whatever is already configured in that field — leave it as-is
and add the telemetry_configs block next to it. Every tuning key below is shown at its
default; only telemetry_export_mode and telemetry_http_endpoint are set to a
typical "send to your collector" choice — change those two to suit. It's a partial
config, so you can safely delete any line you don't want to change (an omitted key keeps
its default).
{
"…": "your existing sdk.app.settings entries — leave as-is",
"telemetry_configs": {
"telemetry_export_mode": "network_only",
"telemetry_http_endpoint": "https://otel.example.com/v1/traces",
"telemetry_http_export_max_batch_size": 512,
"telemetry_max_queue_size": 2048,
"telemetry_schedule_delay_ms": 5000,
"telemetry_http_export_timeout_ms": 10000,
"telemetry_http_export_max_retries": 3,
"telemetry_file_max_size_bytes": 2097152,
"telemetry_file_max_files": 3
}
}Common mistake: pasting the whole
{ "sdk_app_settings": { … } }wrapper into the
field. That nests it one level too deep. The field issdk.app.settings— enter only
its value, withtelemetry_configssitting beside your other entries.
📍 Where telemetry_configs comes from
telemetry_configs comes fromAll the keys above live inside a single telemetry_configs object. The server can
deliver that object from a few places; the SDK looks for it in this order and the
first match wins:
telemetry_configsbesidesdk_app_settings(a sibling in the create-session
response) — checked first.telemetry_configsinsidesdk_app_settings— used only if there is no
sibling one.telemetry_configsin Agent Info (the connection profile) — applied earlier,
at initialize.
The create-session sources (1 & 2) are the highest-priority live tier and override
Agent Info; the resolved result is remembered (encrypted) on the device for the next
launch. All of them are partial — send only the keys you want to change; anything
you leave out keeps whatever value was already in effect.
Why two spots in the create-session response? (BAU vs SaaS)
A single SDK build serves both server types, so it accepts telemetry_configs
whichever way your server sends it:
| Server type | How it sends telemetry_configs | SDK behaviour |
|---|---|---|
| SaaS server (has a connection profile) | as a sibling of sdk_app_settings (option 1) | picked up first; sdk_app_settings isn't consulted for telemetry |
| BAU server (no connection profile) | nested inside sdk_app_settings (option 2) | used because no sibling is present |
You don't choose this in the app — it's determined by which server the device talks to;
the SDK resolves it automatically.
Agent Info — the telemetry_configs object sits alongside the profile's other
sections (the rest is abstracted here):
{
"agent_uuid": "…",
"preferred_agent": "agent_rel_id",
"agent_rel_id": "…",
"device_security_check": { "…": "MTD / security section" },
"telemetry_configs": {
"telemetry_export_mode": "network_only",
"telemetry_http_endpoint": "https://otel.example.com/v1/traces",
"telemetry_schedule_delay_ms": 5000
}
}Create-session response (SaaS) — telemetry_configs as a sibling of
sdk_app_settings:
{
"telemetry_configs": {
"telemetry_export_mode": "network_only",
"telemetry_http_endpoint": "https://otel.example.com/v1/traces"
},
"sdk_app_settings": { "…": "other app settings" }
}Create-session response (BAU) — telemetry_configs lives inside the
sdk.app.settings value (which the server delivers under sdk_app_settings). You don't
build that wrapper yourself — just configure the sdk.app.settings value in GM as shown
in Where to configure it above.
🧭 What each setting does
| Key | What it does |
|---|---|
telemetry_export_mode | The master switch for where traces go: none (off), file_only (keep on device only), network_only (send live to your collector, no file), file_network (keep on device and forward to the collector). |
telemetry_http_endpoint | Your collector's full traces web address that traces are sent to. Needed for any sending; an empty string clears it (and in network_only, sending stops until a new address arrives). |
telemetry_http_export_max_batch_size | How many traces are grouped into one upload. Bigger = fewer, larger uploads; smaller = more frequent, lighter ones. |
telemetry_http_export_timeout_ms | How long a single upload may run before it's given up on — protects against a slow or unreachable collector holding things up. |
telemetry_http_export_max_retries | How many times a failed upload is retried before those traces are dropped. 0 means no retry. |
telemetry_max_queue_size | How many traces can wait in memory for sending. When it's full, new ones are dropped — this protects app memory if the collector is slow or offline. |
telemetry_schedule_delay_ms | How often the SDK sends whatever it has waiting, even if a full batch hasn't built up yet. |
telemetry_file_max_size_bytes | The size limit for each on-device buffer file; when a file reaches it, the SDK moves on to the next one (file modes). |
telemetry_file_max_files | How many buffer files are rotated through. Together with the size limit this sets the total on-device buffer ceiling (files × size) before the oldest data is overwritten (file modes). |
⏱️ How and when traces are sent
Traces aren't sent one-by-one as they happen — that would drain battery and network.
Instead the SDK collects them in memory and sends them in batches. A batch goes out
when either of these happens first:
- The timer elapses — every
telemetry_schedule_delay_ms(default 5 s) the SDK
sends whatever it has waiting, even if that's just one trace. - A batch fills up — as soon as
telemetry_http_export_max_batch_sizetraces
(default 512) have piled up, they're sent right away without waiting for the timer.
So sending is both time- and size-driven — whichever comes first. In a quiet app the
5-second timer does the work; during a busy burst the batch-size trigger kicks in so
memory doesn't keep growing.
The waiting room. Traces wait in an in-memory queue that holds up to
telemetry_max_queue_size (default 2048). If your collector is slow or offline and the
queue fills up, new traces are dropped to protect the app's memory — telemetry never
blocks or crashes the app.
Each upload carries up to telemetry_http_export_max_batch_size traces, may run up to
telemetry_http_export_timeout_ms (default 10 s) before it's given up on, and if it
fails it's retried up to telemetry_http_export_max_retries times (default 3) before
those traces are discarded.
Tuning the behaviour
| You want… | Change | Effect |
|---|---|---|
| Traces to arrive sooner | lower telemetry_schedule_delay_ms (e.g. 1000) | more frequent, smaller uploads (slightly more battery/network) |
| Fewer, larger uploads (save battery/network) | raise telemetry_schedule_delay_ms and/or telemetry_http_export_max_batch_size | traces arrive less often, grouped together |
| Headroom for bursty activity | raise telemetry_max_queue_size | fewer drops when many traces are produced at once |
| To give up faster on a bad collector | lower telemetry_http_export_timeout_ms and telemetry_http_export_max_retries | less time and effort spent on uploads that won't succeed |
All of these are server-side keys inside telemetry_configs — change them the same way as
any other setting (see Where to configure it). Values out of range are quietly clamped.
Mobile reality: these sends happen only while the app is running. If the app is swiped
away or killed before the next send, traces still waiting in memory are lost. To keep
them across restarts, use a file mode (file_network): traces are written to the
on-device buffer first and forwarded afterwards — see On-device buffering &
store-and-forward.
🧮 How missing or invalid values are handled
The SDK never rejects a bad telemetry value — it always resolves to something safe.
There are three behaviours depending on the key:
| Kind of key | If the value is missing | If the value is invalid / out of range |
|---|---|---|
| Numbers — batch size, queue size, timeout, retries, schedule delay | keeps the value already in effect | pulled back into the allowed range (e.g. 0 → the minimum, a huge number → the maximum) |
File limits — telemetry_file_max_size_bytes, telemetry_file_max_files | keeps the value already in effect | zero or below resets to the default (2 MiB / 3 files); a positive value is then pulled back into the allowed range (256 KiB – 64 MiB per file, 1 – 100 files) |
Mode & endpoint — telemetry_export_mode, telemetry_http_endpoint | keeps the value already in effect | an unrecognised mode is ignored (previous mode kept); an empty endpoint clears the address |
"Keeps the value already in effect" vs "the default": the built-in defaults are set
once at startup. Each source (Agent Info, then the remembered settings, then the
create-session telemetry_configs) is layered on top and only changes the keys it
actually includes. So
a key you don't send is not reset to its default — it simply keeps whatever the
previous layer set. Defaults only apply to keys that no source ever set.
The full default and range for every key is in the Server-side settings table above.
🔀 Export modes
The export mode simply says where the diary should go.
| Mode | What happens | Available now? |
|---|---|---|
none | Off. Nothing is recorded or sent. | ✅ Yes |
network_only | Send entries straight to your collector over the web. | ✅ Yes |
file_only | Keep entries on the device only (no network). | ✅ Yes |
file_network | Keep entries on the device and send them to your collector — so nothing is lost if the device is offline or the app is closed. | ✅ Yes |
Default:
telemetry_export_modedefaults tofile_only— out of the box the
SDK buffers to the device and nothing leaves it until you switch tonetwork_onlyor
file_networkand set an endpoint.
Is tracing on? (worked out automatically)
| Mode | Endpoint set? | Result |
|---|---|---|
network_only | yes | On |
network_only | no | Off — and it turns itself on automatically the moment an endpoint arrives |
none | — | Off |
file_only / file_network | any | On |
And remember: the app's disableTrace: 1 switch beats all of the above and
forces tracing Off.
🥇 Which setting wins?
The same setting can come from more than one place. When that happens, the higher
one on this list wins (and each level only changes the specific keys it provides):
- Built-in defaults
telemetry_configsin the connection profile (Agent Info)- The last settings we saved on the device (used at startup, before the server answers)
- App
disableTraceoff-switch — beats everything except the server's live settings for the keys it doesn't touch, and can never be overridden as an off-switch telemetry_configsfrom the create-session response (sibling ofsdk_app_settings, else nested inside it) — the highest live source
The resolved settings are saved on the device in encrypted form, so the SDK can
apply the last known behaviour on the next launch even before the server responds.
🚦 Turning it on and off
- Switching
telemetry_export_modetonone(or removing the endpoint) stops
sending. The SDK keeps everything ready in the background so it can start again
instantly when you re-enable it — no restart needed. - Setting the app's
disableTrace: 1is a hard off: nothing is recorded, and no
server setting can turn telemetry back on while it stays set.
Two ways to keep tracing off — and they are not the same
| How you turn it off | Can the server turn it back on? | Use this when… |
|---|---|---|
App disableTrace: 1 (in the init otelConfig) | No — it's a hard off | You must honour a user saying "no", or meet a compliance / legal requirement |
Off in the connection profile / Agent Info (e.g. mode none, no endpoint) | Yes — this is only a soft default, and a later telemetry_configs from the server can switch it on | It's just an operational default you're fine letting the server change |
Rule of thumb: if the reason for turning telemetry off is consent or
compliance, always use the app'sdisableTrace: 1. Turning it off only in
Agent Info can be silently switched back on by the server.
🔒 Privacy, what's collected & compliance
The diary leaves the device only when you set up an HTTP endpoint (modes
network_only or file_network). Because an entry can include a device identifier,
privacy laws may treat it as personal data — even though it contains no names,
passwords, or message content. Please read this section before turning on HTTP
export in production.
This is integration guidance, not legal advice — confirm your obligations with
your own privacy / legal team.
What goes into an entry
Included:
- Operation names — the SDK step being recorded (for example
EnrollUser,
ForgotPassword,AuthenticateChannel,coreLogOff, and session/channel setup).
These say what the SDK did, not who did it. - A device tag / identifier — a REL-ID device identifier. This is the one item
that could tie an entry back to a specific device (and so, indirectly, a person). - The server host / port — which REL-ID server the device talked to
(infrastructure detail, not user data). - Result / status codes and a few operation labels (for example the step-up
authentication operation/api). trace_id/span_id— random reference numbers; they are not built from any
user or device detail.
Never included — the SDK never puts any of these into an entry: names, email,
phone, user IDs, login IDs, session IDs, passwords or PINs, biometric data,
security keys or tokens, card numbers or any cardholder data, or the contents of any
message.
Private by default
- Nothing leaves the device unless you ask for it. Sending starts only once you
set an HTTP endpoint;network_onlywith no endpoint stays off. - There is a hard off-switch —
disableTrace: 1(see the table above) is the one
to connect to a user's consent choice. - Only the minimum is collected — the list above is fixed and small, and no
free-form user content is ever attached. - Protected in transit and at rest — sending uses OTLP/HTTP, so please use
HTTPS; any on-device diary is kept in the app's private storage and is
size-limited (it rotates through a fixed set of files).
Your part as the integrator
- Where the data lives — the diary goes to the exact
telemetry_http_endpoint
you set. If your users are in a regulated region (EU/EEA, India, and so on), point
them at a collector in that region and record the reason for any cross-border
transfer. Keep DEV/QA and PROD (and, where needed, per-region) endpoints
separate. - Consent & transparency — mention telemetry in your privacy notice; where
consent is required (for example ePrivacy), only send after you have it, and wire
the user's opt-out todisableTrace: 1. - Controls on your side — set retention limits and access controls at your
collector, and remove duplicates bytrace_id/span_id(delivery can send the
same entry more than once). - Laws that often apply when the diary leaves the device include GDPR (a
device identifier counts as an "online identifier", i.e. personal data),
ePrivacy, CCPA/CPRA and other US state laws, the India DPDP Act, and —
for regulated industries — PCI-DSS / GLBA / HIPAA / DORA. The SDK's
data-minimisation and off-switch are there to help you meet these; the lawful basis,
consent, and endpoint choice remain your responsibility.
🗄️ On-device buffering & store-and-forward (file modes)
The file modes (file_only / file_network) provide durable on-device buffering so
entries are not lost when the device is offline or the app is closed before the
next send:
- Entries are written to a size-limited, rotating set of files in the app's
private storage (by default: 3 files × 2 MiB), controlled by
telemetry_file_max_files/telemetry_file_max_size_bytes. - In
file_network, a background helper delivers the saved entries to your collector and
clears them once accepted. The same entry may occasionally be sent more than once,
so your backend should de-duplicate bytrace_id/span_id. - If tracing is turned off (either the
nonemode or the app'sdisableTrace: 1
off-switch): the SDK stops creating new entries and writes nothing more to disk, but
entries already saved are kept on the device — they are never wiped. They stay put
(in the bounded, rotating buffer) and are sent the next time tracing is on with a
reachable endpoint. - No special Android/iOS permissions are needed (it uses private app storage).
❓ FAQ
- Do I need any Android/iOS permissions for telemetry? No.
- Can the server force telemetry on if the app turned it off? No —
disableTrace: 1is a hard off. - I turned tracing off only in Agent Info / the connection profile — is that a hard
off too? No. That's only a soft default, and the server can switch it back on
via a latertelemetry_configs. UsedisableTrace: 1for a real,
consent/compliance-grade off-switch. - Does telemetry contain personal data? It can — an entry may include a device
identifier, which counts as personal data under laws like GDPR. It never includes
names, passwords, biometrics, tokens, card data, or message content. See
Privacy, what's collected & compliance. - Where should I send the diary for EU / India / other regulated users? To a
collector in the right region, using separate DEV/QA and PROD endpoints. Where
the data lives and the lawful basis are your responsibility as the integrator. - What format is used to send it? The OpenTelemetry standard over the web
(OTLP/HTTP), sent to thetelemetry_http_endpointyou set (the full/v1/traces
address). Bothhttp://andhttps://endpoints are supported — usehttps://
in production (the mode namenetwork_onlyrefers to the destination, not the
protocol version). - Where does the diary show up? In whatever OpenTelemetry-compatible tool your
endpoint feeds (for example a collector → Tempo/Jaeger → Grafana). Filter by
service.name(set to your tenant/app id).
Updated 4 days ago
