LCOV - code coverage report
Current view: top level - src - the_logger.dart (source / functions) Coverage Total Hit
Test: filtered_lcov.info Lines: 90.2 % 82 74
Test Date: 2024-09-16 09:22:39 Functions: - 0 0

            Line data    Source code
       1              : import 'package:collection/collection.dart';
       2              : import 'package:flutter/foundation.dart';
       3              : import 'package:logging/logging.dart';
       4              : import 'package:the_logger/src/abstract_logger.dart';
       5              : import 'package:the_logger/src/console_logger.dart';
       6              : import 'package:the_logger/src/db_logger.dart';
       7              : import 'package:the_logger/src/models/models.dart';
       8              : 
       9              : export 'abstract_logger.dart';
      10              : export 'models/console_colors.dart';
      11              : export 'models/masked_log_record.dart';
      12              : export 'models/masking_string.dart';
      13              : 
      14              : /// The Logger: modularity, extensibility, testability
      15              : class TheLogger {
      16              :   /// Get logger instance
      17            4 :   factory TheLogger.i() {
      18            4 :     _instance ??= TheLogger._();
      19              :     return _instance!;
      20              :   }
      21            4 :   TheLogger._();
      22              : 
      23              :   static TheLogger? _instance;
      24              : 
      25              :   final _log = Logger('TheLogger');
      26            0 :   late List<AbstractLogger> _loggers = [];
      27              :   Level _minLevel = Level.ALL;
      28              :   String? _sessionStartExtra;
      29              :   late Level _sessionStartLevel;
      30              : 
      31              :   final MaskingStrings _maskingStrings = {};
      32              : 
      33              :   bool _initialized = false;
      34              : 
      35              :   /// Init app logger
      36              :   ///
      37              :   /// [retainStrategy] processing algorythm:
      38              :   /// * sort all records by level (ALL->OFF)
      39              :   /// * record with minimum level will be used as global filter
      40              :   ///   (for storing and printing)
      41              :   /// * each integer for a level means how many sessions the records with this
      42              :   ///   level will be retained
      43              :   /// * each next entry will add this number
      44              :   /// * if [retainStrategy] is empty => {Level.ALL: 10}
      45              :   /// So, examples:
      46              :   ///
      47              :   /// {
      48              :   ///   Level.ALL:      200,  // ALL records will be deleted after 200 sessions
      49              :   ///   Level.INFO:     100,  // records with INFO and higher level retained for 300 sessions
      50              :   ///   Level.SEVERE:   50,   // records with SEVERE and higher level retained for 350 sessions
      51              :   /// }
      52              :   ///
      53              :   /// {
      54              :   ///   Level.CONFIG:   200,  // records with CONFIG and higher level retained for 200 sessions
      55              :   ///                         // lower level records (FINE, FINER and FINEST) will not
      56              :   ///                         // be printed nor stored because lowest level in the map
      57              :   ///                         // is CONFIG
      58              :   ///   Level.INFO:     100,  // records with INFO and higher level retained for 300 sessions
      59              :   ///   Level.SEVERE:   50,   // records with SEVERE and higher level retained for 350 sessions
      60              :   /// }
      61              :   ///
      62              :   /// {
      63              :   ///   Level.OFF:   0,       // disable logging
      64              :   /// }
      65              :   ///
      66              :   /// {
      67              :   ///   Level.ALL:   1,       // all level records will be retained for 1 session
      68              :   ///                         // (i.e. you will be able to retrieve the logs from the
      69              :   ///                         // previous run)
      70              :   /// }
      71              :   /// [startNewSession] - if true, new session will be started
      72              :   /// [consoleLogger] - if true, console logger will be used
      73              :   /// [dbLogger] - if true, db logger will be used
      74              :   /// [maskDbLogger] - mask sensitive data in db logger
      75              :   /// [consoleLoggerCallback] - callback for console logger
      76              :   /// [consoleColors] - console colors
      77              :   /// [consoleFormatJson] - format JSON in console logger
      78              :   /// [sessionStartExtra] - extra info for session start, will be added to all
      79              :   /// session start records
      80              :   /// [customLoggers] - custom loggers
      81              :   /// [sessionStartLevel] - session start log level
      82            4 :   Future<void> init({
      83              :     Map<Level, int> retainStrategy = const {},
      84              :     bool startNewSession = true,
      85              :     bool consoleLogger = true,
      86              :     bool dbLogger = true,
      87              :     bool maskDbLogger = true,
      88              :     ConsoleLoggerCallback? consoleLoggerCallback,
      89              :     ConsoleColors consoleColors = const ConsoleColors(),
      90              :     bool consoleFormatJson = true,
      91              :     String? sessionStartExtra,
      92              :     List<AbstractLogger>? customLoggers,
      93              :     Level sessionStartLevel = Level.INFO,
      94              :   }) async {
      95            4 :     if (_initialized) {
      96            0 :       _log.warning('TheLogger is already initialized!');
      97              :       return;
      98              :     }
      99              : 
     100            4 :     _initialized = true;
     101              : 
     102            4 :     _sessionStartExtra = sessionStartExtra;
     103            4 :     _sessionStartLevel = sessionStartLevel;
     104              : 
     105              :     // If there are no explicit instructions on how to retain logs
     106              :     final retainStrategyNotEmpty =
     107            8 :         retainStrategy.isEmpty ? _defaultRetainStrategy() : retainStrategy;
     108              : 
     109           12 :     _minLevel = retainStrategyNotEmpty.keys.reduce(
     110            3 :       (value, element) => element.compareTo(value) < 0 ? element : value,
     111              :     );
     112              : 
     113           12 :     Logger.root.level = _minLevel;
     114            8 :     _loggers = <AbstractLogger>[
     115              :       if (consoleLogger)
     116            4 :         ConsoleLogger(
     117              :           loggerCallback: consoleLoggerCallback,
     118              :           colors: consoleColors,
     119              :           formatJson: consoleFormatJson,
     120              :         ),
     121            2 :       if (dbLogger) DbLogger(),
     122            7 :       ...customLoggers ?? [],
     123              :     ];
     124              : 
     125            8 :     for (final logger in _loggers) {
     126            4 :       await logger.init(retainStrategyNotEmpty);
     127            4 :       if (logger is DbLogger) {
     128            2 :         logger.shouldMask = maskDbLogger;
     129              :       }
     130              :     }
     131              : 
     132            8 :     Logger.root.clearListeners();
     133           16 :     Logger.root.onRecord.listen(_writeRecord);
     134              : 
     135            1 :     if (startNewSession) await startSession();
     136              :   }
     137              : 
     138              :   /// Dispose logger
     139            4 :   Future<void> dispose() async {
     140            4 :     _assureInitialized();
     141              : 
     142              :     _instance = null;
     143              :   }
     144              : 
     145              :   /// Get computed minimal level
     146            0 :   Level get minLevel => _minLevel;
     147              : 
     148            4 :   void _writeRecord(LogRecord record) {
     149            4 :     final maskedRecord = MaskedLogRecord.fromLogRecord(
     150              :       record,
     151            4 :       maskingStrings: _maskingStrings,
     152              :     );
     153            8 :     for (final logger in _loggers) {
     154            4 :       logger.write(maskedRecord);
     155              :     }
     156              :   }
     157              : 
     158              :   /// Increment session id
     159            4 :   Future<void> startSession() async {
     160            4 :     _assureInitialized();
     161              : 
     162            4 :     final logStrings = <String>[];
     163            8 :     for (final logger in _loggers) {
     164            4 :       final logString = await logger.sessionStart();
     165              :       if (logString != null) {
     166            3 :         logStrings.add(logString);
     167              :       }
     168              :     }
     169              : 
     170            4 :     final logStringsReduced = logStrings.fold(
     171              :       '',
     172            6 :       (value, element) => value = '$value$element',
     173              :     );
     174              : 
     175              :     final extraString =
     176            8 :         _sessionStartExtra == null ? '' : ' $_sessionStartExtra';
     177            8 :     _log.log(
     178            4 :       _sessionStartLevel,
     179            4 :       'Session start $logStringsReduced$extraString',
     180              :     );
     181              :   }
     182              : 
     183              :   /// Get all logs as strings (for debug purposes only)
     184            1 :   Future<String> getAllLogsAsString() async {
     185            1 :     _assureInitialized();
     186              : 
     187            2 :     return _dbLogger.getAllLogsAsString();
     188              :   }
     189              : 
     190              :   /// Get all logs as [LogRecord]s (for debug purposes only)
     191            0 :   @visibleForTesting
     192              :   Future<List<LogRecord>> getAllLogs() async {
     193            0 :     _assureInitialized();
     194              : 
     195            0 :     return _dbLogger.getAllLogs();
     196              :   }
     197              : 
     198              :   /// Get all logs as maps (for debug purposes only)
     199            2 :   @visibleForTesting
     200              :   Future<List<Map<String, Object?>>> getAllLogsAsMaps() async {
     201            2 :     _assureInitialized();
     202              : 
     203            4 :     return _dbLogger.getAllLogsAsMaps();
     204              :   }
     205              : 
     206              :   /// Write logs to archived JSON, return file path
     207            1 :   Future<String> writeAllLogsToJson([String filename = 'logs.json']) async {
     208            1 :     _assureInitialized();
     209              : 
     210            2 :     return _dbLogger.writeAllLogsToJson(filename);
     211              :   }
     212              : 
     213              :   /// Clear logs (for debug purposes only)
     214            2 :   Future<void> clearAllLogs() {
     215            2 :     _assureInitialized();
     216              : 
     217            4 :     return _dbLogger.clearAllLogs();
     218              :   }
     219              : 
     220              :   /// Add masking string
     221            2 :   void addMaskingString(MaskingString maskingString) {
     222            2 :     _assureInitialized();
     223              : 
     224            4 :     addMaskingStrings({maskingString});
     225              :   }
     226              : 
     227              :   /// Add masking strings
     228            2 :   void addMaskingStrings(MaskingStrings maskingStrings) {
     229            2 :     _assureInitialized();
     230              : 
     231            4 :     _maskingStrings.addAll(maskingStrings);
     232              :   }
     233              : 
     234              :   /// Remove masking string
     235            1 :   void removeMaskingString(MaskingString maskingString) {
     236            1 :     _assureInitialized();
     237              : 
     238            2 :     removeMaskingStrings({maskingString});
     239              :   }
     240              : 
     241              :   /// Remove masking strings
     242            1 :   void removeMaskingStrings(MaskingStrings maskingStrings) {
     243            1 :     _assureInitialized();
     244              : 
     245            2 :     _maskingStrings.removeAll(maskingStrings);
     246              :   }
     247              : 
     248              :   /// Clear masking strings
     249            1 :   void clearMaskingStrings() {
     250            1 :     _assureInitialized();
     251              : 
     252            2 :     _maskingStrings.clear();
     253              :   }
     254              : 
     255            2 :   DbLogger get _dbLogger {
     256              :     final dbLogger =
     257            8 :         _loggers.firstWhereOrNull((logger) => logger is DbLogger) as DbLogger?;
     258              :     if (dbLogger == null) {
     259            0 :       throw Exception('DbLogger is not initialized!');
     260              :     }
     261              : 
     262              :     return dbLogger;
     263              :   }
     264              : 
     265              :   /// Default retain strategy
     266            8 :   Map<Level, int> _defaultRetainStrategy() => {Level.ALL: 10};
     267              : 
     268            4 :   void _assureInitialized() {
     269            4 :     if (!_initialized) {
     270            0 :       throw Exception('TheLogger is not initialized!');
     271              :     }
     272              :   }
     273              : }
        

Generated by: LCOV version 2.1-1