Fix log and analytics bugs

This commit is contained in:
problematicconsumer
2023-10-03 21:12:14 +03:30
parent ba071643ce
commit 8f15022443
16 changed files with 86 additions and 57 deletions

View File

@@ -67,12 +67,7 @@ Future<void> lazyBootstrap(
options.tracesSampleRate = 0.25; options.tracesSampleRate = 0.25;
options.enableUserInteractionTracing = true; options.enableUserInteractionTracing = true;
options.addIntegration(sentryLogger); options.addIntegration(sentryLogger);
options.beforeSend = (event, {hint}) { options.beforeSend = sentryBeforeSend;
return switch (event.throwable) {
ExpectedException _ => null,
_ => event,
};
};
options.logger = (level, message, {exception, logger, stackTrace}) { options.logger = (level, message, {exception, logger, stackTrace}) {
if (level == SentryLevel.fatal) { if (level == SentryLevel.fatal) {
_logger.debug(message); _logger.debug(message);

View File

@@ -73,7 +73,7 @@ class ProfilesRepositoryImpl
() async { () async {
final existingProfile = await profilesDao.getProfileByUrl(url); final existingProfile = await profilesDao.getProfileByUrl(url);
if (existingProfile case RemoteProfile()) { if (existingProfile case RemoteProfile()) {
loggy.info("profile with url[$url] already exists, updating"); loggy.info("profile with same url already exists, updating");
final baseProfile = markAsActive final baseProfile = markAsActive
? existingProfile.copyWith(active: true) ? existingProfile.copyWith(active: true)
: existingProfile; : existingProfile;
@@ -120,9 +120,9 @@ class ProfilesRepositoryImpl
final parseResult = final parseResult =
await singbox.parseConfig(path, tempPath, false).run(); await singbox.parseConfig(path, tempPath, false).run();
return parseResult.fold( return parseResult.fold(
(l) async { (err) async {
loggy.warning("error parsing config: $l"); loggy.warning("error parsing config", err);
return left(ProfileFailure.invalidConfig(l.msg)); return left(ProfileFailure.invalidConfig(err.msg));
}, },
(_) async { (_) async {
final profile = LocalProfile( final profile = LocalProfile(
@@ -267,9 +267,9 @@ class ProfilesRepositoryImpl
final parseResult = final parseResult =
await singbox.parseConfig(path, tempPath, false).run(); await singbox.parseConfig(path, tempPath, false).run();
return parseResult.fold( return parseResult.fold(
(l) async { (err) async {
loggy.warning("error parsing config: $l"); loggy.warning("error parsing config", err);
return left(ProfileFailure.invalidConfig(l.msg)); return left(ProfileFailure.invalidConfig(err.msg));
}, },
(_) async { (_) async {
final profile = Profile.fromResponse(url, headers); final profile = Profile.fromResponse(url, headers);

View File

@@ -8,6 +8,7 @@ part 'app_failure.freezed.dart';
sealed class AppFailure with _$AppFailure, Failure { sealed class AppFailure with _$AppFailure, Failure {
const AppFailure._(); const AppFailure._();
@With<UnexpectedFailure>()
const factory AppFailure.unexpected([ const factory AppFailure.unexpected([
Object? error, Object? error,
StackTrace? stackTrace, StackTrace? stackTrace,

View File

@@ -9,6 +9,7 @@ part 'connection_failure.freezed.dart';
sealed class ConnectionFailure with _$ConnectionFailure, Failure { sealed class ConnectionFailure with _$ConnectionFailure, Failure {
const ConnectionFailure._(); const ConnectionFailure._();
@With<UnexpectedFailure>()
const factory ConnectionFailure.unexpected([ const factory ConnectionFailure.unexpected([
Object? error, Object? error,
StackTrace? stackTrace, StackTrace? stackTrace,

View File

@@ -8,9 +8,10 @@ part 'core_service_failure.freezed.dart';
sealed class CoreServiceFailure with _$CoreServiceFailure, Failure { sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
const CoreServiceFailure._(); const CoreServiceFailure._();
@With<UnexpectedFailure>()
const factory CoreServiceFailure.unexpected( const factory CoreServiceFailure.unexpected(
Object error, Object? error,
StackTrace stackTrace, StackTrace? stackTrace,
) = UnexpectedCoreServiceFailure; ) = UnexpectedCoreServiceFailure;
@With<ExpectedException>() @With<ExpectedException>()

View File

@@ -5,6 +5,11 @@ mixin Failure {
({String type, String? message}) present(TranslationsEn t); ({String type, String? message}) present(TranslationsEn t);
} }
mixin UnexpectedFailure {
Object? get error;
StackTrace? get stackTrace;
}
/// failures ignored by analytics service etc. /// failures ignored by analytics service etc.
mixin ExpectedException {} mixin ExpectedException {}

View File

@@ -8,6 +8,7 @@ part 'profiles_failure.freezed.dart';
sealed class ProfileFailure with _$ProfileFailure, Failure { sealed class ProfileFailure with _$ProfileFailure, Failure {
const ProfileFailure._(); const ProfileFailure._();
@With<UnexpectedFailure>()
const factory ProfileFailure.unexpected([ const factory ProfileFailure.unexpected([
Object? error, Object? error,
StackTrace? stackTrace, StackTrace? stackTrace,

View File

@@ -50,16 +50,16 @@ class AppUpdateNotifier extends _$AppUpdateNotifier with AppLogger {
) )
.match( .match(
(err) { (err) {
loggy.warning("failed to get latest version, $err"); loggy.warning("failed to get latest version", err);
return state = AppUpdateState.error(err); return state = AppUpdateState.error(err);
}, },
(remote) { (remote) {
if (remote.version.compareTo(currentVersion) > 0) { if (remote.version.compareTo(currentVersion) > 0) {
loggy.info("new version available: $remote"); loggy.debug("new version available: $remote");
return state = AppUpdateState.available(remote); return state = AppUpdateState.available(remote);
} }
loggy.info( loggy.info(
"already using latest version[$currentVersion], remote: $remote", "already using latest version[$currentVersion], remote: [${remote.version}]",
); );
return state = const AppUpdateState.notAvailable(); return state = const AppUpdateState.notAvailable();
}, },

View File

@@ -47,9 +47,9 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
return _disconnect(); return _disconnect();
} }
loggy.debug("reconnecting, profile: [$profileId]"); loggy.debug("reconnecting, profile: [$profileId]");
await _core.restart(profileId).mapLeft((l) { await _core.restart(profileId).mapLeft((err) {
loggy.warning("error reconnecting: $l"); loggy.warning("error reconnecting", err);
state = AsyncError(l, StackTrace.current); state = AsyncError(err, StackTrace.current);
}).run(); }).run();
} }
} }
@@ -67,16 +67,16 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
Future<void> _connect() async { Future<void> _connect() async {
final activeProfile = await ref.read(activeProfileProvider.future); final activeProfile = await ref.read(activeProfileProvider.future);
await _core.start(activeProfile!.id).mapLeft((l) { await _core.start(activeProfile!.id).mapLeft((err) {
loggy.warning("error connecting: $l"); loggy.warning("error connecting", err);
state = AsyncError(l, StackTrace.current); state = AsyncError(err, StackTrace.current);
}).run(); }).run();
} }
Future<void> _disconnect() async { Future<void> _disconnect() async {
await _core.stop().mapLeft((l) { await _core.stop().mapLeft((err) {
loggy.warning("error disconnecting: $l"); loggy.warning("error disconnecting", err);
state = AsyncError(l, StackTrace.current); state = AsyncError(err, StackTrace.current);
}).run(); }).run();
} }
} }

View File

@@ -30,9 +30,9 @@ class ProfileDetailNotifier extends _$ProfileDetailNotifier with AppLogger {
} }
final failureOrProfile = await _profilesRepo.get(id).run(); final failureOrProfile = await _profilesRepo.get(id).run();
return failureOrProfile.match( return failureOrProfile.match(
(l) { (err) {
loggy.warning('failed to load profile, $l'); loggy.warning('failed to load profile', err);
throw l; throw err;
}, },
(profile) { (profile) {
if (profile == null) { if (profile == null) {

View File

@@ -43,9 +43,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
Future<Unit> selectActiveProfile(String id) async { Future<Unit> selectActiveProfile(String id) async {
loggy.debug('changing active profile to: [$id]'); loggy.debug('changing active profile to: [$id]');
return _profilesRepo.setAsActive(id).getOrElse((f) { return _profilesRepo.setAsActive(id).getOrElse((err) {
loggy.warning('failed to set [$id] as active profile, $f'); loggy.warning('failed to set [$id] as active profile', err);
throw f; throw err;
}).run(); }).run();
} }
@@ -58,9 +58,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
return ref return ref
.read(profilesRepositoryProvider) .read(profilesRepositoryProvider)
.addByUrl(link.url, markAsActive: markAsActive) .addByUrl(link.url, markAsActive: markAsActive)
.getOrElse((l) { .getOrElse((err) {
loggy.warning("failed to add profile: $l"); loggy.warning("failed to add profile", err);
throw l; throw err;
}).run(); }).run();
} else if (LinkParser.protocol(rawInput) case (final parsed)?) { } else if (LinkParser.protocol(rawInput) case (final parsed)?) {
loggy.debug("adding profile, content"); loggy.debug("adding profile, content");
@@ -71,9 +71,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
name: parsed.name, name: parsed.name,
markAsActive: markAsActive, markAsActive: markAsActive,
) )
.getOrElse((l) { .getOrElse((err) {
loggy.warning("failed to add profile: $l"); loggy.warning("failed to add profile", err);
throw l; throw err;
}).run(); }).run();
} else { } else {
loggy.debug("invalid content"); loggy.debug("invalid content");
@@ -93,9 +93,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
Future<void> deleteProfile(Profile profile) async { Future<void> deleteProfile(Profile profile) async {
loggy.debug('deleting profile: ${profile.name}'); loggy.debug('deleting profile: ${profile.name}');
await _profilesRepo.delete(profile.id).mapLeft( await _profilesRepo.delete(profile.id).mapLeft(
(f) { (err) {
loggy.warning('failed to delete profile, $f'); loggy.warning('failed to delete profile', err);
throw f; throw err;
}, },
).run(); ).run();
} }

View File

@@ -60,9 +60,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
.watchOutbounds() .watchOutbounds()
.map( .map(
(event) => event.getOrElse( (event) => event.getOrElse(
(f) { (err) {
loggy.warning("error receiving proxies: $f"); loggy.warning("error receiving proxies", err);
throw f; throw err;
}, },
), ),
) )
@@ -105,9 +105,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
await ref await ref
.read(coreFacadeProvider) .read(coreFacadeProvider)
.selectOutbound(groupTag, outboundTag) .selectOutbound(groupTag, outboundTag)
.getOrElse((l) { .getOrElse((err) {
loggy.warning("error selecting outbound", l); loggy.warning("error selecting outbound", err);
throw l; throw err;
}).run(); }).run();
state = AsyncData( state = AsyncData(
[ [
@@ -122,9 +122,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
Future<void> urlTest(String groupTag) async { Future<void> urlTest(String groupTag) async {
loggy.debug("testing group: [$groupTag]"); loggy.debug("testing group: [$groupTag]");
if (state case AsyncData()) { if (state case AsyncData()) {
await ref.read(coreFacadeProvider).urlTest(groupTag).getOrElse((l) { await ref.read(coreFacadeProvider).urlTest(groupTag).getOrElse((err) {
loggy.warning("error testing group", l); loggy.warning("error testing group", err);
throw l; throw err;
}).run(); }).run();
} }
} }

View File

@@ -25,9 +25,9 @@ Future<List<InstalledPackageInfo>> installedPackagesInfo(
return ref return ref
.watch(platformSettingsProvider) .watch(platformSettingsProvider)
.getInstalledPackages() .getInstalledPackages()
.getOrElse((l) { .getOrElse((err) {
_logger.warning("error getting installed packages: $l"); _logger.warning("error getting installed packages", err);
throw l; throw err;
}).run(); }).run();
} }
@@ -40,9 +40,9 @@ Future<ImageProvider> packageIcon(
final bytes = await ref final bytes = await ref
.watch(platformSettingsProvider) .watch(platformSettingsProvider)
.getPackageIcon(packageName) .getPackageIcon(packageName)
.getOrElse((l) { .getOrElse((err) {
_logger.warning("error getting package icon: $l"); _logger.warning("error getting package icon", err);
throw l; throw err;
}).run(); }).run();
return MemoryImage(bytes); return MemoryImage(bytes);
} }

View File

@@ -1,3 +1,4 @@
import 'package:hiddify/utils/sentry_utils.dart';
import 'package:loggy/loggy.dart'; import 'package:loggy/loggy.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
@@ -33,6 +34,8 @@ class SentryLoggyIntegration extends LoggyPrinter
@override @override
Future<void> onLog(LogRecord record) async { Future<void> onLog(LogRecord record) async {
if (!canSendEvent(record.error)) return;
if (_shouldLog(record.level, _minEventLevel)) { if (_shouldLog(record.level, _minEventLevel)) {
await _hub.captureEvent( await _hub.captureEvent(
record.toEvent(), record.toEvent(),

View File

@@ -0,0 +1,21 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:hiddify/domain/failures.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
FutureOr<SentryEvent?> sentryBeforeSend(SentryEvent event, {Hint? hint}) {
if (canSendEvent(event.throwable)) return event;
return null;
}
bool canSendEvent(dynamic throwable) {
return switch (throwable) {
UnexpectedFailure(:final error) => canSendEvent(error),
DioException _ => false,
SocketException _ => false,
ExpectedException _ => false,
_ => true,
};
}

View File

@@ -13,6 +13,7 @@ export 'placeholders.dart';
export 'platform_utils.dart'; export 'platform_utils.dart';
export 'sentry_loggy_integration.dart'; export 'sentry_loggy_integration.dart';
export 'sentry_riverpod_observer.dart'; export 'sentry_riverpod_observer.dart';
export 'sentry_utils.dart';
export 'text_utils.dart'; export 'text_utils.dart';
export 'uri_utils.dart'; export 'uri_utils.dart';
export 'validators.dart'; export 'validators.dart';