Created
September 25, 2023 12:04
-
-
Save zs-dima/5ee7419c8d71bae4542a4f8693e4d6c1 to your computer and use it in GitHub Desktop.
ExceptionTrackingManager disscussion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:l/l.dart' as logging; | |
/// Logger instance | |
final Logger logger = AppLogger$L(); | |
/// Possible levels of logging | |
enum LogLevel implements Comparable<LogLevel> { | |
/// Shout level | |
shout._(1200), | |
/// Error level | |
error._(1000), | |
/// Warning level | |
warning._(800), | |
/// Info level | |
info._(600), | |
/// Debug level | |
debug._(400), | |
/// Verbose level | |
verbose._(200); | |
const LogLevel._(this.value); | |
/// Value of the level | |
final int value; | |
@override | |
int compareTo(LogLevel other) => value.compareTo(other.value); | |
@override | |
String toString() => '$LogLevel($value)'; | |
} | |
/// {@template log_options} | |
/// Options for the logger | |
/// {@endtemplate} | |
base class LogOptions { | |
/// {@macro log_options} | |
const LogOptions({ | |
this.showTime = true, | |
this.showEmoji = false, | |
this.logInRelease = false, | |
this.printColors = false, | |
this.level = LogLevel.info, | |
}); | |
/// Log level | |
final LogLevel level; | |
/// Whether to show time | |
final bool showTime; | |
/// Whether to show emoji | |
final bool showEmoji; | |
/// Whether to log in release mode | |
final bool logInRelease; | |
/// Print with colors using ASCII escape codes | |
final bool printColors; | |
} | |
/// {@template log_message} | |
/// Log message | |
/// {@endtemplate} | |
base class LogMessage { | |
/// {@macro log_message} | |
const LogMessage({ | |
required this.message, | |
required this.level, | |
this.hint, | |
this.error, | |
this.stackTrace, | |
this.time, | |
this.data, | |
}); | |
/// Log message | |
final String message; | |
final String? hint; | |
final Object? error; | |
final Object? data; | |
/// Stack trace | |
final StackTrace? stackTrace; | |
/// Time of the log | |
final DateTime? time; | |
/// Log level | |
final LogLevel level; | |
} | |
/// Logger interface | |
abstract base class Logger { | |
/// Logs the error to the console | |
void e( | |
String message, { | |
Object? error, | |
StackTrace? stackTrace, | |
String? hint, | |
Object? data, | |
}); | |
/// Logs the warning to the console | |
void w( | |
String message, { | |
Object? error, | |
StackTrace? stackTrace, | |
String? hint, | |
Object? data, | |
}); | |
/// Logs the info to the console | |
void i( | |
String message, { | |
String? hint, | |
Object? data, | |
}); | |
/// Logs the debug to the console | |
void d(String message); | |
/// Logs the verbose to the console | |
void v(String message); | |
void v2(String message); | |
void v3(String message); | |
void v4(String message); | |
void v5(String message); | |
void v6(String message); | |
/// Set up the logger | |
L runLogging<L>( | |
L Function() fn, [ | |
LogOptions options = const LogOptions(), | |
]); | |
/// Stream of logs | |
Stream<LogMessage> get logs; | |
/// Handy method to log zoneError | |
void logZoneError(Object error, StackTrace stackTrace) { | |
e('Top-level error: $error', stackTrace: stackTrace); | |
} | |
/// Handy method to log [FlutterError] | |
void logFlutterError(FlutterErrorDetails details) { | |
final stackTrace = details.stack ?? StackTrace.current; | |
e('Flutter error: ${details.exceptionAsString()}', stackTrace: stackTrace); | |
} | |
/// Handy method to log [PlatformDispatcher] error | |
bool logPlatformDispatcherError(Object error, StackTrace stackTrace) { | |
e('PlatformDispatcher error: $error', stackTrace: stackTrace); | |
return true; | |
} | |
} | |
/// Default logger using logging package | |
final class AppLogger$L extends Logger { | |
final _log = logging.l; | |
final StreamController<LogMessage> _logController = StreamController<LogMessage>.broadcast(); | |
@override | |
Stream<LogMessage> get logs => _logController.stream; | |
@override | |
L runLogging<L>( | |
L Function() fn, [ | |
LogOptions options = const LogOptions(), | |
]) { | |
if (kReleaseMode && !options.logInRelease) { | |
return fn(); | |
} | |
/// Formats the logger message | |
/// | |
/// Combines emoji, time and message | |
String formatLoggerMessage( | |
Object message, | |
logging.LogLevel level, | |
DateTime time, | |
) { | |
final buffer = StringBuffer(); | |
if (options.showTime) { | |
buffer | |
..write(time.formatted) | |
..write(' ') | |
..writeln(level.splitter) | |
..write(' '); | |
} | |
if (options.showEmoji) { | |
buffer | |
..write(level.emoji) | |
..write(' '); | |
} | |
buffer.writeln(message); | |
return buffer.toString(); | |
} | |
final logOptions = logging.LogOptions( | |
printColors: kDebugMode, | |
handlePrint: true, | |
outputInRelease: options.logInRelease, | |
messageFormatting: formatLoggerMessage, | |
); | |
return _log.capture(fn, logOptions); | |
} | |
@override | |
void e( | |
String message, { | |
Object? error, | |
StackTrace? stackTrace, | |
String? hint, | |
Object? data, | |
}) { | |
_log.e(message, stackTrace); | |
_logController.add( | |
LogMessage( | |
message: message, | |
error: error, | |
stackTrace: stackTrace, | |
level: LogLevel.error, | |
hint: hint, | |
data: data, | |
), | |
); | |
} | |
@override | |
void w( | |
String message, { | |
Object? error, | |
StackTrace? stackTrace, | |
String? hint, | |
Object? data, | |
}) { | |
_log.w(message, stackTrace); | |
_logController.add( | |
LogMessage( | |
message: message, | |
error: error, | |
stackTrace: stackTrace, | |
level: LogLevel.warning, | |
hint: hint, | |
data: data, | |
), | |
); | |
} | |
@override | |
void i( | |
String message, { | |
String? hint, | |
Object? data, | |
}) { | |
_log.i(message); | |
_logController.add( | |
LogMessage( | |
message: message, | |
level: LogLevel.info, | |
hint: hint, | |
data: data, | |
), | |
); | |
} | |
@override | |
void d(String message) => _log.d(message); | |
@override | |
void v(String message) => _log.v(message); | |
@override | |
void v2(String message) => _log.vv(message); | |
@override | |
void v3(String message) => _log.vvv(message); | |
@override | |
void v4(String message) => _log.vvvv(message); | |
@override | |
void v5(String message) => _log.vvvvv(message); | |
@override | |
void v6(String message) => _log.vvvvvv(message); | |
} | |
extension on logging.LogLevel { | |
/// Transforms [logging.LogLevel] to [LogLevel] | |
LogLevel toLogLevel() => maybeWhen( | |
shout: () => LogLevel.shout, | |
error: () => LogLevel.error, | |
warning: () => LogLevel.warning, | |
info: () => LogLevel.info, | |
debug: () => LogLevel.debug, | |
orElse: () => LogLevel.verbose, | |
); | |
/// Transforms [LogLevel] to emoji | |
String get emoji => maybeWhen( | |
shout: () => '❗️', | |
error: () => '🚫', | |
warning: () => '⚠️', | |
info: () => '💡', | |
debug: () => '🐞', | |
orElse: () => '', | |
); | |
String get splitter => kDebugMode | |
? maybeWhen( | |
shout: () => '\x1B[31m|\x1B[0m', | |
error: () => '\x1B[31m|\x1B[0m', | |
warning: () => '\x1B[33m|\x1B[0m', | |
info: () => '\x1B[32m|\x1B[0m', | |
debug: () => '\x1B[36m|\x1B[0m', | |
orElse: () => '|', | |
) | |
: '|'; | |
} | |
extension on LogLevel { | |
/// Transforms [LogLevel] to [logging.LogLevel] | |
logging.LogLevel toLogLevel() => switch (this) { | |
LogLevel.shout => const logging.LogLevel.shout(), | |
LogLevel.error => const logging.LogLevel.error(), | |
LogLevel.warning => const logging.LogLevel.warning(), | |
LogLevel.info => const logging.LogLevel.info(), | |
LogLevel.debug => const logging.LogLevel.debug(), | |
_ => const logging.LogLevel.debug(), | |
}; | |
/// Transforms [LogLevel] to emoji | |
String get emoji => switch (this) { | |
LogLevel.shout => '❗️', | |
LogLevel.error => '🚫', | |
LogLevel.warning => '⚠️', | |
LogLevel.info => '💡', | |
LogLevel.debug => '🐞', | |
_ => '', | |
}; | |
String get splitter => kDebugMode | |
? switch (this) { | |
LogLevel.shout => '\x1B[31m|\x1B[0m', | |
LogLevel.error => '\x1B[31m|\x1B[0m', | |
LogLevel.warning => '\x1B[33m|\x1B[0m', | |
LogLevel.info => '\x1B[32m|\x1B[0m', | |
LogLevel.debug => '\x1B[36m|\x1B[0m', | |
_ => '|', | |
} | |
: '|'; | |
} | |
extension on DateTime { | |
/// Transforms DateTime to String with format: 00:00:00 | |
String get formatted => [ | |
if (!kDebugMode) hour, | |
minute, | |
second, | |
].map((i) => i.toString().padLeft(2, '0')).join(':'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment