Skip to content

Commit

Permalink
- bump up Dio dependency to version 5.5.0
Browse files Browse the repository at this point in the history
- Feat: add response time to log
- Feat: add enable property to enable/disable logging
- Feat: add filter property to filter log by request options or data
- Feat: add emoji support
- Fix: key not shown for multiline value (#7)
  • Loading branch information
Milad-Akarie committed Jul 21, 2024
1 parent 5c5cb57 commit b490bc8
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 313 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.4.0]
- bump up Dio dependency to version 5.5.0
- Feat: add response time to log
- Feat: add enable property to enable/disable logging
- Feat: add filter property to filter log by request options or data
- Feat: add emoji support
- Fix: key not shown for multiline value (#7)

## [1.3.1]

- Add recommended lints
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Simply add PrettyDioLogger to your dio interceptors.
```Dart
Dio dio = Dio();
dio.interceptors.add(PrettyDioLogger());
// customization
dio.interceptors.add(PrettyDioLogger(
requestHeader: true,
Expand All @@ -20,7 +21,18 @@ dio.interceptors.add(PrettyDioLogger());
responseHeader: false,
error: true,
compact: true,
maxWidth: 90));
maxWidth: 90,
enabled: kDebugMode,
filter: (options, args){
// don't print requests with uris containing '/posts'
if(options.path.contains('/posts')){
return false;
}
// don't print responses with unit8 list data
return !args.isResponse || !args.hasUint8ListData;
}
)
);
```

## How it looks like
Expand Down
4 changes: 4 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
include: package:lints/recommended.yaml
linter:
rules:
- public_member_api_docs
- always_declare_return_types
22 changes: 12 additions & 10 deletions example/main.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import 'package:dio/dio.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:pretty_dio_logger/src/pretty_dio_logger.dart';

void main() async {
final dio = Dio()
..interceptors.add(PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseBody: true,
responseHeader: false,
compact: false,
));

..interceptors.add(
PrettyDioLogger(
requestHeader: true,
requestBody: true,
filter: (options, args) {
// return !options.uri.path.contains('posts');
return !args.isResponse || !args.hasUint8ListData;
},
),
);
try {
await dio.get('http://www.mocky.io/v2/5d7fc75c3300000476f0b557');
await dio.get('https://jsonplaceholder.typicode.com/posts/1');
} catch (e) {
print(e);
}
Expand Down
Binary file modified images/response_log_android_studio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
299 changes: 1 addition & 298 deletions lib/pretty_dio_logger.dart
Original file line number Diff line number Diff line change
@@ -1,298 +1 @@
import 'dart:math' as math;
import 'dart:typed_data';

import 'package:dio/dio.dart';

class PrettyDioLogger extends Interceptor {
/// Print request [Options]
final bool request;

/// Print request header [Options.headers]
final bool requestHeader;

/// Print request data [Options.data]
final bool requestBody;

/// Print [Response.data]
final bool responseBody;

/// Print [Response.headers]
final bool responseHeader;

/// Print error message
final bool error;

/// InitialTab count to logPrint json response
static const int kInitialTab = 1;

/// 1 tab length
static const String tabStep = ' ';

/// Print compact json response
final bool compact;

/// Width size per logPrint
final int maxWidth;

/// Size in which the Uint8List will be splitted
static const int chunkSize = 20;

/// Log printer; defaults logPrint log to console.
/// In flutter, you'd better use debugPrint.
/// you can also write log in a file.
final void Function(Object object) logPrint;

PrettyDioLogger(
{this.request = true,
this.requestHeader = false,
this.requestBody = false,
this.responseHeader = false,
this.responseBody = true,
this.error = true,
this.maxWidth = 90,
this.compact = true,
this.logPrint = print});

@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
if (request) {
_printRequestHeader(options);
}
if (requestHeader) {
_printMapAsTable(options.queryParameters, header: 'Query Parameters');
final requestHeaders = <String, dynamic>{};
requestHeaders.addAll(options.headers);
requestHeaders['contentType'] = options.contentType?.toString();
requestHeaders['responseType'] = options.responseType.toString();
requestHeaders['followRedirects'] = options.followRedirects;
requestHeaders['connectTimeout'] = options.connectTimeout?.toString();
requestHeaders['receiveTimeout'] = options.receiveTimeout?.toString();
_printMapAsTable(requestHeaders, header: 'Headers');
_printMapAsTable(options.extra, header: 'Extras');
}
if (requestBody && options.method != 'GET') {
final dynamic data = options.data;
if (data != null) {
if (data is Map) _printMapAsTable(options.data as Map?, header: 'Body');
if (data is FormData) {
final formDataMap = <String, dynamic>{}
..addEntries(data.fields)
..addEntries(data.files);
_printMapAsTable(formDataMap, header: 'Form data | ${data.boundary}');
} else {
_printBlock(data.toString());
}
}
}
super.onRequest(options, handler);
}

@override
void onError(DioError err, ErrorInterceptorHandler handler) {
if (error) {
if (err.type == DioErrorType.badResponse) {
final uri = err.response?.requestOptions.uri;
_printBoxed(
header:
'DioError ║ Status: ${err.response?.statusCode} ${err.response?.statusMessage}',
text: uri.toString());
if (err.response != null && err.response?.data != null) {
logPrint('╔ ${err.type.toString()}');
_printResponse(err.response!);
}
_printLine('╚');
logPrint('');
} else {
_printBoxed(header: 'DioError ║ ${err.type}', text: err.message);
}
}
super.onError(err, handler);
}

@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
_printResponseHeader(response);
if (responseHeader) {
final responseHeaders = <String, String>{};
response.headers
.forEach((k, list) => responseHeaders[k] = list.toString());
_printMapAsTable(responseHeaders, header: 'Headers');
}

if (responseBody) {
logPrint('╔ Body');
logPrint('║');
_printResponse(response);
logPrint('║');
_printLine('╚');
}
super.onResponse(response, handler);
}

