Skip to content

Commit 0b0f527

Browse files
authored
✨ Introduce DioExceptionReadableStringBuilder (#2297)
Resolves #1949 Resolves #2287 Developers may want the URL of the exception to know what endpoint has gone wrong, or they want to have fewer tips about what the status code means. If we open too many flags to support various logging content, it will be a mess eventually. Introducing the `DioExceptionReadableStringBuilder` easily builds text content from the `DioException`. --------- Signed-off-by: Alex Li <github@alexv525.com>
1 parent 518aa6f commit 0b0f527

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

dio/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ See the [Migration Guide][] for the complete breaking changes list.**
1010
- Fixes boundary inconsistency in `FormData.clone()`.
1111
- Support `FileAccessMode` in `Dio.download` and `Dio.downloadUri` to change download file opening mode
1212
- Fix `ListParam` equality by using the `DeepCollectionEquality`.
13+
- Enables configuring the logging details of `DioException` globally and locally.
1314

1415
## 5.7.0
1516

dio/lib/src/dio_exception.dart

+31-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'options.dart';
22
import 'response.dart';
3+
import 'utils.dart' show warningLog;
34

45
/// Deprecated in favor of [DioExceptionType] and will be removed in future major versions.
56
@Deprecated('Use DioExceptionType instead. This will be removed in 6.0.0')
@@ -205,6 +206,14 @@ class DioException implements Exception {
205206
/// The error message that throws a [DioException].
206207
final String? message;
207208

209+
/// Users can customize the content of [toString] when thrown.
210+
static DioExceptionReadableStringBuilder readableStringBuilder =
211+
defaultDioExceptionReadableStringBuilder;
212+
213+
/// Each exception can be override with a customized builder or fallback to
214+
/// the default [DioException.readableStringBuilder].
215+
DioExceptionReadableStringBuilder? stringBuilder;
216+
208217
/// Generate a new [DioException] by combining given values and original values.
209218
DioException copyWith({
210219
RequestOptions? requestOptions,
@@ -226,11 +235,12 @@ class DioException implements Exception {
226235

227236
@override
228237
String toString() {
229-
String msg = 'DioException [${type.toPrettyDescription()}]: $message';
230-
if (error != null) {
231-
msg += '\nError: $error';
238+
try {
239+
return stringBuilder?.call(this) ?? readableStringBuilder(this);
240+
} catch (e, s) {
241+
warningLog(e, s);
242+
return defaultDioExceptionReadableStringBuilder(this);
232243
}
233-
return msg;
234244
}
235245

236246
/// Because of [ValidateStatus] we need to consider all status codes when
@@ -278,3 +288,20 @@ class DioException implements Exception {
278288
return buffer.toString();
279289
}
280290
}
291+
292+
/// The readable string builder's signature of
293+
/// [DioException.readableStringBuilder].
294+
typedef DioExceptionReadableStringBuilder = String Function(DioException e);
295+
296+
/// The default implementation of building a readable string of [DioException].
297+
String defaultDioExceptionReadableStringBuilder(DioException e) {
298+
final buffer = StringBuffer(
299+
'DioException [${e.type.toPrettyDescription()}]: '
300+
'${e.message}',
301+
);
302+
if (e.error != null) {
303+
buffer.writeln();
304+
buffer.write('Error: ${e.error}');
305+
}
306+
return buffer.toString();
307+
}

dio/lib/src/utils.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ Map<String, V> caseInsensitiveKeyMap<V>([Map<String, V>? value]) {
154154
}
155155

156156
// TODO(Alex): Provide a configurable property on the Dio class once https://github.com/cfug/dio/discussions/1982 has made some progress.
157-
void warningLog(String message, StackTrace stackTrace) {
157+
void warningLog(Object message, StackTrace stackTrace) {
158158
if (!kReleaseMode) {
159159
dev.log(
160-
message,
160+
message.toString(),
161161
level: 900,
162162
name: '🔔 Dio',
163163
stackTrace: stackTrace,

dio/test/exception_test.dart

+30
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,34 @@ void main() {
5252
},
5353
testOn: '!browser',
5454
);
55+
56+
test('DioExceptionReadableStringBuilder', () {
57+
final requestOptions = RequestOptions(path: 'just/a/test', method: 'POST');
58+
final exception = DioException(
59+
requestOptions: requestOptions,
60+
response: Response(requestOptions: requestOptions),
61+
error: 'test',
62+
message: 'test message',
63+
stackTrace: StackTrace.current,
64+
);
65+
DioException.readableStringBuilder = (e) => 'Hey, Dio throws an exception: '
66+
'${e.requestOptions.path}, '
67+
'${e.requestOptions.method}, '
68+
'${e.type}, '
69+
'${e.error}, '
70+
'${e.stackTrace}, '
71+
'${e.message}';
72+
expect(
73+
exception.toString(),
74+
'Hey, Dio throws an exception: '
75+
'just/a/test, '
76+
'POST, '
77+
'DioExceptionType.unknown, '
78+
'test, '
79+
'${exception.stackTrace}, '
80+
'test message',
81+
);
82+
exception.stringBuilder = (e) => 'Locally override: ${e.message}';
83+
expect(exception.toString(), 'Locally override: test message');
84+
});
5585
}

0 commit comments

Comments
 (0)