Firebase analytics és crashlytics használatának beállítása a FirebaseLogger-ben.
This commit is contained in:
parent
ccf62aafce
commit
e126f340c6
@ -1,4 +1,7 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.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/routes/app_pages.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/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_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 {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
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 Supabase.initialize(
|
||||
@ -29,6 +45,8 @@ Future<void> main() async {
|
||||
await AppDatabase.instance.database;
|
||||
Get.put(ProjectService(), permanent: true);
|
||||
|
||||
Get.put(AppLogger(), permanent: true);
|
||||
Get.put(FirebaseLogger(), permanent: true);
|
||||
await Get.putAsync<CoordConverterService>(
|
||||
() => CoordConverterService().init());
|
||||
Get.put(GnssDeviceService());
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:latlong2/latlong.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/track_sync_service.dart';
|
||||
|
||||
@ -169,11 +173,18 @@ class TrackingController extends GetxController {
|
||||
_positionSub = _source!.positionStream.listen(
|
||||
_onPosition,
|
||||
onError: (e) {
|
||||
Get.snackbar('GPS hiba', e.toString(),
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
stopRecording();
|
||||
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(),
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
stopRecording();
|
||||
}
|
||||
},
|
||||
);
|
||||
AppLogger.e('track_started', 'name:$trackName');
|
||||
FirebaseLogger.event('track_started', {'name': trackName});
|
||||
|
||||
// Időmérő
|
||||
_startElapsedTimer();
|
||||
@ -268,57 +279,75 @@ class TrackingController extends GetxController {
|
||||
// ── Belső logika ───────────────────────────────────────────────────────────
|
||||
|
||||
Future<void> _onPosition(SourcePosition pos) async {
|
||||
if (isPaused.value) return;
|
||||
try {
|
||||
if (isPaused.value) return;
|
||||
final sw = Stopwatch()..start();
|
||||
|
||||
final trackId = currentTrack.value?.id;
|
||||
if (trackId == null) return;
|
||||
final trackId = currentTrack.value?.id;
|
||||
if (trackId == null) return;
|
||||
|
||||
// Távolság a legutóbbi ponttól
|
||||
double segmentDist = 0;
|
||||
if (_lastPoint != null) {
|
||||
segmentDist = haversineMeters(
|
||||
_lastPoint!.latitude,
|
||||
_lastPoint!.longitude,
|
||||
pos.latitude,
|
||||
pos.longitude,
|
||||
// Távolság a legutóbbi ponttól
|
||||
double segmentDist = 0;
|
||||
if (_lastPoint != null) {
|
||||
segmentDist = haversineMeters(
|
||||
_lastPoint!.latitude,
|
||||
_lastPoint!.longitude,
|
||||
pos.latitude,
|
||||
pos.longitude,
|
||||
);
|
||||
// Szűrés: ugrásszerű változás (pl. GPS lock elvesztése) ignorálása
|
||||
if (segmentDist > 100) return;
|
||||
}
|
||||
|
||||
_accumulatedDistance += segmentDist;
|
||||
sessionDistance.value = _accumulatedDistance;
|
||||
|
||||
// Sebesség km/h-ban
|
||||
currentSpeedKmh.value = (pos.speed ?? 0) * 3.6;
|
||||
|
||||
// Pont mentése
|
||||
final point = TrackPoint(
|
||||
trackId: trackId,
|
||||
latitude: pos.latitude,
|
||||
longitude: pos.longitude,
|
||||
altitude: pos.altitude,
|
||||
accuracy: pos.accuracy,
|
||||
speed: pos.speed,
|
||||
heading: pos.heading,
|
||||
timestamp: pos.timestamp,
|
||||
);
|
||||
// Szűrés: ugrásszerű változás (pl. GPS lock elvesztése) ignorálása
|
||||
if (segmentDist > 100) return;
|
||||
}
|
||||
await _db.addPoint(point, _accumulatedDistance);
|
||||
sw.stop();
|
||||
|
||||
_accumulatedDistance += segmentDist;
|
||||
sessionDistance.value = _accumulatedDistance;
|
||||
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}');
|
||||
}
|
||||
|
||||
// Sebesség km/h-ban
|
||||
currentSpeedKmh.value = (pos.speed ?? 0) * 3.6;
|
||||
TrackSyncService.to.onNewPoint(point);
|
||||
|
||||
// Pont mentése
|
||||
final point = TrackPoint(
|
||||
trackId: trackId,
|
||||
latitude: pos.latitude,
|
||||
longitude: pos.longitude,
|
||||
altitude: pos.altitude,
|
||||
accuracy: pos.accuracy,
|
||||
speed: pos.speed,
|
||||
heading: pos.heading,
|
||||
timestamp: pos.timestamp,
|
||||
);
|
||||
await _db.addPoint(point, _accumulatedDistance);
|
||||
_lastPoint = point;
|
||||
|
||||
TrackSyncService.to.onNewPoint(point);
|
||||
// UI frissítés
|
||||
livePoints.add(LatLng(pos.latitude, pos.longitude));
|
||||
|
||||
_lastPoint = point;
|
||||
|
||||
// UI frissítés
|
||||
livePoints.add(LatLng(pos.latitude, pos.longitude));
|
||||
|
||||
// Értesítés frissítése
|
||||
if (livePoints.length % 10 == 0) {
|
||||
final dist = _formatDistance(_accumulatedDistance);
|
||||
FlutterForegroundTask.updateService(
|
||||
notificationTitle: 'Track rögzítése – $dist',
|
||||
notificationText: elapsedFormatted.value,
|
||||
);
|
||||
// Értesítés frissítése
|
||||
if (livePoints.length % 10 == 0) {
|
||||
final dist = _formatDistance(_accumulatedDistance);
|
||||
FlutterForegroundTask.updateService(
|
||||
notificationTitle: 'Track rögzítése – $dist',
|
||||
notificationText: elapsedFormatted.value,
|
||||
);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
AppLogger.e('_onPosition', 'pont feldolgozási hiba - track folytatódik',
|
||||
error: e, stack: stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:get/get.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 '../models/device_info_model.dart';
|
||||
@ -58,6 +60,11 @@ class DeviceIdentityService extends GetxService {
|
||||
// Háttérben regisztrálás — nem blokkolja az UI-t
|
||||
unawaited(_registerDevice());
|
||||
_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 ──────────────────────────────────────────────────────
|
||||
|
||||
190
lib/services/firebase_logger.dart
Normal file
190
lib/services/firebase_logger.dart
Normal 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;
|
||||
}
|
||||
@ -41,10 +41,10 @@ dependencies:
|
||||
flutter_map_location_marker: ^10.1.0
|
||||
path_provider: ^2.1.5
|
||||
rive: ^0.13.20
|
||||
firebase_core: ^3.12.0
|
||||
firebase_auth: ^5.5.0
|
||||
cloud_firestore: ^5.6.4
|
||||
firebase_storage: ^12.4.3
|
||||
firebase_core: ^4.11.0
|
||||
firebase_auth: ^6.5.4
|
||||
cloud_firestore: ^6.6.0
|
||||
firebase_storage: ^13.4.3
|
||||
firebase_crashlytics: ^5.2.4
|
||||
firebase_analytics: ^12.4.3
|
||||
google_sign_in: ^6.2.2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user