void _printBoxed({String? header, String? text}) {
logPrint('');
logPrint('╔╣ $header');
logPrint('║ $text');
_printLine('╚');
}

void _printResponse(Response response) {
if (response.data != null) {
if (response.data is Map) {
_printPrettyMap(response.data as Map);
} else if (response.data is Uint8List) {
logPrint('║${_indent()}[');
_printUint8List(response.data as Uint8List);
logPrint('║${_indent()}]');
} else if (response.data is List) {
logPrint('║${_indent()}[');
_printList(response.data as List);
logPrint('║${_indent()}]');
} else {
_printBlock(response.data.toString());
}
}
}

void _printResponseHeader(Response response) {
final uri = response.requestOptions.uri;
final method = response.requestOptions.method;
_printBoxed(
header:
'Response ║ $method ║ Status: ${response.statusCode} ${response.statusMessage}',
text: uri.toString());
}

void _printRequestHeader(RequestOptions options) {
final uri = options.uri;
final method = options.method;
_printBoxed(header: 'Request ║ $method ', text: uri.toString());
}

void _printLine([String pre = '', String suf = '╝']) =>
logPrint('$pre${'═' * maxWidth}$suf');

void _printKV(String? key, Object? v) {
final pre = '╟ $key: ';
final msg = v.toString();

if (pre.length + msg.length > maxWidth) {
logPrint(pre);
_printBlock(msg);
} else {
logPrint('$pre$msg');
}
}

void _printBlock(String msg) {
final lines = (msg.length / maxWidth).ceil();
for (var i = 0; i < lines; ++i) {
logPrint((i >= 0 ? '║ ' : '') +
msg.substring(i * maxWidth,
math.min<int>(i * maxWidth + maxWidth, msg.length)));
}
}

String _indent([int tabCount = kInitialTab]) => tabStep * tabCount;

void _printPrettyMap(
Map data, {
int initialTab = kInitialTab,
bool isListItem = false,
bool isLast = false,
}) {
var tabs = initialTab;
final isRoot = tabs == kInitialTab;
final initialIndent = _indent(tabs);
tabs++;

if (isRoot || isListItem) logPrint('║$initialIndent{');

data.keys.toList().asMap().forEach((index, dynamic key) {
final isLast = index == data.length - 1;
dynamic value = data[key];
if (value is String) {
value = '"${value.toString().replaceAll(RegExp(r'([\r\n])+'), " ")}"';
}
if (value is Map) {
if (compact && _canFlattenMap(value)) {
logPrint('║${_indent(tabs)} $key: $value${!isLast ? ',' : ''}');
} else {
logPrint('║${_indent(tabs)} $key: {');
_printPrettyMap(value, initialTab: tabs);
}
} else if (value is List) {
if (compact && _canFlattenList(value)) {
logPrint('║${_indent(tabs)} $key: ${value.toString()}');
} else {
logPrint('║${_indent(tabs)} $key: [');
_printList(value, tabs: tabs);
logPrint('║${_indent(tabs)} ]${isLast ? '' : ','}');
}
} else {
final msg = value.toString().replaceAll('\n', '');
final indent = _indent(tabs);
final linWidth = maxWidth - indent.length;
if (msg.length + indent.length > linWidth) {
final lines = (msg.length / linWidth).ceil();
for (var i = 0; i < lines; ++i) {
final multilineKey = i == 0 ? "$key:" : "";
logPrint(
'║${_indent(tabs)} $multilineKey ${msg.substring(i * linWidth, math.min<int>(i * linWidth + linWidth, msg.length))}');
}
} else {
logPrint('║${_indent(tabs)} $key: $msg${!isLast ? ',' : ''}');
}
}
});

logPrint('║$initialIndent}${isListItem && !isLast ? ',' : ''}');
}

void _printList(List list, {int tabs = kInitialTab}) {
list.asMap().forEach((i, dynamic e) {
final isLast = i == list.length - 1;
if (e is Map) {
if (compact && _canFlattenMap(e)) {
logPrint('║${_indent(tabs)} $e${!isLast ? ',' : ''}');
} else {
_printPrettyMap(e, initialTab: tabs + 1, isListItem: true, isLast: isLast);
}
} else {
logPrint('║${_indent(tabs + 2)} $e${isLast ? '' : ','}');
}
});
}

void _printUint8List(Uint8List list, {int tabs = kInitialTab}) {
var chunks = [];
for (var i = 0; i < list.length; i += chunkSize) {
chunks.add(
list.sublist(
i, i + chunkSize > list.length ? list.length : i + chunkSize),
);
}
for (var element in chunks) {
logPrint('║${_indent(tabs)} ${element.join(", ")}');
}
}

bool _canFlattenMap(Map map) {
return map.values
.where((dynamic val) => val is Map || val is List)
.isEmpty &&
map.toString().length < maxWidth;
}

bool _canFlattenList(List list) {
return list.length < 10 && list.toString().length < maxWidth;
}

void _printMapAsTable(Map? map, {String? header}) {
if (map == null || map.isEmpty) return;
logPrint('╔ $header ');
map.forEach(
(dynamic key, dynamic value) => _printKV(key.toString(), value));
_printLine('╚');
}
}
export 'src/pretty_dio_logger.dart';
Loading

0 comments on commit b490bc8

Please sign in to comment.