Files
plezy/lib/utils/app_logger.dart
T

133 lines
3.9 KiB
Dart
Raw Normal View History

2026-03-08 16:37:28 +01:00
import 'dart:collection';
2025-10-18 00:05:20 +02:00
import 'package:logger/logger.dart';
2025-11-14 23:37:26 +01:00
import 'log_redaction_manager.dart';
2025-11-05 08:19:50 +01:00
/// Redacts sensitive information from a log field.
String _redactSensitiveData(String value) => LogRedactionManager.redact(value);
2025-11-05 08:19:50 +01:00
/// 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});
2025-11-05 08:19:50 +01:00
/// 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.
2026-03-08 16:37:28 +01:00
///
/// Storage is handled by [MemoryAwareLogPrinter.log()]; console output goes
/// through the logger's default [ConsoleOutput].
class MemoryLogOutput {
2026-05-04 22:39:17 +02:00
static const int maxLogSizeBytes = 5 * 1024 * 1024;
2026-03-08 16:37:28 +01:00
static final ListQueue<LogEntry> _logs = ListQueue<LogEntry>();
2025-11-05 08:19:50 +01:00
static int _currentSize = 0;
2026-03-08 16:37:28 +01:00
static List<LogEntry> getLogs() => _logs.toList().reversed.toList();
2025-11-05 08:19:50 +01:00
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());
2025-12-15 02:29:03 +01:00
final error = event.error != null ? _redactSensitiveData(event.error.toString()) : null;
final stackTrace = event.stackTrace != null
? StackTrace.fromString(_redactSensitiveData(event.stackTrace.toString()))
: null;
2025-11-05 08:19:50 +01:00
final logEntry = LogEntry(
timestamp: DateTime.now(),
level: event.level,
message: message,
error: error,
stackTrace: stackTrace,
2025-11-05 08:19:50 +01:00
);
MemoryLogOutput._logs.add(logEntry);
MemoryLogOutput._currentSize += logEntry.estimatedSize;
2026-03-08 16:37:28 +01:00
// Maintain buffer size limit (remove oldest entries) — O(1) with ListQueue
2025-12-15 02:29:03 +01:00
while (MemoryLogOutput._currentSize > MemoryLogOutput.maxLogSizeBytes && MemoryLogOutput._logs.isNotEmpty) {
2026-03-08 16:37:28 +01:00
final removed = MemoryLogOutput._logs.removeFirst();
2025-11-05 08:19:50 +01:00
MemoryLogOutput._currentSize -= removed.estimatedSize;
}
return _wrappedPrinter.log(LogEvent(event.level, message, time: event.time, error: error, stackTrace: stackTrace));
2025-11-05 08:19:50 +01:00
}
}
/// 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();
2025-10-18 00:05:20 +02:00
/// 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);
/// ```
2025-11-05 08:19:50 +01:00
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);
2025-12-15 02:29:03 +01:00
appLogger = Logger(printer: MemoryAwareLogPrinter(SimplePrinter()), filter: _productionFilter, level: newLevel);
2025-11-05 08:19:50 +01:00
Logger.level = newLevel;
}