Files
Melo/lib/services/logger_service.dart
T
Hermes (Server)andClaude Haiku 4.5 45141a5f05 📊 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
2026-08-19 21:32:32 +02:00

138 lines
3.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();