Добавлено логирование
This commit is contained in:
64
lib/app.dart
64
lib/app.dart
@@ -18,6 +18,7 @@ import 'package:remever/components/notifiers/app_settings.dart';
|
||||
import 'package:remever/components/listeners/theme_listener.dart';
|
||||
import 'package:remever/i18n/strings.g.dart';
|
||||
import 'package:remever/theme/custom_theme.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
final Completer<GlobalKey<NavigatorState>> navKeyCompleter =
|
||||
Completer<GlobalKey<NavigatorState>>();
|
||||
@@ -142,42 +143,41 @@ class _MyAppState extends State<MyApp>
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
routerConfig: globalRouter.config(),
|
||||
|
||||
routerConfig: globalRouter.config(
|
||||
navigatorObservers:
|
||||
() => <NavigatorObserver>[TalkerRouteObserver(talker)],
|
||||
),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(
|
||||
context,
|
||||
).copyWith(textScaler: TextScaler.noScaling),
|
||||
child: ChangeNotifierProvider<AppSettingsNotifier>(
|
||||
create: (_) => settingsNotifier,
|
||||
builder: (BuildContext context, Widget? nchild) {
|
||||
if (nchild != null) return nchild;
|
||||
return ChangeNotifierProvider<AppSettingsNotifier>(
|
||||
create: (_) => settingsNotifier,
|
||||
builder: (BuildContext context, Widget? nchild) {
|
||||
if (nchild != null) return nchild;
|
||||
|
||||
return Consumer<AppSettingsNotifier>(
|
||||
// TIP: должно убрать мерцание
|
||||
key: const Key('consumer AppSettingsNotifier'),
|
||||
child: child,
|
||||
builder: (
|
||||
BuildContext context,
|
||||
AppSettingsNotifier value,
|
||||
Widget? nchild,
|
||||
) {
|
||||
final Widget result = nchild ?? const SizedBox();
|
||||
return Consumer<AppSettingsNotifier>(
|
||||
// TIP: должно убрать мерцание
|
||||
key: const Key('consumer AppSettingsNotifier'),
|
||||
child: child,
|
||||
builder: (
|
||||
BuildContext context,
|
||||
AppSettingsNotifier value,
|
||||
Widget? nchild,
|
||||
) {
|
||||
final Widget result = nchild ?? const SizedBox();
|
||||
|
||||
if (value.showFps) {
|
||||
return Material(
|
||||
child: FPSWidget(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: result,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (value.showFps) {
|
||||
return Material(
|
||||
child: FPSWidget(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: result,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
return result;
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:remever/common/resources.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
import 'package:remever/router.dart';
|
||||
import 'package:remever/services/logs/logs_service.dart';
|
||||
import 'package:talker/talker.dart';
|
||||
import 'events/events.dart';
|
||||
|
||||
///
|
||||
@@ -17,6 +19,17 @@ AppRouter get globalRouter {
|
||||
return GetIt.I.get<AppRouter>();
|
||||
}
|
||||
|
||||
///
|
||||
/// Логирование
|
||||
///
|
||||
LogsService get logger {
|
||||
return GetIt.I.get<LogsService>();
|
||||
}
|
||||
|
||||
Talker get talker {
|
||||
return GetIt.I.get<Talker>();
|
||||
}
|
||||
|
||||
///
|
||||
/// Показ тоста
|
||||
///
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Package imports:
|
||||
import 'package:curl_logger_dio_interceptor/curl_logger_dio_interceptor.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_smart_retry/dio_smart_retry.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_settings.dart';
|
||||
|
||||
// Project imports:
|
||||
import '../../components/env.dart';
|
||||
@@ -62,15 +63,11 @@ Dio get apiClient {
|
||||
..add(_auth)
|
||||
..add(_error)
|
||||
..add(
|
||||
PrettyDioLogger(
|
||||
request: true,
|
||||
requestBody: true,
|
||||
requestHeader: true,
|
||||
responseBody: true,
|
||||
error: true,
|
||||
TalkerDioLogger(
|
||||
talker: talker,
|
||||
settings: const TalkerDioLoggerSettings(printRequestHeaders: true),
|
||||
),
|
||||
)
|
||||
..add(CurlLoggerDioInterceptor())
|
||||
..add(
|
||||
RetryInterceptor(
|
||||
dio: client,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Package imports:
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/database/tables.dart';
|
||||
import 'package:remever/models/collection_dto.dart';
|
||||
@@ -16,37 +17,55 @@ class CollectionsDao extends DatabaseAccessor<AppDatabase>
|
||||
|
||||
/// Получение коллекций из базы данных
|
||||
Stream<List<Collection>> getCollections() {
|
||||
return db.managers.collections.watch();
|
||||
try {
|
||||
return db.managers.collections.watch();
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе getCollections', e, st);
|
||||
|
||||
throw ('EXEPTION');
|
||||
}
|
||||
}
|
||||
|
||||
/// Создание коллекции
|
||||
Future<void> createCollection(CollectionDto dto) async {
|
||||
await db.managers.collections.create(
|
||||
(o) => o(
|
||||
title: dto.title,
|
||||
desc: dto.desc,
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
try {
|
||||
await db.managers.collections.create(
|
||||
(o) => o(
|
||||
title: dto.title,
|
||||
desc: dto.desc,
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе createCollection', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Обновление коллекции
|
||||
Future<void> updateCollection(CollectionDto dto, String id) async {
|
||||
await db.managers.collections
|
||||
.filter((f) => f.id(id))
|
||||
.update(
|
||||
(o) => o(
|
||||
title: Value<String>(dto.title),
|
||||
desc: Value<String>(dto.desc),
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
try {
|
||||
await db.managers.collections
|
||||
.filter((f) => f.id(id))
|
||||
.update(
|
||||
(o) => o(
|
||||
title: Value<String>(dto.title),
|
||||
desc: Value<String>(dto.desc),
|
||||
isPublic: Value<bool>(dto.isPublic),
|
||||
image: Value<String?>(dto.avatar),
|
||||
),
|
||||
);
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе updateCollection', e, st);
|
||||
}
|
||||
}
|
||||
|
||||
/// Удаление коллекции
|
||||
Future<void> deleteCollection(String id) async {
|
||||
await db.managers.collections.filter((f) => f.id(id)).delete();
|
||||
try {
|
||||
await db.managers.collections.filter((f) => f.id(id)).delete();
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе deleteCollection', e, st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/// Locales: 2
|
||||
/// Strings: 20 (10 per locale)
|
||||
///
|
||||
/// Built on 2025-03-25 at 18:21 UTC
|
||||
/// Built on 2025-04-01 at 17:23 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -20,6 +20,7 @@ import 'services/collection/collections_service.dart' as _i1001;
|
||||
import 'services/core/enc_keys_service.dart' as _i439;
|
||||
import 'services/core/lang_service.dart' as _i68;
|
||||
import 'services/core/theme_service.dart' as _i84;
|
||||
import 'services/logs/logs_service.dart' as _i393;
|
||||
import 'services/warmup_service.dart' as _i564;
|
||||
|
||||
extension GetItInjectableX on _i174.GetIt {
|
||||
@@ -33,6 +34,7 @@ extension GetItInjectableX on _i174.GetIt {
|
||||
gh.factory<_i439.EncKeysService>(() => _i439.EncKeysService());
|
||||
gh.factory<_i84.ThemeService>(() => _i84.ThemeService());
|
||||
gh.singleton<_i565.AppDatabase>(() => _i565.AppDatabase());
|
||||
gh.singleton<_i393.LogsService>(() => _i393.LogsService());
|
||||
gh.singleton<_i764.CollectionsInterface>(() => _i1001.CollectionsService());
|
||||
gh.singleton<_i580.AuthInterface>(() => _i975.AuthService());
|
||||
await gh.singletonAsync<_i564.WarmupService>(() {
|
||||
|
||||
@@ -2,27 +2,36 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
import 'package:remever/app.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/env.dart';
|
||||
import 'package:remever/i18n/strings.g.dart';
|
||||
import 'package:remever/inject.dart';
|
||||
import 'package:remever/router.dart';
|
||||
import 'package:remever/services/logs/custom_history.dart';
|
||||
import 'package:remever/services/warmup_service.dart';
|
||||
import 'package:talker/talker.dart';
|
||||
import 'package:talker_bloc_logger/talker_bloc_logger_observer.dart';
|
||||
import 'package:talker_bloc_logger/talker_bloc_logger_settings.dart';
|
||||
|
||||
void _onError(Object error, StackTrace trace) {
|
||||
debugPrint('error ${error.toString()}');
|
||||
logger.logError('main _onError', error, trace);
|
||||
}
|
||||
|
||||
void _flutterError(FlutterErrorDetails details) {
|
||||
debugPrint('error ${details.context}');
|
||||
logger.logError('main _flutterError', details.exception, details.stack);
|
||||
}
|
||||
|
||||
bool _platformDispatcher(Object exception, StackTrace stackTrace) {
|
||||
if ('$exception'.toLowerCase().contains('dio')) return true;
|
||||
|
||||
debugPrint('error ${exception.toString()}');
|
||||
logger.logError('main _platformDispatcher', exception, stackTrace);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -43,10 +52,26 @@ void main() {
|
||||
FlutterError.onError = _flutterError;
|
||||
PlatformDispatcher.instance.onError = _platformDispatcher;
|
||||
|
||||
final Talker talker = Talker(
|
||||
history: CustomHistory(
|
||||
TalkerSettings(),
|
||||
history: await logger.getHistory(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerSingleton<AppRouter>(AppRouter());
|
||||
getIt.registerSingleton<Talker>(talker);
|
||||
|
||||
await getIt<WarmupService>().firstStart();
|
||||
|
||||
Bloc.observer = TalkerBlocObserver(
|
||||
talker: talker,
|
||||
settings: const TalkerBlocLoggerSettings(
|
||||
printCreations: true,
|
||||
printClosings: true,
|
||||
),
|
||||
);
|
||||
|
||||
runApp(TranslationProvider(child: const MyApp()));
|
||||
}, _onError);
|
||||
}
|
||||
|
||||
15
lib/models/logs/build_log.dart
Normal file
15
lib/models/logs/build_log.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
// Package imports:
|
||||
import 'package:talker/talker.dart';
|
||||
|
||||
class BuildLog extends TalkerLog {
|
||||
BuildLog(String super.message);
|
||||
|
||||
@override
|
||||
String get title => 'Build';
|
||||
|
||||
@override
|
||||
String? get key => 'build_log_key';
|
||||
|
||||
@override
|
||||
AnsiPen get pen => AnsiPen()..xterm(121);
|
||||
}
|
||||
@@ -4,9 +4,12 @@ import 'package:drift_db_viewer/drift_db_viewer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it_mixin/get_it_mixin.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/widgets/typography.dart';
|
||||
import 'package:remever/common/widgets/wspace.dart';
|
||||
import 'package:remever/database/database.dart';
|
||||
import 'package:remever/widgets/primary_button.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
import '../../components/notifiers/app_settings.dart';
|
||||
import '../../components/env.dart';
|
||||
@@ -64,6 +67,17 @@ class _SandboxScreenState extends State<SandboxScreen> {
|
||||
},
|
||||
child: const Text('Open Db Viewer'),
|
||||
),
|
||||
PrimaryButton(
|
||||
child: const Text('Логи'),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<dynamic>(
|
||||
builder:
|
||||
(BuildContext context) => TalkerScreen(talker: talker),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -40,7 +40,9 @@ final class AuthService implements AuthInterface {
|
||||
if (response['success'] == false) return null;
|
||||
|
||||
return response['result']['authUid'];
|
||||
} catch (e) {
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе login', e, st);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -66,10 +68,8 @@ final class AuthService implements AuthInterface {
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (e) {
|
||||
if (e is DioException) {
|
||||
showErrorToast(e.response?.data['message']);
|
||||
}
|
||||
} catch (e, st) {
|
||||
logger.logError('Ошибка в методе send code', e, st);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
87
lib/services/logs/custom_history.dart
Normal file
87
lib/services/logs/custom_history.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
// Dart imports:
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
// Flutter imports:
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
// Package imports:
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
class CustomHistory implements TalkerHistory {
|
||||
CustomHistory(this.settings, {List<TalkerData>? history}) {
|
||||
if (history != null) {
|
||||
_history.addAll(history);
|
||||
}
|
||||
}
|
||||
|
||||
final TalkerSettings settings;
|
||||
|
||||
final List<TalkerData> _history = <TalkerData>[];
|
||||
|
||||
@override
|
||||
List<TalkerData> get history => _history;
|
||||
|
||||
@override
|
||||
void clean() async {
|
||||
if (settings.useHistory) {
|
||||
_history.clear();
|
||||
|
||||
final Directory docDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
final String path = '${docDir.path}/history.log';
|
||||
|
||||
final File logFile = File(path);
|
||||
|
||||
if (logFile.existsSync()) {
|
||||
logFile.deleteSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void write(TalkerData data) {
|
||||
if (settings.useHistory && settings.enabled) {
|
||||
if (settings.maxHistoryItems <= _history.length) {
|
||||
_history.removeAt(0);
|
||||
}
|
||||
|
||||
_history.add(data);
|
||||
|
||||
_writeToFile(data);
|
||||
}
|
||||
}
|
||||
|
||||
void _writeToFile(TalkerData data) async {
|
||||
try {
|
||||
final Directory docDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
final String path = '${docDir.path}/history.log';
|
||||
|
||||
final File logFile = File(path);
|
||||
|
||||
if (!logFile.existsSync()) {
|
||||
logFile.createSync();
|
||||
}
|
||||
|
||||
final Map<String, dynamic> jData = <String, dynamic>{
|
||||
'error': data.error,
|
||||
'exception': data.exception,
|
||||
'logLevel': data.logLevel,
|
||||
'key': data.key,
|
||||
'message': data.message,
|
||||
'stackTrace': data.stackTrace,
|
||||
'title': data.title,
|
||||
'time': data.time.toString(),
|
||||
};
|
||||
|
||||
logFile.writeAsStringSync(
|
||||
'${jsonEncode(jData)}\n',
|
||||
mode: FileMode.writeOnlyAppend,
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('ALARMA ERROR in _writeToFile customHistory $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
123
lib/services/logs/logs_service.dart
Normal file
123
lib/services/logs/logs_service.dart
Normal file
@@ -0,0 +1,123 @@
|
||||
// Dart imports:
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
// Flutter imports:
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
// Package imports:
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:remever/common/functions.dart';
|
||||
import 'package:remever/common/typedef.dart';
|
||||
import 'package:remever/models/logs/build_log.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
@Singleton()
|
||||
class LogsService {
|
||||
void log(String message) {
|
||||
talker.log(message);
|
||||
}
|
||||
|
||||
void logDebug(String message) {
|
||||
talker.debug(message);
|
||||
}
|
||||
|
||||
void logInfo(String message) {
|
||||
talker.info(message);
|
||||
}
|
||||
|
||||
void logError(String message, Object? exception, StackTrace? stackTrace) {
|
||||
talker.error(message, exception, stackTrace);
|
||||
|
||||
showErrorToast('$message ${exception.toString()}');
|
||||
}
|
||||
|
||||
void logCritical(String message, Object? exception, StackTrace? stackTrace) {
|
||||
talker.critical(message, exception, stackTrace);
|
||||
}
|
||||
|
||||
void logBuild(String message) {
|
||||
talker.logCustom(BuildLog(message));
|
||||
}
|
||||
|
||||
// Метод для получения истории
|
||||
Future<List<TalkerData>?> getHistory() async {
|
||||
try {
|
||||
final Directory docDir = await getApplicationDocumentsDirectory();
|
||||
final String path = '${docDir.path}/history.log';
|
||||
final File logFile = File(path);
|
||||
|
||||
if (!logFile.existsSync()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<String> lines = await logFile.readAsLines();
|
||||
if (lines.length > 1500) {
|
||||
lines.removeRange(0, lines.length - 1500);
|
||||
|
||||
logFile.deleteSync();
|
||||
logFile.createSync();
|
||||
logFile.writeAsStringSync(lines.join('\n'));
|
||||
}
|
||||
|
||||
final List<Json> entities = await _parseLines(lines, logFile);
|
||||
|
||||
if (entities.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<TalkerData> talkerData = _convertEntitiesToTalkerData(
|
||||
entities,
|
||||
);
|
||||
return talkerData;
|
||||
} catch (e) {
|
||||
debugPrint('ALARM ERROR IN GETHISTORY $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Парсинг строк из файла
|
||||
Future<List<Json>> _parseLines(List<String> lines, File logFile) async {
|
||||
final List<Json> entities = <Json>[];
|
||||
|
||||
for (String line in lines) {
|
||||
try {
|
||||
entities.add(jsonDecode(line));
|
||||
} catch (e) {
|
||||
debugPrint('Error $e in line $line of ${logFile.path}');
|
||||
|
||||
if (e.toString().contains('Unexpected character')) {
|
||||
unawaited(logFile.delete());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
// Преобразование JSON-сущностей в TalkerData
|
||||
List<TalkerData> _convertEntitiesToTalkerData(List<Json> entities) {
|
||||
final List<TalkerData> talkerData = <TalkerData>[];
|
||||
|
||||
for (Json json in entities) {
|
||||
talkerData.add(
|
||||
TalkerData(
|
||||
json['message'],
|
||||
error: json['error'],
|
||||
exception: json['exception'],
|
||||
key: json['key'],
|
||||
logLevel: json['logLevel'],
|
||||
stackTrace: json['stackTrace'],
|
||||
time: DateTime.tryParse(json['time']),
|
||||
title: 'SAVED ${json['title']}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return talkerData;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user