added app logger

This commit is contained in:
Schnitzel5 2025-08-26 22:07:49 +02:00
parent 9831ae8cf4
commit f6c2a24af2
20 changed files with 213 additions and 1 deletions

View file

@ -143,6 +143,8 @@
"nsfw_sources_info": "This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app",
"version": "Version",
"check_for_update": "Check for update",
"share_app_logs": "Share app logs",
"no_app_logs": "No log.txt available!",
"n_days_ago": "{days} days ago",
"today": "Today",
"yesterday": "Yesterday",

View file

@ -933,6 +933,18 @@ abstract class AppLocalizations {
/// **'Check for update'**
String get check_for_update;
/// No description provided for @share_app_logs.
///
/// In en, this message translates to:
/// **'Share app logs'**
String get share_app_logs;
/// No description provided for @no_app_logs.
///
/// In en, this message translates to:
/// **'No log.txt available!'**
String get no_app_logs;
/// No description provided for @n_days_ago.
///
/// In en, this message translates to:

View file

@ -434,6 +434,12 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get check_for_update => 'التحقق من التحديثات';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return 'منذ $days أيام';

View file

@ -436,6 +436,12 @@ class AppLocalizationsAs extends AppLocalizations {
@override
String get check_for_update => 'আপডেটৰ বাবে পৰীক্ষা কৰক';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days দিনৰ আগতে';

View file

@ -438,6 +438,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get check_for_update => 'Auf Aktualisierung prüfen';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return 'Vor $days Tagen';

View file

@ -436,6 +436,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get check_for_update => 'Check for update';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days days ago';

View file

@ -440,6 +440,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get check_for_update => 'Buscar actualizaciones';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return 'hace $days días';

View file

@ -442,6 +442,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get check_for_update => 'Rechercher des mises à jour';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return 'Il y a $days jours';

View file

@ -436,6 +436,12 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get check_for_update => 'अपडेट के लिए जांचें';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days दिन पहले';

View file

@ -440,6 +440,12 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get check_for_update => 'Periksa Pembaruan';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days Hari yang Lalu';

View file

@ -440,6 +440,12 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get check_for_update => 'Controlla aggiornamenti';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days giorni fa';

View file

@ -440,6 +440,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get check_for_update => 'Verificar atualização';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days dias atrás';

View file

@ -441,6 +441,12 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get check_for_update => 'Проверить обновления';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days дней назад';

View file

@ -436,6 +436,12 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get check_for_update => 'ตรวจสอบการอัพเดท';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days วันที่แล้ว';

View file

@ -436,6 +436,12 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get check_for_update => 'Güncelleme Kontrol Et';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days gün önce';

View file

@ -428,6 +428,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get check_for_update => '检查更新';
@override
String get share_app_logs => 'Share app logs';
@override
String get no_app_logs => 'No log.txt available!';
@override
String n_days_ago(Object days) {
return '$days天前';

View file

@ -30,6 +30,7 @@ import 'package:mangayomi/l10n/generated/app_localizations.dart';
import 'package:mangayomi/services/http/m_client.dart';
import 'package:mangayomi/src/rust/frb_generated.dart';
import 'package:mangayomi/utils/discord_rpc.dart';
import 'package:mangayomi/utils/log/logger.dart';
import 'package:mangayomi/utils/url_protocol/api.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart';
import 'package:mangayomi/modules/library/providers/file_scanner.dart';
@ -66,6 +67,7 @@ void main(List<String> args) async {
);
}
}
await AppLogger.init();
isar = await StorageProvider().initDB(null, inspector: kDebugMode);
await Hive.initFlutter();
Hive.registerAdapter(TrackSearchAdapter());
@ -144,6 +146,7 @@ class _MyAppState extends ConsumerState<MyApp> {
void dispose() {
_linkSubscription?.cancel();
discordRpc?.destroy();
AppLogger.dispose();
super.dispose();
}

View file

@ -1,12 +1,19 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/more/about/providers/check_for_update.dart';
import 'package:mangayomi/modules/more/about/providers/get_package_info.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:path/path.dart' as path;
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
class AboutScreen extends ConsumerWidget {
@ -73,6 +80,28 @@ class AboutScreen extends ConsumerWidget {
},
title: Text(l10n.check_for_update),
),
ListTile(
onTap: () async {
final storage = StorageProvider();
final directory = await storage.getDefaultDirectory();
final file = File(
path.join(directory!.path, 'logs.txt'),
);
if (await file.exists()) {
if (Platform.isLinux) {
await Clipboard.setData(
ClipboardData(text: file.path),
);
}
Share.shareXFiles([
XFile(file.path),
], text: "log.txt");
} else {
botToast(l10n.no_app_logs);
}
},
title: Text(l10n.share_app_logs),
),
// ListTile(
// onTap: () {},
// title: const Text("What's news"),

View file

@ -13,6 +13,7 @@ import 'package:mangayomi/services/download_manager/m3u8/models/download.dart';
import 'package:mangayomi/services/download_manager/m3u8/models/ts_info.dart';
import 'package:mangayomi/src/rust/frb_generated.dart';
import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:mangayomi/utils/log/logger.dart';
import 'package:path/path.dart' as path;
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:convert/convert.dart';
@ -49,6 +50,7 @@ class M3u8Downloader {
if (kDebugMode) {
log('[M3u8Downloader] $message');
}
AppLogger.log(message);
}
void close() {
@ -151,11 +153,11 @@ class M3u8Downloader {
continue;
}
_log('Downloading subtitle file: ${element.label}');
subtitleFile.createSync(recursive: true);
if (element.file == null || element.file!.trim().isEmpty) {
_log('Warning: No subtitle file: ${element.label}');
continue;
}
subtitleFile.createSync(recursive: true);
if (element.file!.startsWith("http")) {
final response = await _withRetry(
() =>
@ -168,10 +170,13 @@ class M3u8Downloader {
_log('Subtitle file downloaded: ${element.label}');
await subtitleFile.writeAsBytes(response.bodyBytes);
} else {
_log('Subtitle file written: ${element.label}');
await subtitleFile.writeAsString(element.file!);
}
}
} catch (e) {
AppLogger.log("Download failed", logLevel: LogLevel.error);
AppLogger.log(e.toString(), logLevel: LogLevel.error);
throw M3u8DownloaderException('Download failed', e);
} finally {
close();

77
lib/utils/log/logger.dart Normal file
View file

@ -0,0 +1,77 @@
import 'dart:async';
import 'dart:io';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:path/path.dart' as path;
class AppLogger {
static final _logQueue = StreamController<String>();
static late File _logFile;
static late IOSink _sink;
static bool _initialized = false;
/// Initialize the logger
static Future<void> init() async {
final storage = StorageProvider();
final directory = await storage.getDefaultDirectory();
_logFile = File(path.join(directory!.path, 'logs.txt'));
if (await _logFile.exists() && await _logFile.length() > 100 * 1024) {
await _logFile.delete();
}
if (!await _logFile.exists()) {
await _logFile.create(recursive: true);
}
_sink = _logFile.openWrite(mode: FileMode.append);
_initialized = true;
_logQueue.stream.listen((log) {
_sink.writeln(log);
});
log('\n\nLogger initialized\n\n');
}
static void log(String message, {LogLevel logLevel = LogLevel.info}) {
if (!_initialized) return;
final now = DateTime.now();
final timestamp =
'${now.day.toString().padLeft(2, '0')}/${now.month.toString().padLeft(2, '0')}/${now.year.toString().padLeft(4, '0')} '
'${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}';
final logMessage = '[$timestamp][${logLevel.toString()}] $message';
_logQueue.add(logMessage);
}
static Future<void> dispose() async {
if (!_initialized) return;
await _logQueue.close();
await _sink.flush();
await _sink.close();
_initialized = false;
}
}
enum LogLevel {
debug,
info,
warning,
error;
@override
String toString() {
switch (this) {
case LogLevel.debug:
return 'DEBUG';
case LogLevel.info:
return 'INFO';
case LogLevel.warning:
return 'WARNING';
case LogLevel.error:
return 'ERROR';
}
}
}