debugPrint and the power of hiding and customizing your logs in Dart

Photo by Aaron Burden on Unsplash

When developing and debugging software, we usually log some kind of messages to assert its behavior. In Dart, we can do this using the print :

This output doesn’t provide us with much information. Besides the printed message and a flutter label, we don’t have an indication of the app name or a timestamp of when the message was printed.

Furthermore, if you use the command flutter logs you will see the output of the print function in all applications you have installed in the phone/emulator. This means that even if the app is in release mode, it will still be printed in the terminal.

This poses two problems:

  • Any print statement can be potentially seen by any user of the app, which can be a problem if we print sensitive information.
  • If we are monitoring your app using flutter logs we won’t be able to distinguish between our apps and other flutter apps installed in your phone.

Let’s tackle the first problem.

Hiding Print Statements in a Production App

In our apps, we can use the concept of Flavors, which can be used to create different version of our application. This can be useful if our API uses different environments for development and production or if we want to specify a different behavior for our app, as for example hiding the logs in the production app.

To be able to modify the behavior of our print function, we can take a look at the debugPrint method from the foundation library. From the documentation we can read:

The implementation of this function can be replaced by setting the debugPrint variable to a new implementation that matches the DebugPrintCallback signature. For example, flutter_test does this.
The default value is debugPrintThrottled. For a version that acts identically but does not throttle, use debugPrintSynchronously.

So, by overriding the DebugPrintCallback we can change the behaviour of our printed statements. Before changing this implementation, we need to make sure that every printstatement in our project gets replaced by a debugPrint statement

So, assuming we are using different main.dart files: main.dart for production and main_dev.dart for development, we can set a specific callback for each flavor.

Now, when the debugPrint statements in our code are called, they won’t be printed to the console since we gave this function an empty callback.

Creating Custom Print Statements

With this information, we can now look into ways to manipulate the string we use in debugPrint to provide more information when logging. But first, we must look into the documentation of debugPrint again.

The default value is debugPrintThrottled. For a version that acts identically but does not throttle, use debugPrintSynchronously.

As we can see, we have two default callbacks to this function: debugPrintThrottled and debugPrintSynchronously.

Let’s review debugPrintThrottled

Implementation of debugPrint that throttles messages. This avoids dropping messages on platforms that rate-limit their logging (for example, Android).

Though this seems reasonable, we are warned in the debugPrint’s documentation that this method can lead to out-of-order messages in the log if used also with print:

By default, this function very crudely attempts to throttle the rate at which messages are sent to avoid data loss on Android. This means that interleaving calls to this function (directly or indirectly via, e.g., debugDumpRenderTree or debugDumpApp) and to the Dart print method can result in out-of-order messages in the logs.

Alternatively, debugPrintSynchronously has a more straightforward implementation:

Alternative implementation of debugPrint that does not throttle. Used by tests.

So basically, if we want to avoid the printing limit of each OS, we can use debugPrintThrottled, otherwise we can change our implementation to debugPrintSynchronously.

To provide more information about the application being debugged, we can use package_info in order to retrieve the package name, version and build_number. With this, we can now customise our debugPrint statements:

Which will print each message with the following prefix:


And we’re done! 🕺 Now we can fully customize our console logs and hide them from prying eyes.

To further customize your logged messages, you can try to format the time that is logged or change from the package name to app name so that the prefix of the messages are reduced. Also, if you want to create different debugPrint methods for errors, information and warnings, you could try to add some colors to your logs using packages such as ansicolor (note: this may not work correctly for iOS devices because of this issue but you could use it for Android emulators and devices!)