Diagnostic output written with print instead of a logger

ID

dart.maintainability.print_in_production

Severity

info

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

low

Resource

Api Design

Language

Dart

Description

Reports a call to the top-level print function. Calls guarded by kDebugMode or !kReleaseMode, Flutter’s debugPrint, and writes through stdout or a logging abstraction are not reported, and neither are files under test, bin, tool or example, where console output is the program’s own interface rather than a debugging channel.

Rationale

print is the statement typically added while chasing a problem and then left behind, and it gives the application no control over what it wrote: no level, no structure, no correlation identifier, and no way to turn it off short of editing the code. In a released Flutter application the destination makes it worse - output goes to the platform log, where it is retained by the device, readable by anyone with a debug bridge attached, and frequently ends up containing request payloads or user data that were never meant to leave the process. On the web it lands in the browser console, visible to every user. Each call also formats its string and performs a synchronous write even when nothing is listening, which on a hot path costs frames.

The following code illustrates the pattern detected by this rule:

class OrderService {
  Future<void> submit(String reference, num amount) async {
    // FLAGGED: Diagnostic output written with print instead of a logger
    print('submitting $reference');
    try {
      await _post(reference, amount);
    } catch (e) {
      // FLAGGED: Diagnostic output written with print instead of a logger
      print('failed: $e');
      rethrow;
    }
  }

  num amountOf(String reference) => 1;

  Future<void> _post(String reference, num amount) async {}
}

Remediation

Route the message through the application’s logging abstraction (package:logging, or the logger the project already uses) at a level that matches it, and pass the error and stack trace objects rather than their toString(). Delete the call if it was only scaffolding. Where the output really is for a developer, use Flutter’s debugPrint, which is rate-limited and stripped in release builds, or guard the call with if (kDebugMode).

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved