Skip to content

Commit

Permalink
Add JSON viewer for log data
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasWanke committed Nov 7, 2023
1 parent c7d0c32 commit 991bee8
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 34 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ Displays logs generated by your app. To use it, follow these steps:
));
```

The `error` field can also hold data for non-error logs.
If it stores JSON data (on an object with a `toJson()` method), the data can be inspected using [<kbd>json_view</kbd>](https://pub.dev/packages/json_view).

> By default, this only stores the last 50 logs. You can customize this via the `maximumSize` parameter of `LogCollection`.
>
> Logs are only stored in debug builds.
Expand Down
44 changes: 26 additions & 18 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.17.1"
data_size:
dependency: transitive
description:
Expand All @@ -60,10 +60,10 @@ packages:
dependency: transitive
description:
name: device_info_plus
sha256: "093b02a284b4969bb641a6236bbb8e626e4035c6ec9e30c20b65d505c24b3080"
sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "9.1.0"
device_info_plus_platform_interface:
dependency: transitive
description:
Expand Down Expand Up @@ -139,10 +139,26 @@ packages:
dependency: transitive
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
version: "0.18.1"
version: "0.18.0"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_view:
dependency: transitive
description:
name: json_view
sha256: "905c69f9e69d1eab5406b87ab6c10c3706c04c70c6a4959621bd2b43c2d27374"
url: "https://pub.dev"
source: hosted
version: "0.4.2"
lints:
dependency: transitive
description:
Expand All @@ -163,10 +179,10 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.2.0"
meta:
dependency: transitive
description:
Expand All @@ -179,10 +195,10 @@ packages:
dependency: transitive
description:
name: package_info_plus
sha256: "0351aaba3b267c4962ed73058a5f62a84de7e39670a20e2916a6baff2ffcfbe5"
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "4.2.0"
package_info_plus_platform_interface:
dependency: transitive
description:
Expand Down Expand Up @@ -284,14 +300,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
win32:
dependency: transitive
description:
Expand Down
105 changes: 89 additions & 16 deletions lib/src/helpers/logs/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:implicitly_animated_list/implicitly_animated_list.dart';
import 'package:json_view/json_view.dart';

import '../../debug_helper.dart';
import '../../utils/level_selector.dart';
Expand Down Expand Up @@ -112,6 +113,7 @@ class LogEntryWidget extends StatelessWidget {
color: color,
);

final textStyle = TextStyle(color: color);
final title = Text.rich(
TextSpan(children: [
TextSpan(
Expand All @@ -123,7 +125,7 @@ class LogEntryWidget extends StatelessWidget {
),
TextSpan(text: ' ${log.message}'),
]),
style: TextStyle(color: color),
style: textStyle,
);

if (log.error == null && log.stackTrace == null) {
Expand All @@ -134,7 +136,6 @@ class LogEntryWidget extends StatelessWidget {
);
}

final textStyle = TextStyle(color: color);
return _ExpansionTile(
onLongPress: () async => _copyToClipboard(context),
leading: icon,
Expand All @@ -146,25 +147,21 @@ class LogEntryWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (log.error != null) ...[
_buildSubtitle(context, _errorLabel),
Text(_stringify(log.error as Object), style: textStyle),
],
if (log.error != null && log.stackTrace != null)
_buildSubtitle(context, '$_errorLabel:'),
_buildError(context),
const SizedBox(height: 8),
],
if (log.stackTrace != null) ...[
_buildSubtitle(context, 'Stack Trace:'),
Text(log.stackTrace!.toString(), style: textStyle),
const SizedBox(height: 8),
],
],
),
),
);
}

Widget _buildSubtitle(BuildContext context, String text) {
return Text(text, style: Theme.of(context).textTheme.titleSmall);
}

Color _getTextColor(BuildContext context) {
final theme = context.theme;
final brightness = theme.scaffoldBackgroundColor.estimatedBrightness;
Expand All @@ -181,6 +178,24 @@ class LogEntryWidget extends StatelessWidget {
};
}

Widget _buildSubtitle(BuildContext context, String text) =>
Text(text, style: Theme.of(context).textTheme.titleSmall);
Widget _buildError(BuildContext context) {
final json = _errorToJsonListOrMap();
if (json != null) {
return JsonView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
json: json,
);
}

return Text(
_stringify(log.error as Object),
style: TextStyle(color: _getTextColor(context)),
);
}

Future<void> _copyToClipboard(BuildContext context) async {
final error = log.error == null ? null : _stringify(log.error as Object);
final stackTrace = log.stackTrace?.toString();
Expand Down Expand Up @@ -223,6 +238,67 @@ class LogEntryWidget extends StatelessWidget {
} catch (_) {}
return describeIdentity(object);
}

dynamic _errorToJsonListOrMap() {
bool isJson(Object? object) {
if (object == null ||
object is bool ||
object is num ||
object is String) {
return true;
}
if (object is List) return object.every(isJson);
if (object is Map) {
return object.keys.every((it) => it is String) &&
object.values.every(isJson);
}

try {
(object as dynamic).toJson();
return true;
} catch (_) {}
return false;
}

bool isJsonListOrMap(Object? object) {
if (object is List || object is Map) return isJson(object);

try {
return isJsonListOrMap((object as dynamic).toJson());
} catch (_) {}
return false;
}

if (!isJsonListOrMap(log.error!)) return null;

dynamic toJson(Object? object) {
if (object == null ||
object is bool ||
object is num ||
object is String) {
return object;
}
if (object is List) return object.map(toJson).toList();
if (object is Map) {
final entries = <String, dynamic>{};
for (final entry in object.entries) {
if (entry.key is! String) return null;
entries[entry.key as String] = toJson(entry.value);
}
return entries;
}

try {
return toJson((object as dynamic).toJson());
} catch (_) {}
try {
return '$object';
} catch (_) {}
return describeIdentity(object);
}

return toJson(log.error!);
}
}

class _LogEntryLine extends StatelessWidget {
Expand Down Expand Up @@ -291,12 +367,9 @@ class _ExpansionTileState extends State<_ExpansionTile>
with SingleTickerProviderStateMixin {
static const _kExpand = Duration(milliseconds: 200);

static final Animatable<double> _easeOutTween =
CurveTween(curve: Curves.easeOut);
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0, end: 0.5);
static final _easeOutTween = CurveTween(curve: Curves.easeOut);
static final _easeInTween = CurveTween(curve: Curves.easeIn);
static final _halfTween = Tween<double>(begin: 0, end: 0.5);

final _borderTween = ShapeBorderTween();

Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies:
flutter:
sdk: flutter
implicitly_animated_list: ^2.1.1
json_view: ^0.4.2
meta: ^1.3.0
package_info_plus: '>=3.0.0 <5.0.0'
shake: ^2.0.0
Expand Down

0 comments on commit 991bee8

Please sign in to comment.