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.
133 lines
3.9 KiB
Dart
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;
|
|
}
|