- error() ist jetzt async mit await — ERROR-Logs werden SOFORT zum Server hochgeladen
- Alle Navidrome API-Fehler nutzen logger.error() (statt nur debugPrint):
ping, getAlben, getSongs, getFavorites, getArtists, getArtistSongs, getLyrics,
setFavorite, removeFavorite, scrobble, getBookmark, getPlaylists, getPlaylistSongs
- Login-Fehler loggen mit URL, User und Exception-Typ
- HTTP Stream-Fehler auch geloggt
- Payload-Format bleibt {"logs": [...]} Array (https://logs.baka-net.de/api/logs)
Alle 70 Tests bestanden ✅
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiRmGyzAMkzHMqBsBe2h3n
138 lines
3.6 KiB
Dart
138 lines
3.6 KiB
Dart
import 'package:flutter/foundation.dart';
|
||
import 'package:http/http.dart' as http;
|
||
import 'package:device_info_plus/device_info_plus.dart';
|
||
import 'dart:convert';
|
||
import 'dart:io';
|
||
|
||
class LogEntry {
|
||
final String level; // DEBUG, INFO, WARNING, ERROR
|
||
final String message;
|
||
final String? stackTrace;
|
||
final DateTime timestamp;
|
||
final String? userAgent;
|
||
final String? deviceModel;
|
||
|
||
LogEntry({
|
||
required this.level,
|
||
required this.message,
|
||
this.stackTrace,
|
||
DateTime? timestamp,
|
||
this.userAgent,
|
||
this.deviceModel,
|
||
}) : timestamp = timestamp ?? DateTime.now();
|
||
|
||
Map<String, dynamic> toJson() => {
|
||
'level': level,
|
||
'message': message,
|
||
'stackTrace': stackTrace,
|
||
'timestamp': timestamp.toIso8601String(),
|
||
'userAgent': userAgent,
|
||
'deviceModel': deviceModel,
|
||
};
|
||
}
|
||
|
||
/// Centralized Logging Service mit Auto-Upload zu Server
|
||
class LoggerService extends ChangeNotifier {
|
||
static const String _logServerUrl = 'https://logs.baka-net.de/api/logs';
|
||
static const int _batchSize = 5;
|
||
|
||
final List<LogEntry> _logs = [];
|
||
late final String _deviceModel;
|
||
bool _initialized = false;
|
||
|
||
Future<void> init() async {
|
||
if (_initialized) return;
|
||
try {
|
||
final deviceInfo = DeviceInfoPlugin();
|
||
if (Platform.isAndroid) {
|
||
final info = await deviceInfo.androidInfo;
|
||
_deviceModel = '${info.manufacturer} ${info.model}';
|
||
} else {
|
||
_deviceModel = 'Unknown Device';
|
||
}
|
||
} catch (e) {
|
||
_deviceModel = 'Unknown Device';
|
||
}
|
||
_initialized = true;
|
||
debugPrint('✅ LoggerService initialized for $_deviceModel');
|
||
}
|
||
|
||
/// Logge einen Fehler (wird sofort zum Server geschickt)
|
||
Future<void> error(String message, [Object? error, StackTrace? stackTrace]) async {
|
||
final entry = LogEntry(
|
||
level: 'ERROR',
|
||
message: message,
|
||
stackTrace: '$error\n$stackTrace',
|
||
deviceModel: _deviceModel,
|
||
);
|
||
_logs.add(entry);
|
||
debugPrint('❌ ERROR: $message');
|
||
await _uploadIfError(entry);
|
||
}
|
||
|
||
/// Logge eine Warnung
|
||
void warning(String message) {
|
||
final entry = LogEntry(
|
||
level: 'WARNING',
|
||
message: message,
|
||
deviceModel: _deviceModel,
|
||
);
|
||
_logs.add(entry);
|
||
debugPrint('⚠️ WARNING: $message');
|
||
_checkAndFlush();
|
||
}
|
||
|
||
/// Logge Info-Event
|
||
void info(String message) {
|
||
final entry = LogEntry(
|
||
level: 'INFO',
|
||
message: message,
|
||
deviceModel: _deviceModel,
|
||
);
|
||
_logs.add(entry);
|
||
debugPrint('ℹ️ INFO: $message');
|
||
_checkAndFlush();
|
||
}
|
||
|
||
void _checkAndFlush() {
|
||
if (_logs.length >= _batchSize) {
|
||
_uploadLogs();
|
||
}
|
||
}
|
||
|
||
Future<void> _uploadIfError(LogEntry entry) async {
|
||
debugPrint('📤 Uploading ERROR immediately: ${entry.message}');
|
||
await _uploadLogs();
|
||
}
|
||
|
||
/// Hochladen der Logs zum Server
|
||
Future<void> _uploadLogs() async {
|
||
if (_logs.isEmpty) return;
|
||
try {
|
||
final logsToUpload = List<LogEntry>.from(_logs);
|
||
await http.post(
|
||
Uri.parse(_logServerUrl),
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: jsonEncode({
|
||
'logs': logsToUpload.map((l) => l.toJson()).toList(),
|
||
'timestamp': DateTime.now().toIso8601String(),
|
||
}),
|
||
).timeout(const Duration(seconds: 10));
|
||
_logs.clear();
|
||
debugPrint('✅ Logs uploaded successfully (${logsToUpload.length} entries)');
|
||
} catch (e) {
|
||
debugPrint('⚠️ Failed to upload logs: $e');
|
||
}
|
||
}
|
||
|
||
/// Manuelles Flushen (z.B. vor App-Exit)
|
||
Future<void> flush() async {
|
||
await _uploadLogs();
|
||
}
|
||
|
||
int get logCount => _logs.length;
|
||
}
|
||
|
||
/// Global Logger Instance
|
||
final logger = LoggerService();
|