Firebase analytics és crashlytics használatának beállítása a FirebaseLogger-ben.

This commit is contained in:
torok.istvan 2026-06-24 11:24:19 +02:00
parent ccf62aafce
commit e126f340c6
5 changed files with 294 additions and 50 deletions

View File

@ -1,4 +1,7 @@
import 'dart:ui';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -6,8 +9,10 @@ import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart'; import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart';
import 'package:terepi_seged/routes/app_pages.dart'; import 'package:terepi_seged/routes/app_pages.dart';
import 'package:terepi_seged/services/app_database.dart'; import 'package:terepi_seged/services/app_database.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:terepi_seged/services/coord_converter_service.dart'; import 'package:terepi_seged/services/coord_converter_service.dart';
import 'package:terepi_seged/services/device_identity_service.dart'; import 'package:terepi_seged/services/device_identity_service.dart';
import 'package:terepi_seged/services/firebase_logger.dart';
import 'package:terepi_seged/services/gnss/gnss_device_service.dart'; import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
import 'package:terepi_seged/services/gnss/gnss_service.dart'; import 'package:terepi_seged/services/gnss/gnss_service.dart';
import 'package:terepi_seged/services/layer_import_service.dart'; import 'package:terepi_seged/services/layer_import_service.dart';
@ -20,6 +25,17 @@ import 'package:terepi_seged/services/track_sync_service.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(); await Firebase.initializeApp();
FlutterError.onError = (details) {
FlutterError.presentError(details);
FirebaseCrashlytics.instance.recordFlutterFatalError(details);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
await dotenv.load(fileName: ".env"); await dotenv.load(fileName: ".env");
await Supabase.initialize( await Supabase.initialize(
@ -29,6 +45,8 @@ Future<void> main() async {
await AppDatabase.instance.database; await AppDatabase.instance.database;
Get.put(ProjectService(), permanent: true); Get.put(ProjectService(), permanent: true);
Get.put(AppLogger(), permanent: true);
Get.put(FirebaseLogger(), permanent: true);
await Get.putAsync<CoordConverterService>( await Get.putAsync<CoordConverterService>(
() => CoordConverterService().init()); () => CoordConverterService().init());
Get.put(GnssDeviceService()); Get.put(GnssDeviceService());

View File

@ -1,10 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:terepi_seged/services/firebase_logger.dart';
import 'package:terepi_seged/services/project_service.dart'; import 'package:terepi_seged/services/project_service.dart';
import 'package:terepi_seged/services/track_sync_service.dart'; import 'package:terepi_seged/services/track_sync_service.dart';
@ -169,11 +173,18 @@ class TrackingController extends GetxController {
_positionSub = _source!.positionStream.listen( _positionSub = _source!.positionStream.listen(
_onPosition, _onPosition,
onError: (e) { onError: (e) {
AppLogger.e('positionStream', 'Stream hiba', error: e);
FirebaseLogger.e('positionStream', 'Stream hiba', error: e);
if (e is LocationServiceDisabledException ||
e is PermissionDeniedException) {
Get.snackbar('GPS hiba', e.toString(), Get.snackbar('GPS hiba', e.toString(),
backgroundColor: Colors.red, colorText: Colors.white); backgroundColor: Colors.red, colorText: Colors.white);
stopRecording(); stopRecording();
}
}, },
); );
AppLogger.e('track_started', 'name:$trackName');
FirebaseLogger.event('track_started', {'name': trackName});
// Időmérő // Időmérő
_startElapsedTimer(); _startElapsedTimer();
@ -268,7 +279,9 @@ class TrackingController extends GetxController {
// Belső logika // Belső logika
Future<void> _onPosition(SourcePosition pos) async { Future<void> _onPosition(SourcePosition pos) async {
try {
if (isPaused.value) return; if (isPaused.value) return;
final sw = Stopwatch()..start();
final trackId = currentTrack.value?.id; final trackId = currentTrack.value?.id;
if (trackId == null) return; if (trackId == null) return;
@ -304,6 +317,18 @@ class TrackingController extends GetxController {
timestamp: pos.timestamp, timestamp: pos.timestamp,
); );
await _db.addPoint(point, _accumulatedDistance); await _db.addPoint(point, _accumulatedDistance);
sw.stop();
if (sw.elapsedMilliseconds > 300) {
AppLogger.w(
'on_Position',
'Lassú addPoint: ${sw.elapsedMilliseconds}ms, '
'pts: ${livePoints.length}');
FirebaseLogger.w(
'on_Position',
'Lassú addPoint: ${sw.elapsedMilliseconds}ms, '
'pts: ${livePoints.length}');
}
TrackSyncService.to.onNewPoint(point); TrackSyncService.to.onNewPoint(point);
@ -320,6 +345,10 @@ class TrackingController extends GetxController {
notificationText: elapsedFormatted.value, notificationText: elapsedFormatted.value,
); );
} }
} catch (e, stack) {
AppLogger.e('_onPosition', 'pont feldolgozási hiba - track folytatódik',
error: e, stack: stack);
}
} }
void _startElapsedTimer() { void _startElapsedTimer() {

View File

@ -14,6 +14,8 @@ import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:terepi_seged/services/firebase_logger.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import '../models/device_info_model.dart'; import '../models/device_info_model.dart';
@ -58,6 +60,11 @@ class DeviceIdentityService extends GetxService {
// Háttérben regisztrálás nem blokkolja az UI-t // Háttérben regisztrálás nem blokkolja az UI-t
unawaited(_registerDevice()); unawaited(_registerDevice());
_isReady = true; _isReady = true;
FirebaseLogger.setDeviceIdentifier(info.deviceId, deviceLabel.value);
FirebaseLogger.setKey('model', model);
FirebaseLogger.setKey('os_version', osInfo);
FirebaseLogger.setKey('app_version', appInfo);
} }
// Betöltés // Betöltés

View File

@ -0,0 +1,190 @@
// Firebase alapú log service Crashlytics + Analytics
//
// Crashlytics: breadcrumb log, hiba rögzítés, egyedi attribútumok
// Analytics: egyedi esemény tracking (warning-ok, hibák, fontos események)
//
// Éles appban is működik kiegészíti az AppLogger-t (ami csak debug módban fut)
//
// Használat:
// FirebaseLogger.i('startRecording', 'Track indítva: $name');
// FirebaseLogger.w('_onPosition', 'Ugrás kiszűrve: ${dist}m');
// FirebaseLogger.e('positionStream', 'GPS hiba', error: e, stack: s);
// FirebaseLogger.event('track_completed', {'distance_m': 3200, 'points': 142});
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
class FirebaseLogger extends GetxService {
static FirebaseLogger get to => Get.find();
// Belső referenciák
late final FirebaseCrashlytics _crashlytics;
late final FirebaseAnalytics _analytics;
bool _isReady = false;
// Lifecycle
@override
Future<void> onReady() async {
super.onReady();
try {
_crashlytics = FirebaseCrashlytics.instance;
_analytics = FirebaseAnalytics.instance;
// Crashlytics engedélyezése release-ben mindig, debug-ban opcionális
await _crashlytics.setCrashlyticsCollectionEnabled(!kDebugMode);
// Eszközazonosító beállítása ha már elérhető
_setDeviceInfo();
_isReady = true;
_crashlytics.log('FirebaseLogger inicializálva');
} catch (e) {
debugPrint('FirebaseLogger init hiba: $e');
}
}
// Publikus API ugyanolyan mint AppLogger
/// Info szintű log Crashlytics breadcrumb
static void i(String tag, String message) {
_breadcrumb('I', tag, message);
}
/// Figyelmeztetés breadcrumb + Analytics esemény
static void w(String tag, String message, {Object? error}) {
_breadcrumb('W', tag, message);
_logWarningEvent(tag, message, error: error);
}
/// Hiba Crashlytics recordError + Analytics esemény
static void e(
String tag,
String message, {
Object? error,
StackTrace? stack,
bool fatal = false,
}) {
_breadcrumb('E', tag, message);
if (error != null) {
try {
FirebaseLogger.to._crashlytics.recordError(
error,
stack,
reason: '[$tag] $message',
fatal: fatal,
printDetails: kDebugMode,
information: ['tag: $tag', 'message: $message'],
);
} catch (_) {}
}
_logErrorEvent(tag, message, error: error);
}
/// Szeparátor jól látható elválasztó a Crashlytics logban
static void separator(String label) {
_breadcrumb('', '─────', '─── $label ───────────────────────');
}
/// Egyedi Analytics esemény tetszőleges adatokkal
static void event(
String name, [
Map<String, Object>? parameters,
]) {
try {
FirebaseLogger.to._analytics.logEvent(
name: _sanitizeEventName(name),
parameters: parameters,
);
} catch (_) {}
}
/// Egyedi Crashlytics attribútum beállítása
static void setKey(String key, dynamic value) {
try {
FirebaseLogger.to._crashlytics.setCustomKey(key, value);
} catch (_) {}
}
// Eszközinfo beállítása
void _setDeviceInfo() {
try {
// DeviceIdentityService ha már elérhető
if (Get.isRegistered<dynamic>()) {
final deviceService = Get.find(tag: 'DeviceIdentityService');
_crashlytics.setUserIdentifier(deviceService.deviceId ?? '');
}
} catch (_) {
// DeviceIdentityService még nem regisztrált nem baj
}
}
/// Crashlytics azonosító frissítése ha a DeviceIdentityService betöltött
static void setDeviceIdentifier(String deviceId, String label) {
try {
FirebaseLogger.to._crashlytics.setUserIdentifier(deviceId);
FirebaseLogger.to._crashlytics.setCustomKey('device_label', label);
} catch (_) {}
}
// Belső segédek
static void _breadcrumb(String level, String tag, String message) {
try {
final line = '$level [$tag] $message';
FirebaseLogger.to._crashlytics.log(line);
} catch (_) {}
}
static void _logWarningEvent(
String tag,
String message, {
Object? error,
}) {
try {
FirebaseLogger.to._analytics.logEvent(
name: 'app_warning',
parameters: {
'tag': _trim(tag, 40),
'message': _trim(message, 100),
if (error != null) 'error': _trim(error.toString(), 100),
},
);
} catch (_) {}
}
static void _logErrorEvent(
String tag,
String message, {
Object? error,
}) {
try {
FirebaseLogger.to._analytics.logEvent(
name: 'app_error',
parameters: {
'tag': _trim(tag, 40),
'message': _trim(message, 100),
if (error != null) 'error': _trim(error.toString(), 100),
},
);
} catch (_) {}
}
/// Analytics eseménynevek csak betűt, számot és _ -t tartalmazhatnak
static String _sanitizeEventName(String name) =>
name.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_').substring(
0,
name.length.clamp(0, 40),
);
/// Analytics paraméter érték max 100 karakter
static String _trim(String s, int max) =>
s.length > max ? s.substring(0, max) : s;
}

View File

@ -41,10 +41,10 @@ dependencies:
flutter_map_location_marker: ^10.1.0 flutter_map_location_marker: ^10.1.0
path_provider: ^2.1.5 path_provider: ^2.1.5
rive: ^0.13.20 rive: ^0.13.20
firebase_core: ^3.12.0 firebase_core: ^4.11.0
firebase_auth: ^5.5.0 firebase_auth: ^6.5.4
cloud_firestore: ^5.6.4 cloud_firestore: ^6.6.0
firebase_storage: ^12.4.3 firebase_storage: ^13.4.3
firebase_crashlytics: ^5.2.4 firebase_crashlytics: ^5.2.4
firebase_analytics: ^12.4.3 firebase_analytics: ^12.4.3
google_sign_in: ^6.2.2 google_sign_in: ^6.2.2