-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Description
Use case
Our current logging logic is quite convoluted:
if plutil has error {
echoError(error) // use stderr, prints in verbose mode, or when exitError != 0
if not verbose && exitCode == 0 { // condition where echoError is not print
streamOutput(error) // so that we also print non-verbose mode & exitCode == 0
}
}
Why do we write this code?
In xcode_backend.dart
, echoError
writes to stderr
; then in mac.dart
, build project calls _DefaultProcessUtils.run()
, which prints runResult
as trace, which is only printed in verbose mode:
_logger.printTrace(runResult.toString()); |
In addition, when exitCode != 0, mac.dart's _parseIssueInStdout
will print out stderr
:
flutter/packages/flutter_tools/lib/src/ios/mac.dart
Lines 1083 to 1087 in 5483fef
final String? stderr = result.stderr; | |
if (stderr != null && stderr.isNotEmpty) { | |
logger.printStatus('Error output from Xcode build:\n↳'); | |
logger.printStatus(stderr, indent: 4); | |
} |
So with the above, stderr
is only printed in verbose mode or when exitCode != 0. However, xcode_backend.dart wants to always print out that error, so it uses streamOutput
for non-verbose & exitCode == 0.
flutter/packages/flutter_tools/bin/xcode_backend.dart
Lines 132 to 139 in 5483fef
// Stream stderr to the Flutter build process. | |
// When in verbose mode, `echoError` above will show the logs. So only | |
// stream if not in verbose mode to avoid duplicate logs. | |
// Also, only stream if exitCode is 0 since errors are handled separately | |
// by the tool on failure. | |
if (!verbose && exitCode == 0) { | |
streamOutput(errorOutput.toString()); | |
} |
Side Note: we check exitCode
of current process, not result.exitCode
of the subprocess (e.g. plutil). I think this check shouldn't be necessary, because if exitCode is not 0, then the current process is already terminated (xcode_backend never change exitCode without exiting). (TODO: file a separate issue)
Proposal
This is way too convoluted. For example, "verbosity" and "streaming" should have been orthogonal to each other. Why should "streaming" depend on verbosity? Why should "streaming" depend on exitCode?
Consider a more natural logic:
- If we want to print after process is complete, put it in stdout or stderr
- If we want to print immediately when it happens (aka streaming), use
streamOutput
- Verbose mode prints everything. Non-verbose mode only prints errors
(Of course there's also "warning" which should be in stderr
. I won't go into details here.)
For NSBonjour logic, we want to print immediately when it happens, so we would just call streamOutput (and not echoError):
if plutil has error {
streamOutput(error)
}
Optional: split into 2 streams
We could even consider splitting into 2 streams: outStream
and errStream
, so we don't have to rely on "error:" prefix to distinguish them.
You may ask, "isn't it more complicated with 2 streams?". Well, with 1 stream, you still have 2 concepts (out and error), it's just that you re-use the same stream using "error:" prefix.
Having 2 streams is also more consistent with having separate stdout/stderr, so less cognitive overload
Note:
There's actually a logic error in our current 1 stream approach: (TODO: file a separate issue)
xcode_backend.dart uses "error:" prefix, but mac.dart checks whether the text contains "error:". So if a string has "error:" in the middle (rather than prefix), it would be recognized as an error.
An example of such bug would be plutil extract, which has "error" in the middle: Could not extract value, error: No value at that key path or invalid key path
. We don't want to log it as error (since it's one of our normal states), so we didn't put an "error:" prefix. However, it's still printed out because it contains "error:" in the middle. See also: #173879
Of course, we can fix the logic error by just checking startsWith
rather than contains
in mac.dart. However, having 2 streams would have avoided this logic error in the first place.