mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 02:01:36 +03:00
Add log buffer and --log-level argument.
This commit is contained in:
parent
a9e1ae5560
commit
8a0896b12f
@ -1,9 +1,12 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'app/logging.dart';
|
||||
import 'app/views/responsive_dialog.dart';
|
||||
import 'core/state.dart';
|
||||
import 'desktop/state.dart';
|
||||
@ -16,36 +19,90 @@ class AboutPage extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return ResponsiveDialog(
|
||||
title: const Text('About Yubico Authenticator'),
|
||||
title: const Text('Yubico Authenticator'),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// TODO: Store the version number elsewhere
|
||||
const Text('Yubico Authenticator version: 6.0.0-alpha.1'),
|
||||
const Text('Yubico Authenticator: 6.0.0-alpha.1'),
|
||||
if (isDesktop)
|
||||
Text('ykman version: ${ref.watch(rpcStateProvider).version}'),
|
||||
Text('Dart version: ${Platform.version}'),
|
||||
const SizedBox(height: 8.0),
|
||||
const Divider(),
|
||||
if (isDesktop)
|
||||
TextButton(
|
||||
const LoggingPanel(),
|
||||
if (isDesktop) ...[
|
||||
const Divider(),
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
_log.info('Running diagnostics...');
|
||||
final response =
|
||||
await ref.read(rpcProvider).command('diagnose', []);
|
||||
_log.info('Response', response['diagnostics']);
|
||||
final data = response['diagnostics'];
|
||||
final text = const JsonEncoder.withIndent(' ').convert(data);
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Diagnostics done. See log for results...'),
|
||||
content: Text('Diagnostic data copied to clipboard'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('Run diagnostics...'),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingPanel extends ConsumerWidget {
|
||||
const LoggingPanel({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Row(
|
||||
children: [
|
||||
const Text('Log level:'),
|
||||
const SizedBox(width: 8.0),
|
||||
DropdownButton<Level>(
|
||||
value: ref.watch(logLevelProvider),
|
||||
isDense: true,
|
||||
items: [Level.WARNING, Level.INFO, Level.CONFIG, Level.FINE]
|
||||
.map((e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(e.name.toUpperCase()),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (level) {
|
||||
ref.read(logLevelProvider.notifier).setLogLevel(level!);
|
||||
_log.config('Log level set to $level');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Log level set to $level'),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
OutlinedButton(
|
||||
child: const Text('Copy log'),
|
||||
onPressed: () async {
|
||||
_log.info('Copying log to clipboard...');
|
||||
final logs = LogBuffer.of(context).getLogs().join('\n');
|
||||
await Clipboard.setData(ClipboardData(text: logs));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Log copied to clipboard'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
46
lib/app/logging.dart
Executable file
46
lib/app/logging.dart
Executable file
@ -0,0 +1,46 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
extension LoggerExt on Logger {
|
||||
void critical(Object? message, [Object? error, StackTrace? stackTrace]) =>
|
||||
shout(message, error, stackTrace);
|
||||
void error(Object? message, [Object? error, StackTrace? stackTrace]) =>
|
||||
severe(message, error, stackTrace);
|
||||
void debug(Object? message, [Object? error, StackTrace? stackTrace]) =>
|
||||
config(message, error, stackTrace);
|
||||
void traffic(Object? message, [Object? error, StackTrace? stackTrace]) =>
|
||||
fine(message, error, stackTrace);
|
||||
}
|
||||
|
||||
List<String> initLogBuffer(int maxSize) {
|
||||
final List<String> _buffer = [];
|
||||
Logger.root.onRecord.listen((record) {
|
||||
_buffer.add('[${record.loggerName}] ${record.level}: ${record.message}');
|
||||
if (record.error != null) {
|
||||
_buffer.add('${record.error}');
|
||||
}
|
||||
while (_buffer.length > maxSize) {
|
||||
_buffer.removeAt(0);
|
||||
}
|
||||
});
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
class LogBuffer extends InheritedWidget {
|
||||
final List<String> _buffer;
|
||||
const LogBuffer(this._buffer, {required Widget child, Key? key})
|
||||
: super(child: child, key: key);
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
|
||||
|
||||
static LogBuffer of(BuildContext context) {
|
||||
final result = context.dependOnInheritedWidgetOfExactType<LogBuffer>();
|
||||
assert(result != null, 'No LogBuffer found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
List<String> getLogs() {
|
||||
return List.unmodifiable(_buffer);
|
||||
}
|
||||
}
|
@ -109,7 +109,7 @@ class MainPageDrawer extends ConsumerWidget {
|
||||
},
|
||||
),
|
||||
DrawerItem(
|
||||
titleText: 'About',
|
||||
titleText: 'Help and feedback',
|
||||
icon: const Icon(Icons.help_outline),
|
||||
onTap: () {
|
||||
final nav = Navigator.of(context);
|
||||
|
@ -40,13 +40,7 @@ class _WindowResizeListener extends WindowListener {
|
||||
}
|
||||
|
||||
Future<Widget> initialize() async {
|
||||
Logger.root.onRecord.listen((record) {
|
||||
stderr.writeln('[${record.loggerName}] ${record.level}: ${record.message}');
|
||||
if (record.error != null) {
|
||||
stderr.writeln(record.error);
|
||||
}
|
||||
});
|
||||
_log.info('Logging initialized, outputting to stderr');
|
||||
_initLogging();
|
||||
|
||||
await windowManager.ensureInitialized();
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@ -119,3 +113,28 @@ Future<Widget> initialize() async {
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _initLogging() {
|
||||
Logger.root.onRecord.listen((record) {
|
||||
stderr.writeln('[${record.loggerName}] ${record.level}: ${record.message}');
|
||||
if (record.error != null) {
|
||||
stderr.writeln(record.error);
|
||||
}
|
||||
});
|
||||
|
||||
final arguments = Platform.executableArguments;
|
||||
final logLevelIndex = arguments.indexOf('--log-level');
|
||||
if (logLevelIndex != -1) {
|
||||
try {
|
||||
final levelName = arguments[logLevelIndex + 1];
|
||||
Level level = Level.LEVELS
|
||||
.firstWhere((level) => level.name == levelName.toUpperCase());
|
||||
Logger.root.level = level;
|
||||
_log.info('Log level initialized from command line argument');
|
||||
} catch (error) {
|
||||
_log.severe('Failed to set log level', error);
|
||||
}
|
||||
}
|
||||
|
||||
_log.info('Logging initialized, outputting to stderr');
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:async/async.dart';
|
||||
import 'package:yubico_authenticator/app/logging.dart';
|
||||
|
||||
import '../app/models.dart';
|
||||
import 'models.dart';
|
||||
@ -46,6 +47,7 @@ class _Request {
|
||||
}
|
||||
|
||||
const _py2level = {
|
||||
'TRAFFIC': Level.FINE,
|
||||
'DEBUG': Level.CONFIG,
|
||||
'INFO': Level.INFO,
|
||||
'WARNING': Level.WARNING,
|
||||
@ -83,7 +85,7 @@ class RpcSession {
|
||||
//time: DateTime.fromMillisecondsSinceEpoch(event['time'] * 1000),
|
||||
);
|
||||
} catch (e) {
|
||||
_log.severe(e.toString(), event);
|
||||
_log.error(e.toString(), event);
|
||||
}
|
||||
});
|
||||
|
||||
@ -122,7 +124,7 @@ class RpcSession {
|
||||
}
|
||||
|
||||
void _send(Map data) {
|
||||
_log.fine('SEND', jsonEncode(data));
|
||||
_log.traffic('SEND', jsonEncode(data));
|
||||
_process.stdin.writeln(jsonEncode(data));
|
||||
}
|
||||
|
||||
@ -137,7 +139,7 @@ class RpcSession {
|
||||
bool completed = false;
|
||||
while (!completed) {
|
||||
final response = await _responses.next;
|
||||
_log.fine('RECV', jsonEncode(response));
|
||||
_log.traffic('RECV', jsonEncode(response));
|
||||
response.map(
|
||||
signal: (signal) {
|
||||
final signaler = request.signal;
|
||||
|
@ -6,6 +6,7 @@ import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'android/init.dart' as android;
|
||||
import 'app/logging.dart';
|
||||
import 'app/app.dart';
|
||||
import 'core/state.dart';
|
||||
import 'desktop/init.dart' as desktop;
|
||||
@ -15,6 +16,7 @@ final _log = Logger('main');
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final logBuffer = initLogBuffer(1000);
|
||||
|
||||
try {
|
||||
final Widget initializedApp;
|
||||
@ -26,7 +28,10 @@ void main() async {
|
||||
_initializeDebugLogging();
|
||||
throw UnimplementedError('Platform not supported');
|
||||
}
|
||||
runApp(initializedApp);
|
||||
runApp(LogBuffer(
|
||||
logBuffer,
|
||||
child: initializedApp,
|
||||
));
|
||||
} catch (e) {
|
||||
_log.warning('Platform initialization failed: $e');
|
||||
runApp(
|
||||
|
@ -4,7 +4,6 @@ import 'package:logging/logging.dart';
|
||||
|
||||
import 'app/state.dart';
|
||||
import 'app/views/responsive_dialog.dart';
|
||||
import 'core/state.dart';
|
||||
|
||||
final _log = Logger('settings');
|
||||
|
||||
@ -31,20 +30,7 @@ class SettingsPage extends ConsumerWidget {
|
||||
.toList(),
|
||||
onChanged: (mode) {
|
||||
ref.read(themeModeProvider.notifier).setThemeMode(mode!);
|
||||
},
|
||||
),
|
||||
DropdownButtonFormField<Level>(
|
||||
decoration: const InputDecoration(labelText: 'Logging'),
|
||||
value: ref.watch(logLevelProvider),
|
||||
items: [Level.INFO, Level.CONFIG, Level.FINE]
|
||||
.map((e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(e.name.toUpperCase()),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (level) {
|
||||
ref.read(logLevelProvider.notifier).setLogLevel(level!);
|
||||
_log.config('Log level set to $level');
|
||||
_log.config('Set theme mode to $mode');
|
||||
},
|
||||
),
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user