yubioath-flutter/lib/app/logging.dart
2022-06-02 09:07:17 +02:00

110 lines
3.1 KiB
Dart
Executable File

// ignore_for_file: constant_identifier_names
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
String _pad(int value, int zeroes) => value.toString().padLeft(zeroes, '0');
extension DateTimeFormat on DateTime {
String get logFormat =>
'${_pad(hour, 2)}:${_pad(minute, 2)}:${_pad(second, 2)}.${_pad(millisecond, 3)}';
}
class Levels {
/// Key for tracing information ([value] = 500).
static const Level TRAFFIC = Level('TRAFFIC', 500);
/// Key for static configuration messages ([value] = 700).
static const Level DEBUG = Level('DEBUG', 700);
/// Key for informational messages ([value] = 800).
static const Level INFO = Level.INFO;
/// Key for potential problems ([value] = 900).
static const Level WARNING = Level.WARNING;
/// Key for serious failures ([value] = 1000).
static const Level ERROR = Level('ERROR', 1000);
static const List<Level> LEVELS = [
TRAFFIC,
DEBUG,
INFO,
WARNING,
ERROR,
];
}
extension LoggerExt on Logger {
void error(Object? message, [Object? error, StackTrace? stackTrace]) =>
log(Levels.ERROR, message, error, stackTrace);
void debug(Object? message, [Object? error, StackTrace? stackTrace]) =>
log(Levels.DEBUG, message, error, stackTrace);
void traffic(Object? message, [Object? error, StackTrace? stackTrace]) =>
log(Levels.TRAFFIC, message, error, stackTrace);
}
final logLevelProvider =
StateNotifierProvider<LogLevelNotifier, Level>((ref) => LogLevelNotifier());
class LogLevelNotifier extends StateNotifier<Level> {
final List<String> _buffer = [];
LogLevelNotifier() : super(Logger.root.level) {
Logger.root.onRecord.listen((record) {
_buffer.add(
'${record.time.logFormat} [${record.loggerName}] ${record.level}: ${record.message}');
if (record.error != null) {
_buffer.add('${record.error}');
}
while (_buffer.length > 1000) {
_buffer.removeAt(0);
}
});
}
void setLogLevel(Level level) {
state = level;
Logger.root.level = level;
}
Future<List<String>> getLogs() async {
return List.unmodifiable(_buffer);
}
}
class LogWarningOverlay extends StatelessWidget {
final Widget child;
const LogWarningOverlay({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
child,
Consumer(builder: (context, ref, _) {
if (ref.watch(logLevelProvider
.select((level) => level.value <= Level.CONFIG.value))) {
return const Align(
alignment: Alignment.bottomCenter,
child: IgnorePointer(
child: Text(
'WARNING: Potentially sensitive data is being logged!',
textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
);
}
return const SizedBox();
}),
],
);
}
}