Avoid `print` calls in production code. (Documentation) - flutter

I started seeing this warning in all my print statements.
print('Foo'); // Warning: Avoid `print` calls in production code.

It is because of the flutter_lints package which is implicitly added to new projects created after Flutter 2.3.0.
You can use any of the following solutions.
To remove the warning in that single line:
// ignore: avoid_print
print('Hello World');
To remove the warning in that file
// ignore_for_file: avoid_print
print('Hello World');
To remove the warning from the whole project.
Open analysis_options.yaml file and add this linter rule:
include: package:flutter_lints/flutter.yaml
linter:
rules:
avoid_print: false
Why not debugPrint or log?
Although you can also use debugPrint, or log (from dart:developer), but there are a few reasons why I don't like them.
They both work only in Flutter apps (not Dart apps)
You'll have to manually import a library for them to work (importing is such a pain unless imports on fly is implemented in IDE)
They accept only a String as an argument, unlike print which accepts an Object? (everything)

Developers shouldn't suppress such analyzer warnings as well as not use print() for apps they are building. print() will print logs in release builds as well, which is something that shouldn't happen as some developers might log sensitive information.
The suggested by other people debugPrint() is just a wrapper for print() to avoid losing some logs on Android in case the print() function called too often or the output there is too big.
What should be used instead is log() function available in dart.developer, which allows to also attach an error object, severity level, name, etc. of the logged event and won't print anything in release mode, so no information will leak.
Here is more information about proper logging approach, which also describes log() function: https://docs.flutter.dev/testing/code-debugging#logging

If you're printing a lot of logs for debugging, it's better to use debugPrint(String) as mentioned in this answer. Printing numerous amount of lines could sometimes lead the Android kernel to drop some of the lines if you're hitting the limit.
If using print(String) is really needed for your use-case, you're right on adding the lint check for avoid_print since it seems to be set to true by default.

other answers helps to avoid the warning, but why this warning appear and how to fix it?
using print method make your logs available to users when using flutter logs command, so if you log any sensitive data that would be dangerous,
does debugPrint solve this issue and print only in debug mode?
the answer is no, not by default, but the good point with debugPrint that you can override its behavior in a way that makes it not printing in release mode
void main() {
if (kReleaseMode) {
debugPrint = (String message, { int wrapWidth }) {} //
}
}

