skip to content

Logging & Breakpoints

print versus throttled debugPrint, structured log() from dart:developer, IDE breakpoints and debugger() over the VM service, plus the network view. Interviewers ask why debugPrint exists.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

5

In Flutter, what is the difference between print() and debugPrint(), and why does debugPrint exist at all?

level: juniorimportance: must knowfreq 55%

answer

  1. Android drops lines under load
  2. a queue drained in chunks
  3. about 12 KB, then a one-second pause
  4. still prints in release
  5. a replaceable function variable

basics

~20 s

print() writes each message straight to the console. debugPrint() is Flutter's throttled wrapper: it queues lines and releases about 12 KB of text, then pauses a second, so Android does not drop log lines. Both still print in release builds.

solid answer

~40 s

`print()` comes from `dart:core` and writes immediately. `debugPrint` is a Flutter function variable in `package:flutter/foundation.dart` whose default, `debugPrintThrottled`, splits the message into lines, queues them, prints roughly **12 * 1024 characters**, then pauses **one second** before continuing. It exists because platforms that rate-limit logging, Android in particular, silently drop lines when an app floods the log, which is exactly what large dumps like `debugDumpApp()` do. It also takes an optional `wrapWidth` for word wrapping. Two things people get wrong: `debugPrint` is **not** removed in release builds, so real logging should sit behind `kDebugMode` or an assert; and because it is buffered, mixing it with `print` can reorder messages. Tests swap in `debugPrintSynchronously`, and the `avoid_print` lint in `flutter_lints` nudges you away from raw `print`.

code

dart · 9 lines
dart
import 'package:flutter/foundation.dart';

void logCart(List<String> items) {
  // kDebugMode is a compile-time constant, so release builds drop this block.
  if (kDebugMode) {
    // Throttled: a long list is released in chunks instead of flooding the log.
    debugPrint('cart (${items.length}): ${items.join(', ')}', wrapWidth: 100);
  }
}

go deeper

for a junior

Remember that debugPrint throttles output so Android does not drop lines, and that it still prints in release unless you guard it with kDebugMode.

for a middle

Explain the mechanism: a queue of lines, about 12 * 1024 characters per burst, a one-second pause, optional wrapWidth, and why that can reorder output relative to print.

for a senior

Show you treat logs as a release concern: guard or silence debugPrint in release, keep personal data out of logs, and replace the debugPrint variable when you need to redirect framework output.

for a principal

Set the team's logging policy: which API for which purpose, what may reach release logs, and lints like avoid_print enforced in CI.

## Two ways to write a line | | `print()` | `debugPrint()` | |---|---|---| | Defined in | `dart:core` | `package:flutter/foundation.dart` | | What it is | a top-level function | a **variable** of type `DebugPrintCallback` | | Default behaviour | writes the message immediately | `debugPrintThrottled`: queue, print in chunks, pause | | Line wrapping | none | optional `wrapWidth` word-wraps each line | | In release builds | prints | **prints** too | | Ordering with the other | immediate | buffered, so it can interleave out of order with `print` | ## Why debugPrint exists Some platforms **rate-limit** their system log. On Android, an app that writes too much too fast loses lines: they are silently dropped. Flutter's own diagnostics produce exactly that kind of burst, since `debugDumpApp()` or `debugDumpRenderTree()` can print thousands of lines in one call. `debugPrintThrottled`, the default implementation, protects against that: 1. It splits the message on newlines (wrapping each line first if `wrapWidth` is given) and appends the lines to a queue. 2. It prints lines until about **12 * 1024 characters** have gone out. 3. If lines remain, it waits **one second** on a `Timer` and continues. 4. When the queue is empty, the `debugPrintDone` future completes. The result is slower but complete output. The source comments admit it is a crude throttle, measured in characters rather than bytes. ## Consequences to know - **Out-of-order logs.** A `print` issued while `debugPrint` lines are still queued can appear before them. - **Release builds still print.** The doc comment says so explicitly and recommends wrapping calls in `if (kDebugMode)` or an `assert`, both of which the compiler removes from release builds. - **It is replaceable.** Because `debugPrint` is a variable, you can assign another `DebugPrintCallback`, for example to redirect framework output in a test or to add a prefix. The widget test binding uses `debugPrintSynchronously`, which prints without throttling so test output is deterministic. - **The lint.** `flutter_lints` enables `avoid_print`, which flags `print` in production code, pushing you towards `debugPrint`, `dart:developer`'s `log()` or a logging package. ## Choosing between them - A quick one-off trace while developing: either works; `debugPrint` is the habit that survives large output. - Large dumps or anything that may burst: `debugPrint`, so Android keeps every line. - Structured logs with a name, severity and attached error: `log()` from `dart:developer`, viewed in the DevTools Logging view. - Anything that must not reach release logs: guard it with `kDebugMode` or `assert`, whichever function you use. ## A common interview trap Candidates often say `debugPrint` "only prints in debug mode". The name suggests it, but the implementation has no mode check at all: in a release build it still queues and prints. That matters for privacy (tokens, emails, payment details in logs) and for noise in a user's device log. The fix is the guard, not the function name.

  • Why can a Flutter debugPrint message appear after a print() call that ran later?
    `debugPrintThrottled` queues lines and drains them in chunks, pausing for a second when about 12 KB of text has gone out. A `print` bypasses that queue and writes at once, so while earlier `debugPrint` lines are still waiting, a later `print` can reach the console first. The source comments warn about exactly this interleaving.
  • How do you stop Flutter's debugPrint from writing anything in a release build?
    Guard the call with `if (kDebugMode)` or put it inside an `assert`, both of which the compiler removes from release builds. Alternatively, assign a no-op `DebugPrintCallback` to the `debugPrint` variable at startup when not in debug mode, which also silences framework calls that use it.

saying these in an interview costs you the question

  • debugPrint is automatically stripped from release builds.
  • print() and debugPrint() are aliases that behave identically.
  • debugPrint exists to add colours and timestamps to console output.
  • debugPrint always preserves ordering relative to print().
  • debugPrint is a final function that cannot be replaced.
open as a page

In Flutter, how do you set breakpoints and step through code with the DevTools debugger, and what does dart:developer's debugger() add?

level: middleimportance: should knowfreq 40%

basics

~20 s

Click the line-number gutter in the DevTools Debugger or your IDE to set a breakpoint, then read the call stack and variables and step in, over or out. dart:developer's debugger(when: condition) is a code-level breakpoint that pauses only when true.

open as a page

In Flutter, what does log() from dart:developer give you over print(), and how do you read its output in DevTools?

level: middleimportance: should knowfreq 40%

basics

~20 s

log() from dart:developer emits a structured event with a name, a severity level (0-2000, default 0) and an optional error and stack trace, sent to attached tools. The DevTools Logging view lists and filters these events.

open as a page

In Flutter, what is the Dart VM service, and what does the DevTools network view record through it?

level: middleimportance: should knowfreq 30%

basics

~20 s

The Dart VM service is the tooling protocol a debug or profile Flutter app exposes; flutter run prints its URL. Through it, the DevTools network view records HTTP, HTTPS and WebSocket traffic from dart:io (dio included) and http_profile clients.

open as a page

A Flutter checkout's payment-success callback sometimes fires twice; how would you trace why with logging, breakpoints and DevTools?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Log each call with a name and sequence number, pause on the second call with a conditional breakpoint or debugger(when:) and compare its call stack with the first, and count requests in the network view. Usually a listener was registered twice.

open as a page