Controlling Volume
An app with a hundred thousand users writing debug logs will produce more rows than a single Postgres wants to hold, and it should not have to. The answer is restraint at the source rather than a bigger machine, and there are four separate controls. They stack, so it helps to know which one does what.
Sampling whole launches
Section titled “Sampling whole launches”This is the one to reach for first.
LoggingBehavior(sessionSampleRate: 0.1)That records one launch in ten. It is a fraction between 0 and 1, not a percentage, so 0.1
is ten per cent and 10 is not ten per cent of anything. Values outside the range are clamped
rather than rejected, so a mistake makes the app quieter or louder without breaking it.
The decision is made once when the app starts, not once per log. That matters more than it sounds. A launch that is kept keeps its whole timeline, so you can follow what somebody did from one end to the other. Sampling individual logs instead would leave gaps that look exactly like your app doing nothing, which is the worst possible failure for a debugging tool.
A launch that is sampled out costs almost nothing, because the check sits above the database write. Nothing is stored and nothing is uploaded.
It still works on the device
Section titled “It still works on the device”Sampling only affects what is stored and uploaded. Your console and the in-app panel show everything either way, so a sampled-out launch is still fully debuggable on the phone in front of you.
Checking which way this launch went
Section titled “Checking which way this launch went”Open the panel and tap the info icon. The Info screen shows the rate in force and says plainly whether this particular launch was kept. Since the draw happens once at startup, it will not change until you restart the app.
Turning the rate down from the server
Section titled “Turning the rate down from the server”The same rate can be set per project in the dashboard, under project settings. The SDK reads it during the credential check it already makes at startup, so there is no extra request.
It then uses whichever of the two rates is lower. You can therefore turn the volume down from the server without shipping a release, but the server can never make an app send more than its developer agreed to. If the server cannot be reached, the app’s own setting stands.
Because the value is read at startup, a change reaches a given phone the next time that app is launched rather than immediately.
Setting the server rate to 0 is allowed and means record nothing. That is a real setting, not
an unset one, which is why it silences a project completely until you change it back.
Levels
Section titled “Levels”LoggingBehavior( minimumLevel: LogLevel.info, // drops verbose and debug)Cheap and blunt. Everything below the level is discarded before it reaches your console, the panel, the device or the network.
Be aware of what info excludes. Network requests and responses are logged at debug, so
raising the minimum to info switches off HTTP capture along with everything else at that
level. If you want quiet logs but still want to see network calls, keep debug and use sampling
to control the volume instead.
enabledTags is a list of what you keep, not a list of what you drop:
LoggingBehavior( enabledTags: {'payments', 'auth', 'network'}, allowUntagged: false,)There is no way to mute a single noisy tag. Narrowing this list means naming every tag you still
want, and anything you forget stops being captured with no warning. That includes network,
which is the only tag network logs carry, so leaving it out switches off HTTP capture entirely.
The default is {'*'}, a wildcard meaning keep everything. allowUntagged decides what happens
to logs with no tags at all and defaults to true.
Given how easy it is to lose something here, sampling is usually the better instrument.
Caps on the server
Section titled “Caps on the server”Set under Settings, and read live, with no restart:
| Setting | What it does |
|---|---|
| Retention | How long logs are kept before the nightly job deletes them |
| Max upload size | The largest batch the server will accept, applied to the very next upload |
| Daily log cap | How many logs one project may store in a UTC day |
The daily cap is 0 by default, which means no cap. If you do set one it has to be at least
1000, and the count resets at midnight UTC.
When a project is over its cap the server refuses the whole upload with a 429 and tells the
SDK how long to wait. It never accepts part of a batch, because the SDK deletes a batch on
success and has no way to say “I took 300 of your 400”.
What the SDK does when it is told to wait
Section titled “What the SDK does when it is told to wait”A 429 or a 503 is not treated as a failure. The SDK reads the Retry-After header and goes
quiet for that long, resuming on the next scheduled sync after the time is up. It understands
both forms the header can take, a number of seconds and an HTTP date.
Whatever the header says, the wait is clamped to between one second and 24 hours. The floor stops a server answering “retry in 0 seconds” from turning into a busy loop, and the ceiling means a misconfigured proxy cannot park an app indefinitely.
Because this is not counted as a failure, a server protecting itself can never trip the SDK’s own backoff.
That backoff is separate and applies to real failures. Five in a row make the SDK wait five minutes and then try again by itself. It pauses, it does not stop, so a server that was down for a minute does not cost you every log until somebody restarts the app.
Throughout all of this, nothing is deleted on the phone. Logs are marked as syncing, uploaded, and only then deleted, and any failure rolls them back. Waiting costs you delay, not data.
Finding out a project has hit its cap
Section titled “Finding out a project has hit its cap”The Info screen in the app shows the upload state, including that uploads are paused and how long is left. On the dashboard, the project keeps working and simply stops gaining new logs until the day rolls over.
A reasonable production setting
Section titled “A reasonable production setting”CodeScoutConfiguration( logging: LoggingBehavior( minimumLevel: LogLevel.debug, // keep network capture sessionSampleRate: 0.1, // one launch in ten ), redaction: RedactionBehavior.recommended(), sync: LogSyncBehavior(syncInterval: Duration(minutes: 5)),)Then set a retention window and, if you have a lot of users, a daily cap on the server. Leave the level alone and turn the sample rate down if it is still too much, because that keeps whole sessions rather than fragments of every session.