123 lines
3.3 KiB
Dart
123 lines
3.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
/// Umfassendes Log-System für die Melo App.
|
|
/// Zeichnet ALLES auf: Aktionen, Fehler, Netzwerk, Performance.
|
|
class MeloLogger {
|
|
static final MeloLogger _instanz = MeloLogger._();
|
|
factory MeloLogger() => _instanz;
|
|
MeloLogger._();
|
|
|
|
bool _initialisiert = false;
|
|
String _sessionId = '';
|
|
String _version = '2.7';
|
|
final List<Map<String, dynamic>> _eintraege = [];
|
|
int _logId = 0;
|
|
Timer? _flushTimer;
|
|
|
|
static const String _serverUrl = 'http://159.195.51.99:8991/api/log';
|
|
|
|
void init(String version) {
|
|
if (_initialisiert) return;
|
|
_initialisiert = true;
|
|
_version = version;
|
|
_sessionId = DateTime.now().millisecondsSinceEpoch.toString();
|
|
|
|
_flushTimer = Timer.periodic(const Duration(seconds: 30), (_) => _senden());
|
|
_addEintrag('app', 'start', {'version': version, 'session': _sessionId});
|
|
|
|
FlutterError.onError = (details) {
|
|
FlutterError.presentError(details);
|
|
_addEintrag('crash', 'flutter_error', {
|
|
'fehler': details.exceptionAsString(),
|
|
'stack': details.stack.toString(),
|
|
});
|
|
};
|
|
|
|
PlatformDispatcher.instance.onError = (error, stack) {
|
|
_addEintrag('crash', 'dart_error', {
|
|
'fehler': error.toString(),
|
|
'stack': stack.toString(),
|
|
});
|
|
return true;
|
|
};
|
|
|
|
debugPrint('📋 MeloLogger ready (Session: $_sessionId)');
|
|
}
|
|
|
|
void _addEintrag(String kategorie, String aktion, [Map<String, dynamic>? details]) {
|
|
_eintraege.add({
|
|
'id': _logId++,
|
|
'zeit': DateTime.now().toIso8601String(),
|
|
'kategorie': kategorie,
|
|
'aktion': aktion,
|
|
'details': details ?? {},
|
|
});
|
|
}
|
|
|
|
void aktion(String name, [Map<String, dynamic>? details]) {
|
|
_addEintrag('aktion', name, details);
|
|
}
|
|
|
|
void netzwerk(String methode, String url, {int? statusCode, String? fehler, int? dauerMs}) {
|
|
_addEintrag('netzwerk', '$methode $url', {
|
|
if (statusCode != null) 'status': statusCode,
|
|
if (fehler != null) 'fehler': fehler,
|
|
if (dauerMs != null) 'dauer_ms': dauerMs,
|
|
});
|
|
}
|
|
|
|
void fehler(String kontext, Object? error, [StackTrace? stack]) {
|
|
_addEintrag('fehler', kontext, {
|
|
'error': error.toString(),
|
|
'stack': stack?.toString() ?? '',
|
|
});
|
|
_senden();
|
|
}
|
|
|
|
void performance(String name, int dauerMs) {
|
|
_addEintrag('performance', name, {'dauer_ms': dauerMs});
|
|
}
|
|
|
|
void zustand(String name, Map<String, dynamic> daten) {
|
|
_addEintrag('zustand', name, daten);
|
|
}
|
|
|
|
void manuell(String nachricht) {
|
|
_addEintrag('user', nachricht);
|
|
_senden();
|
|
}
|
|
|
|
Future<void> _senden() async {
|
|
if (_eintraege.isEmpty) return;
|
|
|
|
final batch = _eintraege.toList();
|
|
_eintraege.clear();
|
|
|
|
try {
|
|
await http.post(
|
|
Uri.parse(_serverUrl),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({
|
|
'typ': 'log_batch',
|
|
'session': _sessionId,
|
|
'version': _version,
|
|
'device': '${Platform.operatingSystem} ${Platform.operatingSystemVersion}',
|
|
'eintraege': batch,
|
|
}),
|
|
).timeout(const Duration(seconds: 5));
|
|
} catch (_) {
|
|
_eintraege.insertAll(0, batch);
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
_flushTimer?.cancel();
|
|
_senden();
|
|
}
|
|
}
|