fix long log details - #10011
fix long log details#10011khanak0509 wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors how developer log details are processed in the logging controller, moving from string concatenation to structured JSON manipulation via the new computeDeveloperLogDetailsJson helper, and adds corresponding unit tests. The review feedback identifies a performance concern regarding the use of jsonDecode(jsonEncode(...)) for deep copying on the UI thread, suggesting a more efficient copy-on-write approach to avoid UI lag.
| final detailsJson = jsonDecode(jsonEncode(eventJson)) as Map<String, dynamic>; | ||
| final logRecord = detailsJson['logRecord']; | ||
| if (logRecord is Map<String, dynamic>) { | ||
| _applyFullStringToInstanceJson(logRecord['message'], fullMessage); | ||
|
|
||
| if (fullError != null) { | ||
| final errorJson = logRecord['error']; | ||
| if (errorJson is Map<String, dynamic> && | ||
| errorJson['valueAsString'] != null) { | ||
| _applyFullStringToInstanceJson(errorJson, fullError); | ||
| } else { | ||
| logRecord['errorAsString'] = fullError; | ||
| } | ||
| } | ||
|
|
||
| if (fullStackTrace != null) { | ||
| final stackJson = logRecord['stackTrace']; | ||
| if (stackJson is Map<String, dynamic> && | ||
| stackJson['valueAsString'] != null) { | ||
| _applyFullStringToInstanceJson(stackJson, fullStackTrace); | ||
| } else { | ||
| logRecord['stackTraceAsString'] = fullStackTrace; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return jsonEncode(detailsJson); | ||
| } | ||
|
|
||
| void _applyFullStringToInstanceJson(Object? instanceJson, String fullValue) { | ||
| if (instanceJson is! Map) return; | ||
| instanceJson['valueAsString'] = fullValue; | ||
| instanceJson['valueAsStringIsTruncated'] = false; | ||
| instanceJson['length'] = fullValue.length; | ||
| } |
There was a problem hiding this comment.
[CONCERN] Using jsonDecode(jsonEncode(eventJson)) to perform a deep copy of the log event can be a significant performance bottleneck, especially on the UI thread where DevTools runs, and particularly for large log payloads (such as JWTs or large JSON objects).
Instead of a full serialization/deserialization cycle, we can use a shallow copy-on-write approach for only the parts of the map that we actually modify. This avoids unnecessary CPU overhead and garbage collection pressure.
final detailsJson = Map<String, dynamic>.from(eventJson);
final logRecord = detailsJson['logRecord'];
if (logRecord is Map) {
final logRecordCopy = Map<String, dynamic>.from(logRecord);
detailsJson['logRecord'] = logRecordCopy;
logRecordCopy['message'] = _applyFullStringToInstanceJson(
logRecordCopy['message'],
fullMessage,
);
if (fullError != null) {
final errorJson = logRecordCopy['error'];
if (errorJson is Map && errorJson['valueAsString'] != null) {
logRecordCopy['error'] = _applyFullStringToInstanceJson(
errorJson,
fullError,
);
} else {
logRecordCopy['errorAsString'] = fullError;
}
}
if (fullStackTrace != null) {
final stackJson = logRecordCopy['stackTrace'];
if (stackJson is Map && stackJson['valueAsString'] != null) {
logRecordCopy['stackTrace'] = _applyFullStringToInstanceJson(
stackJson,
fullStackTrace,
);
} else {
logRecordCopy['stackTraceAsString'] = fullStackTrace;
}
}
}
return jsonEncode(detailsJson);
}
Object? _applyFullStringToInstanceJson(Object? instanceJson, String fullValue) {
if (instanceJson is! Map) return instanceJson;
return <String, dynamic>{
...instanceJson,
'valueAsString': fullValue,
'valueAsStringIsTruncated': false,
'length': fullValue.length,
};References
- Prioritize logic, performance on the UI thread, and architectural consistency. (link)
Issue #9596
this PR fixes issue #9596 where long
dart:developer.logmessages lost their structured Details view. Short logs showed the full event tree, but long ones (like a JWT) only showed the plain message string.How I fixed it:
basically, when the VM truncates a long message we still fetch the full string, but instead of replacing the details with that plain string we patch it back into the original event JSON. So Details stays structured for both short and long logs.
before --

after --



Pre-launch Checklist
General checklist
///).Issues checklist
contributions-welcomeorgood-first-issuelabel.contributions-welcomeorgood-first-issuelabel. I understand this means my PR might take longer to be reviewed.Tests checklist
AI-tooling checklist
Feature-change checklist
release-notes-not-requiredlabel or left a comment requesting the label be added.packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md.If you need help, consider asking for help on Discord.