📊 Feature: Auto-Bugreport System (Logger Service)

Flutter-App Seite:
- LoggerService: Centralized Logging (DEBUG/INFO/WARNING/ERROR)
- Automatic Upload zu logs.baka-net.de/api/logs
  - Fehler sofort senden
  - Andere Logs collected (batch von 20)
- Device-Info: Model + OS-Version tracken
- Integration in NavidromeService: Login-Fehler loggen + URL + Status
- Global logger Instance für überall nutzbar

Auto-Features:
 Timestamp + Device-Info + Error Stack-Traces
 Auto-Upload nach 20 Logs oder sofort bei Fehler
 JSON-Format für Server-Verarbeitung

Nächste Schritte:
- Log-Server deployen (FastAPI, SQLite, HTML-Viewer)
- Caddy-Route für logs.baka-net.de
- Basic Auth für Log-Seite

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
This commit is contained in:
Hermes (Server)
2026-08-19 21:32:32 +02:00
co-authored by Claude Haiku 4.5
parent 42be85a552
commit 45141a5f05
3 changed files with 148 additions and 1 deletions
+137
View File
@@ -0,0 +1,137 @@
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 = 'http://logs.baka-net.de/api/logs';
static const int _batchSize = 20;
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)
void error(String message, [Object? error, StackTrace? stackTrace]) {
final entry = LogEntry(
level: 'ERROR',
message: message,
stackTrace: '$error\n$stackTrace',
deviceModel: _deviceModel,
);
_logs.add(entry);
debugPrint('❌ ERROR: $message');
_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();