Fix log and analytics bugs
This commit is contained in:
@@ -67,12 +67,7 @@ Future<void> lazyBootstrap(
|
||||
options.tracesSampleRate = 0.25;
|
||||
options.enableUserInteractionTracing = true;
|
||||
options.addIntegration(sentryLogger);
|
||||
options.beforeSend = (event, {hint}) {
|
||||
return switch (event.throwable) {
|
||||
ExpectedException _ => null,
|
||||
_ => event,
|
||||
};
|
||||
};
|
||||
options.beforeSend = sentryBeforeSend;
|
||||
options.logger = (level, message, {exception, logger, stackTrace}) {
|
||||
if (level == SentryLevel.fatal) {
|
||||
_logger.debug(message);
|
||||
|
||||
@@ -73,7 +73,7 @@ class ProfilesRepositoryImpl
|
||||
() async {
|
||||
final existingProfile = await profilesDao.getProfileByUrl(url);
|
||||
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
|
||||
? existingProfile.copyWith(active: true)
|
||||
: existingProfile;
|
||||
@@ -120,9 +120,9 @@ class ProfilesRepositoryImpl
|
||||
final parseResult =
|
||||
await singbox.parseConfig(path, tempPath, false).run();
|
||||
return parseResult.fold(
|
||||
(l) async {
|
||||
loggy.warning("error parsing config: $l");
|
||||
return left(ProfileFailure.invalidConfig(l.msg));
|
||||
(err) async {
|
||||
loggy.warning("error parsing config", err);
|
||||
return left(ProfileFailure.invalidConfig(err.msg));
|
||||
},
|
||||
(_) async {
|
||||
final profile = LocalProfile(
|
||||
@@ -267,9 +267,9 @@ class ProfilesRepositoryImpl
|
||||
final parseResult =
|
||||
await singbox.parseConfig(path, tempPath, false).run();
|
||||
return parseResult.fold(
|
||||
(l) async {
|
||||
loggy.warning("error parsing config: $l");
|
||||
return left(ProfileFailure.invalidConfig(l.msg));
|
||||
(err) async {
|
||||
loggy.warning("error parsing config", err);
|
||||
return left(ProfileFailure.invalidConfig(err.msg));
|
||||
},
|
||||
(_) async {
|
||||
final profile = Profile.fromResponse(url, headers);
|
||||
|
||||
@@ -8,6 +8,7 @@ part 'app_failure.freezed.dart';
|
||||
sealed class AppFailure with _$AppFailure, Failure {
|
||||
const AppFailure._();
|
||||
|
||||
@With<UnexpectedFailure>()
|
||||
const factory AppFailure.unexpected([
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
|
||||
@@ -9,6 +9,7 @@ part 'connection_failure.freezed.dart';
|
||||
sealed class ConnectionFailure with _$ConnectionFailure, Failure {
|
||||
const ConnectionFailure._();
|
||||
|
||||
@With<UnexpectedFailure>()
|
||||
const factory ConnectionFailure.unexpected([
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
|
||||
@@ -8,9 +8,10 @@ part 'core_service_failure.freezed.dart';
|
||||
sealed class CoreServiceFailure with _$CoreServiceFailure, Failure {
|
||||
const CoreServiceFailure._();
|
||||
|
||||
@With<UnexpectedFailure>()
|
||||
const factory CoreServiceFailure.unexpected(
|
||||
Object error,
|
||||
StackTrace stackTrace,
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
) = UnexpectedCoreServiceFailure;
|
||||
|
||||
@With<ExpectedException>()
|
||||
|
||||
@@ -5,6 +5,11 @@ mixin Failure {
|
||||
({String type, String? message}) present(TranslationsEn t);
|
||||
}
|
||||
|
||||
mixin UnexpectedFailure {
|
||||
Object? get error;
|
||||
StackTrace? get stackTrace;
|
||||
}
|
||||
|
||||
/// failures ignored by analytics service etc.
|
||||
mixin ExpectedException {}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ part 'profiles_failure.freezed.dart';
|
||||
sealed class ProfileFailure with _$ProfileFailure, Failure {
|
||||
const ProfileFailure._();
|
||||
|
||||
@With<UnexpectedFailure>()
|
||||
const factory ProfileFailure.unexpected([
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
|
||||
@@ -50,16 +50,16 @@ class AppUpdateNotifier extends _$AppUpdateNotifier with AppLogger {
|
||||
)
|
||||
.match(
|
||||
(err) {
|
||||
loggy.warning("failed to get latest version, $err");
|
||||
loggy.warning("failed to get latest version", err);
|
||||
return state = AppUpdateState.error(err);
|
||||
},
|
||||
(remote) {
|
||||
if (remote.version.compareTo(currentVersion) > 0) {
|
||||
loggy.info("new version available: $remote");
|
||||
loggy.debug("new version available: $remote");
|
||||
return state = AppUpdateState.available(remote);
|
||||
}
|
||||
loggy.info(
|
||||
"already using latest version[$currentVersion], remote: $remote",
|
||||
"already using latest version[$currentVersion], remote: [${remote.version}]",
|
||||
);
|
||||
return state = const AppUpdateState.notAvailable();
|
||||
},
|
||||
|
||||
@@ -47,9 +47,9 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
|
||||
return _disconnect();
|
||||
}
|
||||
loggy.debug("reconnecting, profile: [$profileId]");
|
||||
await _core.restart(profileId).mapLeft((l) {
|
||||
loggy.warning("error reconnecting: $l");
|
||||
state = AsyncError(l, StackTrace.current);
|
||||
await _core.restart(profileId).mapLeft((err) {
|
||||
loggy.warning("error reconnecting", err);
|
||||
state = AsyncError(err, StackTrace.current);
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
@@ -67,16 +67,16 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
|
||||
|
||||
Future<void> _connect() async {
|
||||
final activeProfile = await ref.read(activeProfileProvider.future);
|
||||
await _core.start(activeProfile!.id).mapLeft((l) {
|
||||
loggy.warning("error connecting: $l");
|
||||
state = AsyncError(l, StackTrace.current);
|
||||
await _core.start(activeProfile!.id).mapLeft((err) {
|
||||
loggy.warning("error connecting", err);
|
||||
state = AsyncError(err, StackTrace.current);
|
||||
}).run();
|
||||
}
|
||||
|
||||
Future<void> _disconnect() async {
|
||||
await _core.stop().mapLeft((l) {
|
||||
loggy.warning("error disconnecting: $l");
|
||||
state = AsyncError(l, StackTrace.current);
|
||||
await _core.stop().mapLeft((err) {
|
||||
loggy.warning("error disconnecting", err);
|
||||
state = AsyncError(err, StackTrace.current);
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ class ProfileDetailNotifier extends _$ProfileDetailNotifier with AppLogger {
|
||||
}
|
||||
final failureOrProfile = await _profilesRepo.get(id).run();
|
||||
return failureOrProfile.match(
|
||||
(l) {
|
||||
loggy.warning('failed to load profile, $l');
|
||||
throw l;
|
||||
(err) {
|
||||
loggy.warning('failed to load profile', err);
|
||||
throw err;
|
||||
},
|
||||
(profile) {
|
||||
if (profile == null) {
|
||||
|
||||
@@ -43,9 +43,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
|
||||
|
||||
Future<Unit> selectActiveProfile(String id) async {
|
||||
loggy.debug('changing active profile to: [$id]');
|
||||
return _profilesRepo.setAsActive(id).getOrElse((f) {
|
||||
loggy.warning('failed to set [$id] as active profile, $f');
|
||||
throw f;
|
||||
return _profilesRepo.setAsActive(id).getOrElse((err) {
|
||||
loggy.warning('failed to set [$id] as active profile', err);
|
||||
throw err;
|
||||
}).run();
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
|
||||
return ref
|
||||
.read(profilesRepositoryProvider)
|
||||
.addByUrl(link.url, markAsActive: markAsActive)
|
||||
.getOrElse((l) {
|
||||
loggy.warning("failed to add profile: $l");
|
||||
throw l;
|
||||
.getOrElse((err) {
|
||||
loggy.warning("failed to add profile", err);
|
||||
throw err;
|
||||
}).run();
|
||||
} else if (LinkParser.protocol(rawInput) case (final parsed)?) {
|
||||
loggy.debug("adding profile, content");
|
||||
@@ -71,9 +71,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
|
||||
name: parsed.name,
|
||||
markAsActive: markAsActive,
|
||||
)
|
||||
.getOrElse((l) {
|
||||
loggy.warning("failed to add profile: $l");
|
||||
throw l;
|
||||
.getOrElse((err) {
|
||||
loggy.warning("failed to add profile", err);
|
||||
throw err;
|
||||
}).run();
|
||||
} else {
|
||||
loggy.debug("invalid content");
|
||||
@@ -93,9 +93,9 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
|
||||
Future<void> deleteProfile(Profile profile) async {
|
||||
loggy.debug('deleting profile: ${profile.name}');
|
||||
await _profilesRepo.delete(profile.id).mapLeft(
|
||||
(f) {
|
||||
loggy.warning('failed to delete profile, $f');
|
||||
throw f;
|
||||
(err) {
|
||||
loggy.warning('failed to delete profile', err);
|
||||
throw err;
|
||||
},
|
||||
).run();
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
||||
.watchOutbounds()
|
||||
.map(
|
||||
(event) => event.getOrElse(
|
||||
(f) {
|
||||
loggy.warning("error receiving proxies: $f");
|
||||
throw f;
|
||||
(err) {
|
||||
loggy.warning("error receiving proxies", err);
|
||||
throw err;
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -105,9 +105,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
||||
await ref
|
||||
.read(coreFacadeProvider)
|
||||
.selectOutbound(groupTag, outboundTag)
|
||||
.getOrElse((l) {
|
||||
loggy.warning("error selecting outbound", l);
|
||||
throw l;
|
||||
.getOrElse((err) {
|
||||
loggy.warning("error selecting outbound", err);
|
||||
throw err;
|
||||
}).run();
|
||||
state = AsyncData(
|
||||
[
|
||||
@@ -122,9 +122,9 @@ class ProxiesNotifier extends _$ProxiesNotifier with AppLogger {
|
||||
Future<void> urlTest(String groupTag) async {
|
||||
loggy.debug("testing group: [$groupTag]");
|
||||
if (state case AsyncData()) {
|
||||
await ref.read(coreFacadeProvider).urlTest(groupTag).getOrElse((l) {
|
||||
loggy.warning("error testing group", l);
|
||||
throw l;
|
||||
await ref.read(coreFacadeProvider).urlTest(groupTag).getOrElse((err) {
|
||||
loggy.warning("error testing group", err);
|
||||
throw err;
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ Future<List<InstalledPackageInfo>> installedPackagesInfo(
|
||||
return ref
|
||||
.watch(platformSettingsProvider)
|
||||
.getInstalledPackages()
|
||||
.getOrElse((l) {
|
||||
_logger.warning("error getting installed packages: $l");
|
||||
throw l;
|
||||
.getOrElse((err) {
|
||||
_logger.warning("error getting installed packages", err);
|
||||
throw err;
|
||||
}).run();
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ Future<ImageProvider> packageIcon(
|
||||
final bytes = await ref
|
||||
.watch(platformSettingsProvider)
|
||||
.getPackageIcon(packageName)
|
||||
.getOrElse((l) {
|
||||
_logger.warning("error getting package icon: $l");
|
||||
throw l;
|
||||
.getOrElse((err) {
|
||||
_logger.warning("error getting package icon", err);
|
||||
throw err;
|
||||
}).run();
|
||||
return MemoryImage(bytes);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:hiddify/utils/sentry_utils.dart';
|
||||
import 'package:loggy/loggy.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
@@ -33,6 +34,8 @@ class SentryLoggyIntegration extends LoggyPrinter
|
||||
|
||||
@override
|
||||
Future<void> onLog(LogRecord record) async {
|
||||
if (!canSendEvent(record.error)) return;
|
||||
|
||||
if (_shouldLog(record.level, _minEventLevel)) {
|
||||
await _hub.captureEvent(
|
||||
record.toEvent(),
|
||||
|
||||
21
lib/utils/sentry_utils.dart
Normal file
21
lib/utils/sentry_utils.dart
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export 'placeholders.dart';
|
||||
export 'platform_utils.dart';
|
||||
export 'sentry_loggy_integration.dart';
|
||||
export 'sentry_riverpod_observer.dart';
|
||||
export 'sentry_utils.dart';
|
||||
export 'text_utils.dart';
|
||||
export 'uri_utils.dart';
|
||||
export 'validators.dart';
|
||||
|
||||
Reference in New Issue
Block a user