Null safe working version in 2022:
import 'package:flutter/foundation.dart';
void main() async {
if (kReleaseMode) {
debugPrint = (String? message, {int? wrapWidth}) => '';
}

Replace all print( to logPrint(
Create a new Function
logPrint(String s){print(s);}
And you can remark all the print commands in one line

Related

Why does Dart have so many silent runtime exceptions/errors?

I have been getting very frustrated with Dart as runtime exceptions seem to fail silently. Program execution will continue after some type of failure and leave me totally stumped about what is not working and why. For example, in using aqueduct for dart server (this is not an issue unique to aqueduct, but dart in general) I have this code:
jsonData.forEach((jsonObject) async {
ManagedObject record = Document();
record.read(jsonObject);
print(Util.jsonFormatToString(record.asMap()));
....more code
}
In this case, the application fails silently on record.read() falls out of the forEach loop and then continues code execution. During debugging, the application returns completely strange results and after debugging, presumably there is a problem with the jsonObject attempting to be read into the managed object, but Dart gives no notice that there is a problem or what that problem might be.
This is one example of many I have been running into.
Why does Dart fail so silently or am I missing some setting somewhere that is hiding critical info? I am using IntelliJ IDE.

WWW::Mechanize::Chrome How to close a tab

I can't seem to figure out how to close a tab when using WWW::Mechanize::Chrome.
my $mech = WWW::Mechanize::Chrome->new();
$mech->get('https://www.google.com/');
There is no $mech->close_tab() method.
So the question is, how do you close the tab you're currently working with?
As suggested by #zdim:
my $driver = $mech->driver
Seems to return the Chrome::DevToolsProtocol instance.
Then in order to close the tab, you have to call the close_tab() function and pass the current tab as the argument:
$mech->driver->close_tab($mech->tab);
This approach is generating the following error but it does close the tab:
Calling ->catch in void context at C:/Perl64/site/lib/Chrome/DevToolsProtocol.pm line 567
There is a close_tab method in Chrome::DevToolsProtocol, which is an
asynchronous dispatcher for the DevTools protocol
and does not offer a method to instantiate an object.
The WWW::Mechanize::Chrome has the method driver documented as
Access the Chrome::DevToolsProtocol instance connecting to Chrome.
In a lucky case that this is simple perhaps $chrome->driver->close_tab may do it.
There is also an option driver for its constructor, described as
A premade Chrome::DevToolsProtocol object.
with no further details. Update: using this requires more work.
I cannot yet pursue these clues since the install stubbornly fails at the end, after 54 (fifty-four) distributions are installed.
Hopefully this helps someone to figure it out though.
Update After forcing the install I can confirm the question update
use warnings;
use strict;
...
my $chrome = WWW::Mechanize::Chrome->new(tab => undef); # new tab for demo
$chrome->get('https://www.google.com/');
sleep 5;
$chrome->driver->close_tab($chrome->tab);
sleep 5;
This does issue a warning
Calling ->catch in void context at .../Chrome/DevToolsProtocol.pm line 567.
which, judged by the code quoted in the message, seems to be exposing a bug.
There is no ->close_tab method because I didn't find a need for it yet.
The "driver" object is a Chrome::DevToolsProtocol object, which is used to talk more directly to Chrome. Blindly calling random methods on random results of methods is recommended here in the comments but is not a method that seems to be fruitful.
If you're hell-bent on closing a tab instead of letting the $mech object go out of scope, you can call
$mech->driver->close_tab( $mech->tab )->get();
but this will break further stuff you might have done with $mech. ->close_tab returns a Future, so you shouldn't discard it blindly.
If you just want no tab to appear, have you considered the headless mode?
This question was cross-posted at https://perlmonks.org/?node_id=1222776 .

Where Does create_custom_level() Need to Be Declared (log4perl)?

I'm trying to create a custom message level 'alert' (between warn and error) in Perl, but consistently get the error message:
create_custom_level must be called before init or first get_logger() call at /usr/share/perl5/Log/Log4perl/Logger.pm line 705.
My declaration of the custom level looks like this:
use Log::Log4perl qw(get_logger);
use Log::Log4perl::Level;
Log::Log4perl::Logger::create_custom_level("ALERT", "ERROR");
As far as I can tell from the documentation putting this at the top of any file which intends to use the custom level should be enough. So I can't tell what I'm doing wrong. Looking in the file Logger.pm where the error is thrown from shows that logger is being initialized before the custom level is being declared. Does anyone know how this could be happening?
P.S. I assure you creating a custom level is the right choice here, even if it's frowned upon.
EDIT: Question Answered! The top answer was more a guide to debugging, so I wanted to copy my solution from the comment section so that future readers would be more likely to see it.
I found that there were two steps to fixing my problem:
I needed to put create_custom_level in a BEGIN { ... } statement so that it would run at compile time, since it was apparently being beaten by a logger initialization that was being called at compile time.
I realized that putting the same create_custom_level line in both the main script (.pl) and its modules (.pm) is redundant and caused part of my problems. Depending on the order in which you've put your statements that execute at compile time (like 'use' and 'BEGIN'), calling create_custom_level in multiple files could lead to the sequence: 'create custom level', 'initialize logger', 'create custom level', across multiple files. I didn't figure out where the logger was being initialized early, but I was able to circumvent that by just creating my custom level as early as possible (for other inexperienced coders, using the perl debugger can be key in understanding the order in which lines and files are executed). Best to put create_custom_level in the original script or the first module it uses.
Hope this helps someone else!
The code you provided doesn't produce an error.
Perhaps you have some other code later in your script that is evaluated at compile time -- a module loaded in a use statement or some code in a BEGIN { ... } block -- that initializes a Logger.
If it's not obvious where this might be happening, you can use the Perl debugger to find out where the Logger call could be coming from. First, put this line in your file right after the use Log::Log4perl; and use Log::Log4perl::Level; statements:
BEGIN { $DB::single=1 }
This statement will get the debugger to stop at this line during the compile time phase, and allow you to stop at breakpoints during the rest of the compile phase. Then fire up a debugger
$ perl -d the_script.pl
set breakpoints on the critical Log::Log4perl functions
DB<1> b Log::Log4perl::init
DB<2> b Log::Log4perl::get_logger
begin executing the code
DB<3> c
and when the code stops, get a stack trace
Log::Log4perl::get_logger(/usr/local/lib/perl5/site_perl/5.18.1/Log/Log4perl.pm:371):
371: my $category;
DB<4> T

perl: function name clash

Let's say, there are two modules is our framework: Handler.pm and Queries.pm
Queries.pm is optional and is being loaded at fastcgi process startup
BEGIN {
&{"check_module_$_"} () foreach (Queries);
}
sub check_module_queries {
...
require Eludia::Content::Queries;
...
}
every module function is loaded in one common namespace
now, there are two functions with same name (setup_page_content) in Handler.pm and Queries.pm
setup_page_content is being called in Handler.pm
It looks like original author suggested that Queries.pm:setup_page_content will be called, whenever Queries.pm is loaded
Sometimes it doesn't happen: traceback (obtained via caller ()) in these cases indicates, that setup_page_content was called from module Handler.pm
I logged %INC just before call and it contains Queries.pm and it full path in these cases
This behaviour is inconsistent and pops like in 2-3% of attempts on production installation, mostly when I send two parallel identical http requests. Due amount of effort to reproduce, I doesn't determine yet, whether it is installation specific.
How it will be decided which version of function with same name will be called?
Is it well-defined behaviour?
There should be a reason, original author wrote the code this way
UPDATE
perl version is v5.10.1 built for x86_64-linux-gnu-thread-multi
UPDATE 2
code: Handler.pm and Queries.pm
Queries.pm loading occurs in check_module_queries (BEGIN {} of Eludia.pm),
loaded for every request using Loader.pm (via use Loader.pm <params>)
I'd like to call Custom.pm:setup_page_content, whenever Custom.pm is loaded
So you'd like to call Custom::setup_page_content when it exists.
if (exists(&Custom::setup_page_content)) {
Custom::setup_page_content(...);
} else {
Handler::setup_page_content(...);
}
Sometimes it doesn't happen.
The total lack of information about this prevents me from commenting. Code? Anything?
Is there a way to 'unload' a module but leave it in %INC?
Loading a module is simply running it. You can't "unrun" code. At best, you can manually undo some of the effects of running it. Whether that includes changing %INC or not is entirely up to you.
When there is a namespace collision, the most recently defined/loaded function will take precedence.
These are not proper modules. These are simply libraries. If you turn them into proper modules with proper name spacing you won't have this issue. You barely need to do anything more than:
package Queries;
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
bless ( $self, $class );
return $self;
}
And you'll be well on your way. Of course your program will need a couple changes to actually access the class functions. You can look at exporting to the functions to possibly save some refactoring time.
I have forked the code at github and initiated a pull request that I hope will address your problem.
The behaviour is perfectly defined: when you call PACKAGENAME::function_name, PACKAGENAME::function_name is called. It cannot be more than one PACKAGENAME::function_name at at time.
It is possible to redefine each function so many times as you wish. When you, say, execute some code like
eval "sub PACKAGENAME::function_name {...}";
the function is [re]defined. When you require some .pm file, it's much like eval the string with its content.
If PACKAGENAME is not defined explicitly, the __PACKAGE__ value is used.
So if you remark that the version of setup_page_content is taken from Handler.pm, it must indicate that Handler.pm's content was executed (by use, require, read/eval etc.) after Queries.pm was loaded (for the last time).
So you have to track all events of Handler.pm loading. The easy way is to add some debug print at the start of Handler.pm.

How can I replace all 'die's with 'confess' in a Perl application?

I'm working in a large Perl application and would like to get stack traces every time 'die' is called. I'm aware of the Carp module, but I would prefer not to search/replace every instance of 'die' with 'confess'. In addition, I would like full stack traces for errors in Perl modules or the Perl interpreter itself, and obviously I can't change those to use Carp.
So, is there a way for me to modify the 'die' function at runtime so that it behaves like 'confess'? Or, is there a Perl interpreter setting that will throw full stack traces from 'die'?
Use Devel::SimpleTrace or Carp::Always and they'll do what you're asking for without any hard work on your part. They have global effect, which means they can easily be added for just one run on the commandline using e.g. -MDevel::SimpleTrace.
What about setting a __DIE__ signal handler? Something like
$SIG{__DIE__} = sub { Carp::confess #_ };
at the top of your script? See perlvar %SIG for more information.
I usually only want to replace the dies in a bit of code, so I localize the __DIE__ handler:
{
use Carp;
local $SIG{__DIE__} = \&Carp::confess;
....
}
As a development tool this can work, but some modules play tricks with this to get their features to work. Those features may break in odd ways when you override the handler they were expecting. It's not a good practice, but it happens sometimes.
The Error module will convert all dies to Error::Simple objects, which contain a full stacktrace (the constructor parses the "at file... line.." text and creates a stack trace). You can use an arbitrary object (generally subclassed from Error::Simple) to handle errors with the $Error::ObjectifyCallback preference.
This is especially handy if you commonly throw around exceptions of other types to signal other events, as then you just add a handler for Error::Simple (or whatever other class you are using for errors) and have it dump its stacktrace or perform specialized logging depending on the type of error.