Change initialization and logging

This commit is contained in:
problematicconsumer
2023-12-22 14:16:24 +03:30
parent defca7de5e
commit 6219663a4c
22 changed files with 409 additions and 277 deletions

View File

@@ -0,0 +1,67 @@
import 'package:flutter/foundation.dart';
import 'package:hiddify/core/analytics/analytics_filter.dart';
import 'package:hiddify/core/analytics/analytics_logger.dart';
import 'package:hiddify/core/app_info/app_info_provider.dart';
import 'package:hiddify/core/logger/logger_controller.dart';
import 'package:hiddify/core/model/environment.dart';
import 'package:hiddify/core/preferences/preferences_provider.dart';
import 'package:hiddify/utils/custom_loggers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'analytics_controller.g.dart';
const String enableAnalyticsPrefKey = "enable_analytics";
bool _testCrashReport = false;
@Riverpod(keepAlive: true)
class AnalyticsController extends _$AnalyticsController with AppLogger {
@override
bool build() {
return _preferences.getBool(enableAnalyticsPrefKey) ?? true;
}
SharedPreferences get _preferences =>
ref.read(sharedPreferencesProvider).requireValue;
Future<void> enableAnalytics() async {
loggy.debug("enabling analytics");
if (!state) {
await _preferences.setBool(enableAnalyticsPrefKey, true);
}
final env = ref.read(environmentProvider);
final appInfo = await ref.read(appInfoProvider.future);
final dsn = !kDebugMode || _testCrashReport ? Environment.sentryDSN : "";
final sentryLogger = SentryLoggyIntegration();
LoggerController.instance.addPrinter("analytics", sentryLogger);
await SentryFlutter.init(
(options) {
options.dsn = dsn;
options.environment = env.name;
options.dist = appInfo.release.name;
options.debug = kDebugMode;
options.enableNativeCrashHandling = true;
options.enableNdkScopeSync = true;
options.attachThreads = true;
options.tracesSampleRate = 0.20;
options.enableUserInteractionTracing = true;
options.addIntegration(sentryLogger);
options.beforeSend = sentryBeforeSend;
},
);
state = true;
}
Future<void> disableAnalytics() async {
loggy.debug("disabling analytics");
await _preferences.setBool(enableAnalyticsPrefKey, false);
await Sentry.close();
LoggerController.instance.removePrinter("analytics");
state = false;
}
}

View File

@@ -0,0 +1,29 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:hiddify/core/model/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,
HttpException _ => false,
HandshakeException _ => false,
ExpectedFailure _ => false,
ExpectedMeasuredFailure _ => false,
_ => true,
};
}
bool canLogEvent(dynamic throwable) => switch (throwable) {
ExpectedMeasuredFailure _ => true,
_ => canSendEvent(throwable),
};

View File

@@ -0,0 +1,98 @@
import 'package:hiddify/utils/sentry_utils.dart';
import 'package:loggy/loggy.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
// modified version of https://github.com/getsentry/sentry-dart/tree/main/logging
class SentryLoggyIntegration extends LoggyPrinter
implements Integration<SentryOptions> {
SentryLoggyIntegration({
LogLevel minBreadcrumbLevel = LogLevel.info,
LogLevel minEventLevel = LogLevel.error,
}) : _minBreadcrumbLevel = minBreadcrumbLevel,
_minEventLevel = minEventLevel;
final LogLevel _minBreadcrumbLevel;
final LogLevel _minEventLevel;
late Hub _hub;
@override
void call(Hub hub, SentryOptions options) {
_hub = hub;
options.sdk.addIntegration('LoggyIntegration');
}
@override
Future<void> close() async {}
bool _shouldLog(LogLevel logLevel, LogLevel minLevel) {
if (logLevel == LogLevel.off) {
return false;
}
return logLevel.priority >= minLevel.priority;
}
@override
Future<void> onLog(LogRecord record) async {
if (!canLogEvent(record.error)) return;
if (_shouldLog(record.level, _minEventLevel)) {
await _hub.captureEvent(
record.toEvent(),
stackTrace: record.stackTrace,
hint: Hint.withMap({TypeCheckHint.record: record}),
);
}
if (_shouldLog(record.level, _minBreadcrumbLevel)) {
await _hub.addBreadcrumb(
record.toBreadcrumb(),
hint: Hint.withMap({TypeCheckHint.record: record}),
);
}
}
}
extension LogRecordX on LogRecord {
Breadcrumb toBreadcrumb() {
return Breadcrumb(
category: 'log',
type: 'debug',
timestamp: time.toUtc(),
level: level.toSentryLevel(),
message: message,
data: <String, Object>{
if (object != null) 'LogRecord.object': object!,
if (error != null) 'LogRecord.error': error!,
if (stackTrace != null) 'LogRecord.stackTrace': stackTrace!,
'LogRecord.loggerName': loggerName,
'LogRecord.sequenceNumber': sequenceNumber,
},
);
}
SentryEvent toEvent() {
return SentryEvent(
timestamp: time.toUtc(),
logger: loggerName,
level: level.toSentryLevel(),
message: SentryMessage(message),
throwable: error,
// ignore: deprecated_member_use
extra: <String, Object>{
if (object != null) 'LogRecord.object': object!,
'LogRecord.sequenceNumber': sequenceNumber,
},
);
}
}
extension LogLevelX on LogLevel {
SentryLevel? toSentryLevel() => switch (this) {
LogLevel.all || LogLevel.debug => SentryLevel.debug,
LogLevel.info => SentryLevel.info,
LogLevel.warning => SentryLevel.warning,
LogLevel.error => SentryLevel.error,
_ => null,
};
}