diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/logging/FlutterLog.kt b/android/app/src/main/kotlin/com/yubico/authenticator/logging/FlutterLog.kt index 303d56d2..b33348e7 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/logging/FlutterLog.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/logging/FlutterLog.kt @@ -5,8 +5,13 @@ import io.flutter.plugin.common.MethodChannel class FlutterLog(messenger: BinaryMessenger) { + private val _buffer = arrayListOf() private var _channel = MethodChannel(messenger, "android.log.redirect") + companion object { + const val MAX_BUFFER_SIZE = 1000 + } + init { _channel.setMethodCallHandler { call, result -> @@ -21,7 +26,7 @@ class FlutterLog(messenger: BinaryMessenger) { if (level == null) { loggerError("Invalid level for message from [$loggerName]: $levelValue") } else if (loggerName != null && message != null) { - Log.log(level, loggerName, message, error) + log(level, loggerName, message, error) result.success(null) } else { result.error("-1", "Invalid log parameters", null) @@ -35,6 +40,13 @@ class FlutterLog(messenger: BinaryMessenger) { } else { loggerError("Invalid log level requested: $levelArgValue") } + result.success(null) + } + "getLogs" -> { + result.success(_buffer) + } + else -> { + result.notImplemented() } } } @@ -44,6 +56,14 @@ class FlutterLog(messenger: BinaryMessenger) { Log.LogLevel.values().firstOrNull { it.name == argValue?.uppercase() } private fun loggerError(message: String) { - Log.e("FlutterLog", message, null) + log(Log.LogLevel.ERROR,"FlutterLog", message, null) + } + + private fun log(level: Log.LogLevel, loggerName: String, message: String, error: String?) { + if (_buffer.size > MAX_BUFFER_SIZE) { + _buffer.removeAt(0) + } + + _buffer.addAll(Log.log(level, loggerName, message, error)) } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/logging/Log.kt b/android/app/src/main/kotlin/com/yubico/authenticator/logging/Log.kt index b12bea90..fc25d59e 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/logging/Log.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/logging/Log.kt @@ -47,12 +47,16 @@ object Log { } @Suppress("unused") - fun log(level: LogLevel, loggerName: String, message: String, error: String?) { + fun log(level: LogLevel, loggerName: String, message: String, error: String?) : List { if (level < this.level) { - return + return listOf() } - val logMessage = "[$loggerName] ${level.name}: $message" + val lines = mutableListOf() + + val logMessage = "[$loggerName] ${level.name}: $message".also { + lines.add(it) + } when (level) { LogLevel.TRAFFIC -> Log.v(TAG, logMessage) @@ -63,8 +67,12 @@ object Log { } error?.let { - Log.e(TAG, "[$loggerName] ${level.name}: $error") + Log.e(TAG, "[$loggerName] ${level.name}: $error".also { + lines.add(it) + }) } + + return lines } @Suppress("unused") diff --git a/lib/about_page.dart b/lib/about_page.dart index 19f9bd50..e6f1a896 100755 --- a/lib/about_page.dart +++ b/lib/about_page.dart @@ -83,9 +83,8 @@ class LoggingPanel extends ConsumerWidget { child: const Text('Copy log'), onPressed: () async { _log.info('Copying log to clipboard...'); - final logs = - ref.read(logLevelProvider.notifier).getLogs().join('\n'); - await Clipboard.setData(ClipboardData(text: logs)); + final logs = await ref.read(logLevelProvider.notifier).getLogs(); + await Clipboard.setData(ClipboardData(text: logs.join('\n'))); showMessage(context, 'Log copied to clipboard'); }, ), diff --git a/lib/android/init.dart b/lib/android/init.dart index 64efc3d5..7b2118b2 100644 --- a/lib/android/init.dart +++ b/lib/android/init.dart @@ -23,8 +23,6 @@ import 'qr_scanner/qr_scanner_provider.dart'; import 'state.dart'; import 'views/tap_request_dialog.dart'; -final androidLogger = AndroidLogger(); - Future initialize() async { if (kDebugMode) { Logger.root.level = Levels.DEBUG; @@ -36,6 +34,7 @@ Future initialize() async { Application.oath, ]), prefProvider.overrideWithValue(await SharedPreferences.getInstance()), + logLevelProvider.overrideWithProvider(androidLogProvider), attachedDevicesProvider .overrideWithProvider(androidAttachedDevicesProvider), currentDeviceDataProvider.overrideWithProvider(androidDeviceDataProvider), @@ -56,14 +55,6 @@ Future initialize() async { // activates window state provider ref.read(androidWindowStateProvider); - ref.listen(logLevelProvider, (oldLevel, newLevel) { - if (oldLevel != newLevel && newLevel is Level) { - androidLogger.setLogLevel(newLevel); - } - }); - - androidLogger.setLogLevel(Logger.root.level); - /// initializes global handler for dialogs FDialogApi.setup(FDialogApiImpl(ref.watch(withContextProvider))); return const MainPage(); diff --git a/lib/android/logger.dart b/lib/android/logger.dart index 97545ff1..9028af08 100644 --- a/lib/android/logger.dart +++ b/lib/android/logger.dart @@ -1,12 +1,17 @@ import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logging/logging.dart'; +import 'package:yubico_authenticator/app/logging.dart'; final _log = Logger('android.logger'); -class AndroidLogger { +final androidLogProvider = + StateNotifierProvider((ref) => AndroidLogger()); + +class AndroidLogger extends LogLevelNotifier { final MethodChannel _channel = const MethodChannel('android.log.redirect'); - AndroidLogger() { + AndroidLogger() : super() { Logger.root.onRecord.listen((record) { if (record.level >= Logger.root.level) { log(record); @@ -15,12 +20,21 @@ class AndroidLogger { _log.info('Logging initialized, outputting to Android/logcat'); } + @override void setLogLevel(Level level) { + super.setLogLevel(level); _channel.invokeMethod('setLevel', { 'level': level.name, }); } + @override + Future> getLogs() async { + _log.debug('Getting logs...'); + var buffer = await _channel.invokeMethod('getLogs', {}); + return List.unmodifiable(buffer); + } + void log(LogRecord record) { _channel.invokeMethod('log', { 'loggerName': record.loggerName, diff --git a/lib/app/logging.dart b/lib/app/logging.dart index 697e1440..fd67b6be 100755 --- a/lib/app/logging.dart +++ b/lib/app/logging.dart @@ -60,7 +60,7 @@ class LogLevelNotifier extends StateNotifier { Logger.root.level = level; } - List getLogs() { + Future> getLogs() async { return List.unmodifiable(_buffer); } }