Files
Melo/lib/services/logger_service.dart
T
Hermes (Server)andClaude Haiku 4.5 323a67e3a2 🐛 Fix: INTERNET-Permission fehlte im Release-Manifest
ROOT CAUSE für die fehlenden Logs — und für den fehlschlagenden
Navidrome-Login: Flutter injiziert android.permission.INTERNET nur in
debug/ und profile/AndroidManifest.xml. In main/AndroidManifest.xml
stand sie nicht, das Release-APK hatte damit gar keinen Netzzugriff.
Jede http-Anfrage schlug sofort mit SocketException fehl.

- INTERNET + ACCESS_NETWORK_STATE in main/AndroidManifest.xml ergänzt
- Login-Versuch/-Erfolg/-Fehlschlag im Settings-Dialog explizit geloggt
  (URL + User + ob das Passwortfeld leer war; das Passwort selbst nicht)
- Batch-Schwelle 20 -> 5, damit INFO-Logs auch ohne vorherigen Fehler
  hochgeladen werden

Nicht geaendert: der Sofort-Upload bei ERROR war bereits implementiert
(error() ruft _uploadIfError -> _uploadLogs direkt auf) und war nicht
die Ursache.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
2026-08-19 22:28:36 +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 = '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)
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();