import 'dart:async'; import 'dart:io'; import 'dart:isolate'; import 'package:flutter/foundation.dart'; import 'package:logger/logger.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:spotube/utils/platform.dart'; class AppLogger { static late final Logger log; static late final File logFile; static initialize(bool verbose) { log = Logger( level: kDebugMode || (verbose && kReleaseMode) ? Level.all : Level.info, ); getLogsPath().then((value) => logFile = value); } static R? runZoned(R Function() body) { FlutterError.onError = (details) { reportError(details.exception, details.stack ?? StackTrace.current); }; PlatformDispatcher.instance.onError = (error, stackTrace) { reportError(error, stackTrace); return true; }; if (!kIsWeb) { Isolate.current.addErrorListener( RawReceivePort((pair) async { final isolateError = pair as List; reportError( isolateError.first.toString(), isolateError.last, ); }).sendPort, ); } return runZonedGuarded( body, (error, stackTrace) { reportError(error, stackTrace); }, ); } static Future getLogsPath() async { String dir = (await getApplicationDocumentsDirectory()).path; if (kIsAndroid) { dir = (await getExternalStorageDirectory())?.path ?? ""; } if (kIsMacOS) { dir = join((await getLibraryDirectory()).path, "Logs"); } final file = File(join(dir, ".spotube_logs")); if (!await file.exists()) { await file.create(recursive: true); } return file; } static Future reportError( dynamic error, [ StackTrace? stackTrace, message = "", ]) async { log.e(message, error: error, stackTrace: stackTrace); if (kReleaseMode) { await logFile.writeAsString( "[${DateTime.now()}]---------------------\n" "$error\n$stackTrace\n" "----------------------------------------\n", mode: FileMode.writeOnlyAppend, ); } } }