import 'dart:io'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; class LogManager { static LogManager? _instance; final Logger _logger; late IOSink? _logSink; factory LogManager.getInstance() { _instance ??= LogManager._internal(); return _instance!; } LogManager._internal() : _logger = Logger('MumbleBullet') { _logSink = null; } void initialize({ required Level level, required bool logToConsole, String? logFilePath, int maxLogFiles = 5, int maxLogSizeBytes = 1024 * 1024, // 1MB }) { Logger.root.level = level; if (logToConsole) { Logger.root.onRecord.listen((record) { stdout.writeln('${record.time}: ${record.level.name}: ${record.message}'); if (record.error != null) { stdout.writeln('Error: ${record.error}'); } if (record.stackTrace != null) { stdout.writeln('Stack trace: ${record.stackTrace}'); } }); } if (logFilePath != null) { _setupFileLogging(logFilePath: logFilePath, maxLogFiles: maxLogFiles, maxLogSizeBytes: maxLogSizeBytes); } } void _setupFileLogging({required String logFilePath, required int maxLogFiles, required int maxLogSizeBytes}) { final directory = path.dirname(logFilePath); final dirFile = Directory(directory); if (!dirFile.existsSync()) { dirFile.createSync(recursive: true); } // Check if log rotation is needed final logFile = File(logFilePath); if (logFile.existsSync() && logFile.lengthSync() > maxLogSizeBytes) { _rotateLogFiles(logFilePath, maxLogFiles); } _logSink = logFile.openWrite(mode: FileMode.append); Logger.root.onRecord.listen((record) { _logSink?.writeln('${record.time}: ${record.level.name}: ${record.message}'); if (record.error != null) { _logSink?.writeln('Error: ${record.error}'); } if (record.stackTrace != null) { _logSink?.writeln('Stack trace: ${record.stackTrace}'); } }); } void _rotateLogFiles(String logFilePath, int maxLogFiles) { // Remove oldest log file if maxLogFiles is reached for (var i = maxLogFiles; i >= 1; i--) { final oldFile = File('$logFilePath.$i'); if (oldFile.existsSync() && i == maxLogFiles) { oldFile.deleteSync(); } else if (oldFile.existsSync()) { oldFile.renameSync('$logFilePath.${i + 1}'); } } // Rename current log file to .1 final currentFile = File(logFilePath); if (currentFile.existsSync()) { currentFile.renameSync('$logFilePath.1'); } } void close() { _logSink?.flush(); _logSink?.close(); _logSink = null; } Logger get logger => _logger; void debug(String message, [Object? error, StackTrace? stackTrace]) { _logger.fine(message, error, stackTrace); } void info(String message, [Object? error, StackTrace? stackTrace]) { _logger.info(message, error, stackTrace); } void warning(String message, [Object? error, StackTrace? stackTrace]) { _logger.warning(message, error, stackTrace); } void error(String message, [Object? error, StackTrace? stackTrace]) { _logger.severe(message, error, stackTrace); } }