There is some confusion when to use print and debugPrint, so there are some statements which may be false, and which should be clearified:
When using the direct print method then it will bring a lot of a trash into production, will not it?
When using the debugPrint method then it will print only while developing, or it will print also in production?
When I generate a release file (apk), will not it delete all the print calls to optimize the app and reduce the release file size?
and 3. 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.
debugPrint generally is used to avoid the printing limit of each OS, if default debugPrintThrottled callback is used. It will also print in production, but you can customize it to work only in dev mode:
void main() {
if (kReleaseMode) {
debugPrint = (String? message, {int? wrapWidth}) {};
}
}
With this when the debugPrint statements in your code called in production, it won’t be printed to the console since you gave this function an empty callback.
Related
I have deployed my flutter app and one user says that a certain part is not working. Therefore, I am interested in finding a way to write all output that usually gets dumped to the console to a string. I could then send this string to myself and use it to debug the problem.
Is there a way to subscribe to all console output? I found runZoned(() => ...) but this seems to only collect certain logs and especially no logs from other isolates.
In theory, it's possible to capture all log output and then store it as a variable. In practice, it's a lot of work and will likely require elevated privileges to access the log stream from within the app itself (which might not even be possible on iOS without a rooted device).
However, I propose that you flip the equation - instead of retrieving the log output, capture your logs before they make it to the console in the first place.
This is where packages such as logger shine. By routing all of your print statements through a logging service, you can set up middleware that will capture your output on the way through the pipe. This way, you can store all of the output without having to mess with things like OS permissions and privileges.
final buffer = BufferOutput();
final logger = Logger(output: buffer);
class BufferOutput extends LogOutput {
final lines = <String>[];
#override
void output(OutputEvent event) {
lines.addAll(event.lines);
for (var line in event.lines) {
print(line);
}
}
}
Usage:
logger.v("This is verbose text");
logger.d("This is debug text");
logger.i("This is info text");
logger.w("This is warning text");
logger.e("This is error text");
logger.wtf("This is what-the-fudgestickles text");
print(buffer.lines);
// Output:
// ["This is verbose text","This is debug text","This is info text","This is warning text","This is error text","This is what-the-fudgestickles text"]
Note: This will work if you want to capture normal app logging output. If you want to automatically capture exceptional log output, you are better off using something like Crashlytics or Sentry to capture and collate those error logs for you since, depending on the error, you can't depend on your app code to be able to run after the error happens anyway.
This question already has answers here:
Avoid `print` calls in production code. (Documentation)
(6 answers)
Closed 4 months ago.
My code keeps giving me a warning...
Avoid print calls in production code.
How do I resolve it?
Use debugPrint instead. Just know that debugPrint can only take a String? so you might need to use toString() or correctly format the data you are trying to display.
import 'package:flutter/foundation.dart';
debugPrint('This is a debugPrint');
You can use print statement in only debug mode when app run in release mode print statement avoid. Write print using below both method:-
import 'package:flutter/foundation.dart';
if (kDebugMode) {
print("Hello");
}
OR
debugPrint('This is a debugPrint');
Print() is a function which helps you in debugging process, it's not a good idea to use it unconditionally, you may leak some sensitive information in Release version of your app
It's better to make sure that you're using this function in Debug mode
There are some options:
Using debugPrint() instead of print()
debugPrint('some debug info ...')
Using assert() function which will be called in Debug mode only
assert(() {
print('some debug info ...');
return true;
});
Checking Debug Mode before using print()
import 'package:flutter/foundation.dart';
...
if (kDebugMode) {
print('some debug info ...');
}
If you're sure that you have enough control on using print() function, you can ignore that warning by using ignore directive :
Use this on top of the file : // ignore_for_file: avoid_print
Or add this line before print() function
// ignore: avoid_print
print('some debug info ...');
To ignore that warning for the whole project, you can add this lines to analysis_options.yaml file of the project :
include: package:flutter_lints/flutter.yaml
linter:
rules:
avoid_print: false
In my flutter code, I have logic that does this:
final jsonString = await rootBundle.loadString('AssetManifest.json');
And I have tests that I want to return a fake AssetManifest.json when this line is reached.
To mock it, I do this in the test:
ServicesBinding.instance.defaultBinaryMessenger
.setMockMessageHandler('flutter/assets', (message) {
final Uint8List encoded =
utf8.encoder.convert('{"Foo.ttf":["Foo.ttf"]}');
return Future.value(encoded.buffer.asByteData());
});
The weird thing is, this works, but any tests that run after it hang (they all get stuck in the code when it reaches the await rootBundle.loadString('AssetManifest.json') line.
I've tried adding
ServicesBinding.instance.defaultBinaryMessenger
.setMockMessageHandler('flutter/assets', null);
But this doesn't seem to properly "clean up" the mocked behavior. In fact, if I run the above line in my setUp, the first test to run hangs.
So am I mocking the behavior wrong? Or am I not cleaning it up properly?
I ran into the same issue, and believe it's due to caching by the bundle. This will cause the above test to fail, because the message never gets sent. When calling loadString, you can specify whether to cache the result. E.g. loadString('AssetManifest.json', false).
Note that if you use loadStructuredData, implementations can cache the result and you can't tell it not to.
was just wondering if I can determine if my app currently runs in a Testing environment.
Reason is that I am running automated screenshots and want to hide/modify parts of my App only when running that UI Test.
For example I'd like to skip registering for push notifications to avoid that iOS Popup at launch.
I'm searching for something like
if (kTestingMode) { ... }
I know that we do have a driver that basically launches the app and then connects. Guess the App actually does not even know if it is running in Testmode or not. But maybe someone knows an answer.
Thanks!
Some answers aim to detect if you are in debug mode. The question was about how to detect if you are in a test environment, not if you are in debug mode. In fact, when you run a test, you are in debug mode, but you can run an app in debug mode even without running a test.
In order to properly detect if you are running a test, you can check for the presence of the key FLUTTER_TEST in Platform.environment.
import 'dart:io' show Platform;
if (Platform.environment.containsKey('FLUTTER_TEST')) { ... }
Another solutions is to use --dart-define build environment variable. It is available from Flutter 1.17
Example of running tests with --dart-define:
flutter drive --dart-define=testing_mode=true --target=test_driver/main.dart
In code you can check this environment variable with the following code:
const bool.fromEnvironment('testing_mode', defaultValue: false)
Not using const can lead to the variable not being read on mobile, see here.
Okay I just found a solution my myself.
What I did is introduce a global variable which I will set in my main driver.
First I created a new globals.dart file:
library my_prj.globals;
bool testingActive = false;
Then, in my test_driver/main.dart file I import that and set the testingActive variable to true
import '../lib/globals.dart' as globals;
..
void main() {
final DataHandler handler = (_) async {
final response = {};
return Future.value(c.jsonEncode(response));
};
// Enable integration testing with the Flutter Driver extension.
// See https://flutter.io/testing/ for more info.
enableFlutterDriverExtension(handler: handler);
globals.testingActive = true;
WidgetsApp.debugAllowBannerOverride = false; // remove debug banner
runApp(App());
}
Now, I do have this global variable everywhere in my Flutter App by simply importing and checking.
e.g. in my app.dart
import '../globals.dart' as globals;
...
if (globals.testingActive) {
print("We are in a testing environment!");
}
Is there a better solution? Guess this works just fine!
I have another solution for this, may be this would work out as well for you. Let me know if that goes well with you or not.
1. So, I am suggesting to use assert(), as it only runs on debug mode.
Here is an example for navigator:
assert(() {
if (navigator == null && !nullOk) {
throw new FlutterError(
'Error!!!'
);
}
return true;
}());
Note: In particular the () at the end of the call - assert can only operate on a boolean, so just passing in a function doesn't work.
2. Other way is to use kReleaseMode from package package:flutter/foundation.dart
kReleaseMode is a constant. Therefore the compiler is correctly able to remove unused code, and we can safely do:
import 'package:flutter/foundation.dart' as Foundation;
//is release mode
if (Foundation.kReleaseMode) {
print('release mode');
} else {
print('debug mode');
}
3. This is the snippet which will be helpful for you:
bool get isInDebugMode {
bool inDebugMode = false;
assert(inDebugMode = true);
return inDebugMode;
}
If not you can configure your IDE to launch a different main.dart in debug mode where you can set a boolean.
In this oversimplified script I'm doing a GET request with Net::Async::HTTP using IO::Async::Loop::EV:
use Modern::Perl '2017';
use IO::Async::Loop::EV;
use Net::Async::HTTP;
use Future;
my $loop = IO::Async::Loop::EV->new;
my $http = Net::Async::HTTP->new(max_redirects => 0);
$loop->add($http);
my $f = $http->GET('https://www.perl.org')
->then(sub {
my $response = shift;
printf STDERR "got resp code %d\n", $response->code;
return Future->done;
});
$http->adopt_future($f->else_done);
$loop->run;
I get this warning a couple of times:
EV: error in callback (ignoring): Can't call method "sysread" on an undefined value at .../IO/Async/Stream.pm line 974
I get this warning when using IO::Async::Loop::Event too (again in IO::Async::Stream, at line 974).
For non-secure (http) links, however, all looks good. So something's probably wrong with IO::Async::SSL. (I tried this on different machines, with different OS - still getting those warnings)
Why am I getting this warning multiple times? Does it occur on your machines too?
It seems this warning is specific to the IO::Async::Loop::EV implementation. If you just
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
then it appears to work just fine. Unless you're picking that for a specific purpose it's best to avoid it and just let the IO::Async::Loop->new magic constructor find a good one.
Additionally, rather than ending the script with
$loop->run
You could instead use
$f->get
so it performs a blocking wait until that Future is complete but then exits cleanly afterwards, so as not to have to <Ctrl-C> it to abort.
I've raised this as a bug against IO::Async::Loop::EV at https://rt.cpan.org/Ticket/Display.html?id=124030