Delivery and offline

Batching, retries, the offline queue, and settings fetched from the dashboard.

Batching

Logs wait in memory and go out together in one request to POST /{projectId}/logs/batch. A batch is sent:

  • as soon as batchSize logs are waiting (default 10);
  • every flushIntervalMs (default 5,000 ms);
  • when you call await logger.flush();
  • when the visitor leaves or hides the page.

At most 1,000 logs wait at once; beyond that the oldest are dropped, with a console warning. Two logs with the same message and the same timestamp in one batch are sent once.

The API takes 100 logs per request

Each flush sends everything waiting, and the API rejects a request with more than 100 logs. Keep batchSize at 100 or less. If Apperio can’t be reached for a while and more than 100 logs pile up, every later flush is refused too, and nothing more from that page arrives.

Retries

  1. If a request fails with a network error or a 5xx response, the SDK tries again, up to maxRetries more times (default 3).
  2. It waits retryDelayMs before the first retry (default 1,000 ms) and doubles the wait each time, with a little random jitter, up to 60 seconds.
  3. A 4xx response, such as 401, 403, 400 or 429, isn’t retried: it would fail the same way.
  4. If every attempt fails, or the request was refused, the logs go back to the front of the queue and the next flush tries again.

While this happens you see messages such as Apperio: Network error on attempt 1 in the console.

When the visitor leaves

A normal request is cancelled when the page unloads. So when the page is hidden or unloaded, the SDK sends what is waiting with keepalive, which the browser finishes in the background. Browsers cap these requests, so up to about 40 KB of logs go that way and anything beyond in a normal request, as a best effort. There are no retries at this point. This needs no code from you.

Offline queue

In a browser, when the device goes offline, new logs go to a separate queue instead of being sent. When the connection comes back, the queue is sent in one request. It is on by default and only uses memory, so a queue is lost if the tab closes.

TypeScript
import { Apperio } from 'apperio';

const logger = new Apperio({
  apiKey: 'your-api-key',
  projectId: 'your-project-id',
  offline: {
    maxQueueSize: 100,
    prioritizeCritical: true,
    onOffline: () => console.info('Offline: holding logs'),
    onOnline: () => console.info('Back online'),
    onSyncComplete: (count) => console.info(`Sent ${count} queued logs`),
  },
});
OptionDefaultWhat it does
maxQueueSize500Most logs held while offline.
prioritizeCriticaltrueWhen the queue is full, drop the oldest log that isn’t an error or fatal first. Otherwise drop the oldest.
onOffline, onOnlinenoneCalled when the browser goes offline or comes back.
onSyncCompletenoneCalled with the number of queued logs sent after reconnecting.

Set maxQueueSize to 100 or less

The queue goes out as a single request when the connection returns, and the API rejects more than 100 logs per request. With the default of 500, a long offline spell can fill a queue that is never delivered.

Remote config

Off by default. Turn it on to let Settings › SDK Config in the dashboard adjust a running SDK:

TypeScript
import { Apperio } from 'apperio';

const logger = new Apperio({
  apiKey: 'your-api-key',
  projectId: 'your-project-id',
  remoteConfig: {
    enabled: true,
    refreshIntervalMs: 300000, // default: every 5 minutes
    onConfigUpdate: (config) => console.info('Apperio settings updated', config),
  },
});

The SDK fetches the settings when it starts and then on every interval. Once fetched, these replace the values in your code:

  • Minimum Log Level
  • Batch Size and Flush Interval
  • the sanitization preset (Strict Mode)

The preset replaces any sanitization settings you passed in code, including custom rules. Everything else on that page, such as environment, service name, the auto-capture switches, and the sensitive fields and custom rules you enter there, doesn’t change a running SDK. Set those in code.

Other exports

The package also exports CircuitBreaker, compressPayload and HealthMetricsCollector. The Apperio class doesn’t use them when sending logs, and you don’t need them to use Apperio, so they aren’t covered here. AutoInstrumentation and BreadcrumbManager are exported too; the logger creates and manages them for you. See Configuration for everything the logger itself supports.