Files
plezy/lib/utils/app_logger.dart
edde746 5fe1e6dc65 refactor(ui): delete unwired log output, dead notifier filters, and never-passed widget params
MemoryLogOutput extended LogOutput but was never wired as a Logger output (storage happens in the printer); DeletionNotifier/WatchStateNotifier's forServer/forItem filtered streams and WatchStateEvent.mediaType had no production consumers (tests now filter .stream directly); context.hiddenLibraries/profileSettings and toPlexUrl had zero call sites; MonoTokens.splashFactory was never read; OptimizedMediaImage's enableTranscoding/cacheKey chains, FocusableMediaCard.width/height/forceGridMode, TvBrowseRail's constant-zero gap functions, and media_image_helper's scaleFactor were never varied by any caller. The sha1 LRU stays (rebuild-hot artwork URLs); TvRailTrailing.none and the mono-theme copyWith stay live (lerp delegates to copyWith). Also carries the MusicPlayContext.id argument drops in the music screens and remaining sweep test updates.
2026-08-17 19:02:25 +02:00

133 lines
3.9 KiB
Dart

import 'dart:collection';
import 'package:logger/logger.dart';
import 'log_redaction_manager.dart';
/// Redacts sensitive information from a log field.
String _redactSensitiveData(String value) => LogRedactionManager.redact(value);
/// Represents a single log entry stored in memory
class LogEntry {
final DateTime timestamp;
final Level level;
final String message;
final Object? error;
final StackTrace? stackTrace;
const LogEntry({required this.timestamp, required this.level, required this.message, this.error, this.stackTrace});
/// Estimate the memory size of this log entry in bytes
int get estimatedSize {
int size = 0;
size += 8;
size += 4;
size += message.length * 2;
if (error != null) {
size += error.toString().length * 2;
}
if (stackTrace != null) {
size += stackTrace.toString().length * 2;
}
return size;
}
}
/// In-memory log store with a circular buffer.
///
/// Storage is handled by [MemoryAwareLogPrinter.log()]; console output goes
/// through the logger's default [ConsoleOutput].
class MemoryLogOutput {
static const int maxLogSizeBytes = 5 * 1024 * 1024;
static final ListQueue<LogEntry> _logs = ListQueue<LogEntry>();
static int _currentSize = 0;
static List<LogEntry> getLogs() => _logs.toList().reversed.toList();
static void clearLogs() {
_logs.clear();
_currentSize = 0;
}
}
/// Custom log printer that also stores error and stack trace information
class MemoryAwareLogPrinter extends LogPrinter {
final LogPrinter _wrappedPrinter;
MemoryAwareLogPrinter(this._wrappedPrinter);
@override
List<String> log(LogEvent event) {
// Store the log with error and stack trace if available
final message = _redactSensitiveData(event.message.toString());
final error = event.error != null ? _redactSensitiveData(event.error.toString()) : null;
final stackTrace = event.stackTrace != null
? StackTrace.fromString(_redactSensitiveData(event.stackTrace.toString()))
: null;
final logEntry = LogEntry(
timestamp: DateTime.now(),
level: event.level,
message: message,
error: error,
stackTrace: stackTrace,
);
MemoryLogOutput._logs.add(logEntry);
MemoryLogOutput._currentSize += logEntry.estimatedSize;
// Maintain buffer size limit (remove oldest entries) — O(1) with ListQueue
while (MemoryLogOutput._currentSize > MemoryLogOutput.maxLogSizeBytes && MemoryLogOutput._logs.isNotEmpty) {
final removed = MemoryLogOutput._logs.removeFirst();
MemoryLogOutput._currentSize -= removed.estimatedSize;
}
return _wrappedPrinter.log(LogEvent(event.level, message, time: event.time, error: error, stackTrace: stackTrace));
}
}
/// Custom production filter that respects our level setting even in release mode
class ProductionFilter extends LogFilter {
Level _currentLevel = Level.debug;
void setLevel(Level level) {
_currentLevel = level;
}
@override
bool shouldLog(LogEvent event) {
return event.level.value >= _currentLevel.value;
}
}
final _productionFilter = ProductionFilter();
/// Centralized logger instance for the application.
///
/// Usage:
/// ```dart
/// import 'package:plezy/utils/app_logger.dart';
///
/// appLogger.d('Debug message');
/// appLogger.i('Info message');
/// appLogger.w('Warning message');
/// appLogger.e('Error message', error: e, stackTrace: stackTrace);
/// ```
Logger appLogger = Logger(
printer: MemoryAwareLogPrinter(SimplePrinter()),
filter: _productionFilter,
level: Level.debug,
);
/// Update the logger's level dynamically based on debug setting
/// Recreates the logger instance to ensure it works in release mode
void setLoggerLevel(bool debugEnabled) {
final newLevel = debugEnabled ? Level.debug : Level.info;
_productionFilter.setLevel(newLevel);
appLogger = Logger(printer: MemoryAwareLogPrinter(SimplePrinter()), filter: _productionFilter, level: newLevel);
Logger.level = newLevel;
}