Logging AI requests in a mobile app: where it breaks
A user taps a button, the app sends a request to the model, a response comes back — but the server has not a single line about this request. The log accumulates in a local buffer on the device and is sent in a batch on the next launch, if it is sent at all.
The app was minimized, the system unloaded it from memory, the network dropped midway through sending — and request traceability breaks exactly where incident investigation begins.
Reproducing such a situation from a support ticket is nearly impossible. There is no request ID, no link between the complaint and a specific model response, and the prompt version and invocation parameters were not saved.
The engineer sees only “it doesn't work for me” and an empty server log, while the mobile app telemetry shows only the fact that a screen was opened.
Separately, request consumption comes up. It is counted in an analytics dashboard, not in tracing: it is unclear how many calls one scenario makes, where retries happen when the connection drops, and how many requests are sent in the background from a minimized app. After a release, the counter grows, and there is no way to explain the growth.
End-to-end logging of AI requests closes both gaps: every request gets an ID and reaches storage, an incident is investigated step by step, and consumption is visible by feature and session. Below, we break down how to build such a scheme.
What managed telemetry of model requests provides
Every model call from the app becomes a record: session ID, build version, screen, request type, context size, latency, and failure flag. This way, mobile app observability stops depending on a tester's retelling — the state is reproduced from data, not from a description. Further, these records deliver three measurable effects that can be verified in the first week.
Consumption per model request is visible in a breakdown: how much went into the input, how much the model returned, which scenario pulls the full context.
It is usually discovered that some screens send the entire dialogue history where the last message would suffice — and this is fixed at the prompt and routing level between the lightweight and heavy model, without cutting features. Savings here are a consequence of precision, not a separate task.
We keep the volume of stored logs under control with a retention policy: full call chains are written only for failures and slow requests, while for successful ones we write per-scenario aggregates.
We use OpenTelemetry for instrumentation, so the data is not tied to a single model provider, and the retention period is set separately for each level of detail — the registry does not grow linearly with traffic.
Failure investigation time drops to minutes: the entire chain is retrieved by session ID — request, response, retry, client version on the device.
The engineer sees exactly where expectation and reality diverge, instead of trying to reproduce the failure in a test environment with similar conditions.
Log collection methods: on-device, server-side, hybrid
The choice of mode is determined by where the record physically ends up and what needs to be reconstructed during investigation.
Client logs are written to the app buffer and sent in a batch on the next launch or when the network appears — this is the only way to catch a crash before the first network request, but on a hard termination the buffer is lost.
Server-side log collection captures backend responses: status code, latency, session ID, and app version. The UI context, however, remains out of frame — such records do not show which action preceded the error or which screen the user stopped on.
| Scheme | When it fits | Limitations |
|---|---|---|
| Client logs | Investigating crashes and freezes on specific device models and OS versions | Record is lost on hard termination; volume grows with the number of devices |
| Server-side collection | Monitoring service stability and end-to-end response latency | No UI context — it is unclear what caused the request |
| Hybrid scheme | The full path from user action to service response is required | Requires an end-to-end identifier and a unified field schema in both streams |
The hybrid logging scheme joins both streams by a common session ID, so an incident can be traced from tap to backend response.
Selective log recording makes such a scheme workable over the long haul: basic events are collapsed into counters, and the full stream is enabled by a flag or for a share of sessions.
As a result, log volume stops growing linearly with the number of users, while incident investigation remains complete. We determine exactly what to log and at what level during the design phase, tailored to your app's load profile.
Stages of implementing logging in a mobile app
Logging AI requests in a mobile app rarely comes down to adding one line of code: you need to decide in advance what to write, where to store it, and how not to drain the user's battery and data traffic.
That is why we proceed step by step — from inventorying request points to handing observability over to your team.
- Logging audit. We inventory AI request points: screens, background tasks, offline scenarios; for each, we record a set of fields — session ID, request type, response time, error code, app version.
- Log storage scheme. We divide records into local and server-side: an on-device buffer with volume limits and rotation, retention period, and masking of personal data before sending.
- Log sending setup. Batch upload by event and timer, backfill after a network outage, separate rules for roaming and weak channels — logs do not compete with primary traffic.
- Testing under real load. A run on live traffic: share of lost records, delivery latency, impact on launch time and battery consumption.
- Observability acceptance. We verify that the request path is reconstructed from tap to response, and that alerts fire on an error spike and response time growth.
- Handover and maintenance. We provide a description of the record format, a procedure for adding new fields, and an on-call procedure for alerts.
Each stage ends with a verifiable artifact — this way it is clear what already works and what is still ahead.
What remains in the project: log schema, rules, procedures
A project is considered delivered not when the app is built, but when the on-call engineer reconstructs the entire chain from a single incident — from user action to model response.
That is why the handover package includes not only source code but also observability artifacts: they can be opened, read, and passed to another team without verbal explanations.
- Log stream schema — where each stream comes from, where it is aggregated, what goes to long-term storage, and what is dropped at the input.
- Personal data masking rules — a list of fields that do not leave the perimeter in cleartext, and verification that the rule fires before writing, not after.
- Log lifetimes — for each type: how long it is kept in hot storage, when it is moved to archive, and what is permanently deleted.
- Recording levels and volume — what is always written, what is enabled by a toggle during investigation: otherwise log volume grows faster than the load.
- Incident investigation procedure — who starts the investigation, within what time, which slices they look at first, and at what point they bring in a developer.
- Monitoring dashboards and alerts — error share by scenario, model response time, failure spikes, trigger threshold, and notification recipient.
- Observability documentation — where logs are stored, how to find a chain by request ID, how to add a new field without breaking masking.
- Plan for volume growth — the sequence of actions: partial event sampling, lowering detail, archiving old periods instead of disabling recording.
Case study: failure investigation accelerated, log volume decreased
A mobile app with an AI assistant logged every request in full: the request text, model response, user attachments, and service headers.
After a few months, investigating a single incident took hours — the needed events drowned in same-type records, and some data could not be stored at all because of personal data.
Worse still: external model failures did not make it into the log at all, and the app showed only a generic error without details.
What was changed
Our engineers moved logging to structured events via OpenTelemetry: every model request is assigned an identifier that links app, gateway, and model response records into a single chain.
Levels were separated — in normal mode, the result, response time, and error code are written; full texts are enabled selectively, by an on-device toggle or by a share of requests.
Personal data is cut at the log input, and the retention period is set by policy separately for each event type.
What changed
Incident investigation dropped from several hours to 15–20 minutes: the entire chain is retrieved by identifier, including the external model response. Log volume decreased by about seven times — due to selective recording and a short retention period for detailed events.
A whole class of failures was eliminated: timeouts and model errors are now visible by the share of unsuccessful requests and response time, and service recovery time is calculated from the failure chart, not from user complaints.
What to do if logs are already going to a third-party service?
If log collection has already been outsourced to a third-party analytics service, the event goes from the device directly into someone else's perimeter, and there is no control point on your side.
Formally, AI request logging works, but when investigating a failure, only an aggregate in someone else's dashboard is available, not raw records for a specific request.
There is one fork: leave it as is and build on top, duplicate the log stream, or replace collection entirely. Each path has its own limitations in terms of data and scope of work.
| Option | When it fits | Limitations |
|---|---|---|
| Build on top with your own collector over the third-party service | A second control point is needed, and current analytics must work without pauses | Two event schemas live in parallel — field names and version instrumentation have to be reconciled manually |
| Duplicate the log stream | You need to make sure nothing is lost before abandoning the previous receiver | Double volume on the device and in the channel; thinning and sending limits are mandatory |
| Replace collection entirely | Your own perimeter already covers requests, and you have your own storage and incident investigation | No backward compatibility: history from the third-party service can only be migrated by export; it cannot be caught up with in real time |
In practice, logging migration goes through duplication: we enable the in-house collector in parallel, compare volumes and field composition, and only then switch the stream.
This way the transition remains reversible — the previous receiver is available until the discrepancy goes to zero.
We separately define exactly what to store: event levels, sampling share, and record lifetime.
Without this, volume grows linearly with the number of requests, and logging itself becomes a source of costs — a retention period for the hot perimeter removes this without losing investigability.
Storage, access, and load: technical doubts of teams
Questions about logs come up not at the start, but at volume: one assistant session is dozens of records. People usually ask about log storage, log access, app load, and cleanup.
We build this into the collection scheme from the start: an on-disk buffer, batch sending, client-side masking, and a retention policy with a fixed period. The format is compatible with OpenTelemetry — the collector can be deployed on your side.
Are logs lost when the app is closed?
No, if records go to an on-disk buffer rather than memory: unsent data is sent on the next launch. Loss is possible if permissions are revoked or the device fails — the buffer size is set in the configuration.
How is personal data masked?
Masking happens before writing, according to a list of rules agreed with your security service: phone numbers, addresses, and tokens are replaced, and the session ID remains in the log.
What happens if the collector is unavailable?
Sending is asynchronous — the user response does not wait for it. Records accumulate in the buffer, retries happen with increasing delay, and old ones are evicted by new ones: the app does not grow in memory and does not crash on timeout.
How to calculate storage volume and when to clean up logs?
Number of requests per day × average record size × retention period — we calculate on a weekly slice. Cleanup is by retention period, automatically: hot storage for failure investigation, then aggregates without personal data.
Let's discuss the task and come back with an estimate and work plan
So that the project estimate is based on facts rather than assumptions, it is enough to bring to the discussion what already exists in your infrastructure: log exports, stream descriptions, and an understanding of which data must not be lost.
- a list of model requests for a typical period — it shows the volume of AI request logging, peak load, and the share of retries;
- current app and server-side logs: format, retention depth, whether they are written by an off-the-shelf layer or custom code;
- storage requirements: retention period, who can access the content, whether personal data is present and the rules for handling it;
- a list of recent failures — response time excesses, external service failures, user complaints: this makes clear which events must be logged;
- environment diagram and access procedure with roles — from test to production;
- metrics by which you judge quality: response time, share of successful calls, data volume per request.
Based on these materials, we come back with an estimate and work plan: scope of work by stage, log storage and rotation scheme, access requirements, a set of metrics, and the rollback procedure to the previous configuration.
Describe the task — we will gather what is missing together on a call and lock in the implementation plan with owners and deadlines.







