├── logo.png ├── test ├── test_utils │ ├── platform.dart │ ├── utils.dart │ ├── frame_factory.dart │ └── stack_trace_factory.dart ├── outputs │ └── dump_output_test.dart ├── roggle_test.dart └── printers │ ├── crashlytics_printer_test.dart │ └── single_pretty_printer_test.dart ├── lib ├── src │ ├── outputs │ │ └── dump_output.dart │ ├── printers │ │ ├── crashlytics_printer.dart │ │ └── single_pretty_printer.dart │ └── roggle.dart └── roggle.dart ├── .github ├── dependabot.yml └── workflows │ ├── publish.yaml │ └── test.yaml ├── .run ├── main.run.xml └── example_main.dart - release.run.xml ├── .metadata ├── analysis_options.yaml ├── .vscode └── launch.json ├── Makefile ├── pubspec.yaml ├── .gitignore ├── LICENSE ├── example └── main.dart ├── CHANGELOG.md └── README.md /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/roggle/HEAD/logo.png -------------------------------------------------------------------------------- /test/test_utils/platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:path/path.dart' as path; 2 | 3 | bool kIsWeb = path.Style.platform == path.Style.url; 4 | bool kIsWindows = path.Style.platform == path.Style.windows; 5 | -------------------------------------------------------------------------------- /lib/src/outputs/dump_output.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | /// Do nothing 4 | class DumpOutput extends LogOutput { 5 | @override 6 | void output(OutputEvent event) { 7 | // dump event 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/test_utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | List getAvailableLogLevel() => [ 4 | Level.trace, 5 | Level.debug, 6 | Level.info, 7 | Level.warning, 8 | Level.error, 9 | Level.fatal, 10 | ]; 11 | -------------------------------------------------------------------------------- /lib/roggle.dart: -------------------------------------------------------------------------------- 1 | export 'package:logger/logger.dart' hide LogCallback, Logger, OutputCallback; 2 | 3 | export 'src/outputs/dump_output.dart'; 4 | export 'src/printers/crashlytics_printer.dart'; 5 | export 'src/printers/single_pretty_printer.dart'; 6 | export 'src/roggle.dart'; 7 | -------------------------------------------------------------------------------- /test/outputs/dump_output_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:roggle/roggle.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('output()', () { 6 | final output = DumpOutput(); 7 | final logEvent = LogEvent(Level.error, ''); 8 | output.output(OutputEvent(logEvent, [])); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/ja/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 2 | 3 | version: 2 4 | enable-beta-ecosystems: true 5 | updates: 6 | - package-ecosystem: "pub" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | -------------------------------------------------------------------------------- /.run/main.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5464c5bac742001448fe4fc0597be939379f88ea 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | #include: package:lints/recommended.yaml 2 | # https://pub.dev/packages/pedantic_mono 3 | include: package:pedantic_mono/analysis_options.yaml 4 | 5 | linter: 6 | rules: 7 | avoid_classes_with_only_static_members: false 8 | constant_identifier_names: true 9 | prefer_relative_imports: true 10 | cascade_invocations: false 11 | -------------------------------------------------------------------------------- /.run/example_main.dart - release.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "example-debug", 6 | "program": "example/main.dart", 7 | "request": "launch", 8 | "type": "dart", 9 | "args": [ 10 | "--enable-asserts", 11 | ] 12 | }, 13 | { 14 | "name": "example-release", 15 | "program": "example/main.dart", 16 | "request": "launch", 17 | "type": "dart", 18 | "args": [ 19 | "--no-enable-asserts", 20 | ] 21 | }, 22 | ] 23 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: get-dependences 2 | get-dependences: 3 | dart pub get 4 | 5 | .PHONY: test 6 | test: 7 | dart format --set-exit-if-changed . 8 | dart analyze --fatal-infos --fatal-warnings lib 9 | dart test --coverage=coverage 10 | dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib 11 | genhtml coverage/lcov.info -o coverage/html 12 | open coverage/html/index.html 13 | 14 | .PHONY: doc 15 | doc: 16 | rm -rf doc/api 17 | dartdoc 18 | open doc/api/index.html 19 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: roggle 2 | description: Simple, colorful and easy to expand logger for dart. Inspired by the logger and dependent to the logger. 3 | 4 | version: '0.4.2+1' 5 | homepage: https://github.com/susatthi/roggle 6 | 7 | environment: 8 | sdk: ">=2.17.0 <4.0.0" 9 | 10 | dependencies: 11 | logger: "2.0.2+1" 12 | meta: ^1.7.0 13 | stack_trace: ^1.10.0 14 | 15 | dev_dependencies: 16 | coverage: ^1.2.0 17 | path: ^1.8.3 18 | pedantic_mono: ^1.14.0+1 19 | test: ^1.21.4 20 | 21 | platforms: 22 | android: 23 | ios: 24 | linux: 25 | macos: 26 | web: 27 | windows: 28 | 29 | screenshots: 30 | - description: 'Roggle logo.' 31 | path: 'logo.png' 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | 31 | # Customize 32 | coverage/ 33 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish to pub.dev 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | publish: 10 | permissions: 11 | id-token: write 12 | contents: write 13 | actions: write 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 10 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 19 | - name: Install dependencies 20 | run: dart pub get 21 | - name: Publish to pub.dev 22 | run: dart pub publish -f 23 | - name: Create release 24 | uses: actions/create-release@v1 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | with: 28 | tag_name: ${{ github.ref }} 29 | release_name: ${{ github.ref }} 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Keyber Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test_utils/frame_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:stack_trace/stack_trace.dart'; 2 | 3 | class FrameFactory { 4 | /// Dart on Mac 5 | static Frame dartMac() => Frame( 6 | Uri.parse('file:///Users/dummy/Develop/roggle/example/main.dart'), 7 | 66, 8 | 10, 9 | 'dummy', 10 | ); 11 | 12 | /// Dart on Windows 13 | static Frame dartWindows() => Frame( 14 | Uri.parse('file:///C:/Users/dummy/Develop/roggle/example/main.dart'), 15 | 66, 16 | 10, 17 | 'dummy', 18 | ); 19 | 20 | /// Flutter on Web 21 | static Frame flutterWeb() => Frame( 22 | Uri.parse( 23 | 'http://localhost:62180/packages/flutter_sample_roggle/main.dart', 24 | ), 25 | 66, 26 | 10, 27 | 'dummy', 28 | ); 29 | 30 | /// Flutter on Android 31 | static Frame flutterAndroid() => Frame( 32 | Uri.parse('package:flutter_sample_roggle/main.dart'), 33 | 66, 34 | 10, 35 | 'dummy', 36 | ); 37 | 38 | /// Flutter on iOS 39 | static Frame flutterIOS() => Frame( 40 | Uri.parse('package:flutter_sample_roggle/main.dart'), 41 | 66, 42 | 10, 43 | 'dummy', 44 | ); 45 | 46 | /// Flutter on Mac 47 | static Frame flutterMac() => Frame( 48 | Uri.parse('package:flutter_sample_roggle/main.dart'), 49 | 66, 50 | 10, 51 | 'dummy', 52 | ); 53 | 54 | /// Flutter on Windows 55 | static Frame flutterWindows() => Frame( 56 | Uri.parse('package:flutter_sample_roggle/main.dart'), 57 | 66, 58 | 10, 59 | 'dummy', 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | push: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | test: 15 | name: Test 16 | runs-on: ${{ matrix.os }} 17 | timeout-minutes: 10 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, macos-latest, windows-latest] 21 | sdk: [stable, beta, 2.17.0] 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | with: 26 | fetch-depth: 1 27 | - name: Download dart sdk 28 | uses: dart-lang/setup-dart@v1.3 29 | with: 30 | sdk: ${{ matrix.sdk }} 31 | - name: Install dependencies 32 | run: dart pub get 33 | - name: Format code 34 | run: dart format --set-exit-if-changed . 35 | - name: Static analyze project 36 | run: dart analyze --fatal-infos --fatal-warnings lib 37 | - name: Run tests 38 | run: dart test 39 | 40 | coverage: 41 | name: Coverage 42 | needs: 43 | - test 44 | runs-on: ubuntu-latest 45 | timeout-minutes: 10 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v3 49 | with: 50 | fetch-depth: 1 51 | - name: Download dart sdk 52 | uses: dart-lang/setup-dart@v1 53 | with: 54 | sdk: stable 55 | - name: Install dependencies 56 | run: dart pub get 57 | - name: Run tests 58 | run: dart test --coverage=coverage 59 | - name: Convert coverage to ICOV 60 | run: dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib 61 | - name: Upload coverage to Codecov 62 | uses: codecov/codecov-action@v2 63 | with: 64 | file: coverage/lcov.info 65 | fail_ci_if_error: true 66 | flags: unittests 67 | verbose: true 68 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | import 'package:roggle/roggle.dart'; 4 | 5 | bool get isRelease { 6 | var isRelease = true; 7 | assert( 8 | () { 9 | isRelease = false; 10 | return true; 11 | }(), 12 | ); 13 | return isRelease; 14 | } 15 | 16 | const loggerName = '[APP]'; 17 | 18 | final logger = isRelease 19 | ? Roggle.crashlytics( 20 | printer: CrashlyticsPrinter( 21 | errorLevel: Level.error, 22 | onError: (event) { 23 | // Send an error to Firebase Crashlytics as follows 24 | 25 | // FirebaseCrashlytics.instance.recordError( 26 | // event.exception, 27 | // event.stack, 28 | // fatal: true, 29 | // ); 30 | 31 | print('FirebaseCrashlytics.exception: ${event.exception}'); 32 | event.stack 33 | .toString() 34 | .split('\n') 35 | .where((line) => line.isNotEmpty) 36 | .forEach((line) { 37 | print('FirebaseCrashlytics.stack: $line'); 38 | }); 39 | }, 40 | // ignore: unnecessary_lambdas 41 | onLog: (event) { 42 | // Send logs to Firebase Crashlytics as follows 43 | 44 | // FirebaseCrashlytics.instance.log(event.message); 45 | 46 | print('FirebaseCrashlytics.log: ${event.message}'); 47 | }, 48 | loggerName: loggerName, 49 | ), 50 | ) 51 | : Roggle( 52 | printer: SinglePrettyPrinter( 53 | loggerName: loggerName, 54 | stackTraceLevel: Level.error, 55 | ), 56 | ); 57 | 58 | void main() { 59 | print( 60 | 'Run with either `dart example/main.dart` or `dart --enable-asserts example/main.dart`.', 61 | ); 62 | demo(); 63 | } 64 | 65 | void demo() { 66 | logger.t('Hello roggle!'); 67 | logger.d(1000); 68 | logger.i(true); 69 | logger.i([1, 2, 3]); 70 | logger.i({'key': 'key', 'value': 'value'}); 71 | logger.i({'apple', 'banana'}); 72 | logger.i(() => 'function message'); 73 | logger.w(Exception('some exception')); 74 | logger.e(TypeError()); 75 | 76 | try { 77 | throw Exception('some exception'); 78 | } on Exception catch (e, s) { 79 | logger.f('fatal...', error: e, stackTrace: s); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.2+1 2 | 3 | - Added logo image. 4 | 5 | ## 0.4.1 6 | 7 | - Fixed warning. 8 | 9 | ## 0.4.0 10 | 11 | - Bumped upper SDK constraint to `<4.0.0`. 12 | - Upgrade logger package from 1.3.0 to 2.0.2+1 13 | - Fixed supported platforms list. 14 | - Added override capability for logger defaults. 15 | - `Level.verbose`, `Level.wtf` and `Level.nothing` have been deprecated and are replaced 16 | by `Level.trace`, `Level.fatal` and `Level.off`. 17 | Additionally `Level.all` has been added. 18 | - Added `addLogListener`, `removeLogListener`, `addOutputListener`, `removeOutputListener` and ``isClosed`. 19 | - Added `time` to LogEvent. 20 | 21 | ### Breaking changes 22 | 23 | - `log` signature has been changed to closer match dart's developer `log` function and allow for 24 | future 25 | optional parameters. 26 | 27 | Additionally, `time` has been added as an optional named parameter to support providing custom 28 | timestamps for LogEvents instead of `DateTime.now()`. 29 | 30 | #### Migration: 31 | - Before: 32 | ```dart 33 | logger.e("An error occurred!", error, stackTrace); 34 | ``` 35 | - After: 36 | ```dart 37 | logger.e("An error occurred!", error: error, stackTrace: stackTrace); 38 | ``` 39 | - `init` and `close` methods of `LogFilter`, `LogOutput` and `LogPrinter` are now async along 40 | with `Logger.close()`. 41 | (Fixes FileOutput) 42 | - Levels are now sorted by their respective value instead of the enum index (Order didn't change). 43 | 44 | ## 0.3.2 45 | 46 | - upgrade: Support Dart version to 3.0.0 47 | - upgrade: Upgrade logger package to 1.3.0 48 | 49 | ## 0.3.1 50 | 51 | - upgrade: Upgrade logger package to 1.2.2 52 | 53 | ## 0.3.0 54 | 55 | - feature: Added `printLocation` parameter. If set to `true`, location of caller will be output to the log. 56 | - feature: Added `printFunctionName` parameter. If set to `true`, function name of caller will be output to the log. 57 | - fix: Unified Caller format across all platforms. Because changed to use `Trace.current()` instead of `StackTrace.current` when getting a Caller. 58 | - fix: Changed default emojis. 59 | 60 | ## 0.2.1+1 61 | 62 | - fix: Fixed minimum version of meta to 1.7.0 63 | 64 | ## 0.2.1 65 | 66 | - fix: Deleted beginning space when `defaultStackTracePrefix` is ​​empty string 67 | 68 | ## 0.2.0 69 | 70 | - fix: Changed the message property type of Roggle.log() from `Object' to 'Object?' 71 | 72 | ## 0.1.0 73 | 74 | - Update version 75 | 76 | ## 0.1.0-dev.2 77 | 78 | - Support crashlytics 79 | 80 | ## 0.1.0-dev.1 81 | 82 | - First version -------------------------------------------------------------------------------- /lib/src/printers/crashlytics_printer.dart: -------------------------------------------------------------------------------- 1 | import '../../roggle.dart'; 2 | 3 | /// An event of crashlytics error. 4 | class CrashlyticsErrorEvent { 5 | CrashlyticsErrorEvent(this.level, this.exception, this.stack); 6 | 7 | final Level level; 8 | final dynamic exception; 9 | final StackTrace stack; 10 | } 11 | 12 | /// An event of crashlytics log. 13 | class CrashlyticsLogEvent { 14 | CrashlyticsLogEvent(this.level, this.message); 15 | 16 | final Level level; 17 | final String message; 18 | } 19 | 20 | /// [LogPrinter] for crashlytics. 21 | /// 22 | /// Calls [onLog] when there is a log message, and calls [onError] when there is 23 | /// an error. Logs above [errorLevel] will be sent as error logs. 24 | /// [CrashlyticsLogEvent.message] of [onLog] looks like this: 25 | /// ``` 26 | /// 💡 [INFO] demo (file:///your/file/path/roggle/example/main.dart:16:10): Log message 27 | /// ``` 28 | class CrashlyticsPrinter extends SinglePrettyPrinter { 29 | CrashlyticsPrinter({ 30 | required this.errorLevel, 31 | required this.onError, 32 | this.onLog, 33 | super.loggerName, 34 | super.printCaller, 35 | super.printFunctionName, 36 | super.printLocation, 37 | super.printEmojis, 38 | super.printLabels, 39 | super.levelEmojis, 40 | super.levelLabels, 41 | }) : super( 42 | colors: false, 43 | printTime: false, 44 | stackTraceMethodCount: null, 45 | stackTracePrefix: '', 46 | ); 47 | 48 | /// The current logging level to send error. 49 | /// 50 | /// Logs above this level will call onError. 51 | final Level errorLevel; 52 | 53 | /// Callback called when an error occurs 54 | final CrashlyticsErrorCallback onError; 55 | 56 | /// Callback called when printing a log message 57 | final CrashlyticsLogCallback? onLog; 58 | 59 | @override 60 | List log(LogEvent event) { 61 | if (onLog != null) { 62 | final message = _formatMessage( 63 | level: event.level, 64 | message: stringifyMessage(event.message), 65 | time: event.time, 66 | ); 67 | onLog!.call(CrashlyticsLogEvent(event.level, message)); 68 | } 69 | 70 | if (event.level.index >= errorLevel.index) { 71 | Object error; 72 | if (event.error != null) { 73 | // If error is not null, it will be sent with priority. 74 | error = event.error!; 75 | } else if (event.message is Exception) { 76 | error = event.message as Exception; 77 | } else if (event.message is Error) { 78 | error = event.message as Error; 79 | } else { 80 | error = stringifyMessage(event.message); 81 | } 82 | 83 | List stackTraceLines; 84 | if (event.stackTrace != null) { 85 | // If stackTrace is not null, it will be sent with priority. 86 | stackTraceLines = getStackTrace(stackTrace: event.stackTrace); 87 | } else { 88 | stackTraceLines = getStackTrace(); 89 | } 90 | onError.call( 91 | CrashlyticsErrorEvent( 92 | event.level, 93 | error, 94 | StackTrace.fromString(stackTraceLines.join('\n')), 95 | ), 96 | ); 97 | } 98 | 99 | return []; 100 | } 101 | 102 | String _formatMessage({ 103 | required Level level, 104 | required String message, 105 | required DateTime time, 106 | }) { 107 | final color = getLevelColor(level); 108 | final fixed = formatFixed(level: level, time: time); 109 | return color('$fixed$message'); 110 | } 111 | } 112 | 113 | /// Function to call when an error occurs 114 | typedef CrashlyticsErrorCallback = void Function(CrashlyticsErrorEvent event); 115 | 116 | /// Function to call when printing a log message 117 | typedef CrashlyticsLogCallback = void Function(CrashlyticsLogEvent event); 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://github.com/susatthi/roggle/blob/main/logo.png?raw=true) 2 | 3 | # Roggle 4 | 5 | Test 6 | codecov 7 | MIT 8 | 9 | Simple, colorful and easy to expand logger for dart.
10 | Inspired by [the logger](https://pub.dev/packages/logger) and dependent to the logger. 11 | 12 | ## Getting Started 13 | 14 | Just create an instance of `Roggle` and start logging: 15 | 16 | ```dart 17 | import 'package:roggle/roggle.dart'; 18 | 19 | final logger = Roggle(); 20 | 21 | logger.d('Roggle is working!'); 22 | ``` 23 | 24 | Instead of a string message, you can also pass other objects like `List`, `Map`, `Set` or `Function`. 25 | 26 | ## Output 27 | 28 | ![Android Studio](https://user-images.githubusercontent.com/13707135/195221622-341dbf2d-5708-441b-a804-5eae969855d6.png) 29 | 30 | # Documentation 31 | 32 | Roggle's API is almost the same as the logger. See [the logger](https://pub.dev/packages/logger) for basic usage. 33 | 34 | ## Difference from the logger 35 | 36 | - Printer defaults to `SinglePrettyPrinter` class. 37 | - Not try-catch at `LogOutput` class, because we want to stop the process. 38 | - Added some getters in `Roggle` class. 39 | - Changed the log interface `dynamic` to `Object?`. 40 | 41 | ## Options 42 | 43 | If you use the `SinglePrettyPrinter`, there are more options: 44 | 45 | ```dart 46 | final logger = Roggle( 47 | printer: SinglePrettyPrinter( 48 | loggerName: '[APP]', // Print a logger name for each log message 49 | colors: true, // Colorful log messages 50 | printCaller: true, // Print a caller for each log message 51 | printLocation: true, // Print a location of caller for each log message 52 | printFunctionName: true, // Print a function name of caller for each log message 53 | printEmojis: true, // Print an emoji for each log message 54 | printLabels: true, // Print a level string for each log message 55 | printTime: true, // Print a timestamp for each log message 56 | stackTraceLevel: 57 | Level.error, // The current logging level to display stack trace 58 | stackTraceMethodCount: 10, // Number of stack trace methods to display 59 | stackTracePrefix: '>>> ', // Replace stack trace prefix 60 | levelColors: {}, // Replace level colors map 61 | levelEmojis: {}, // Replace level emojis map 62 | levelLabels: {}, // Replace level labels map 63 | timeFormatter: (now) => DateFormat('yyyy/MM/dd HH:mm:ss.SSS') 64 | .format(now), // Replace time formatter 65 | ), 66 | ); 67 | ``` 68 | 69 | ![SinglePrettyPrinter](https://user-images.githubusercontent.com/13707135/195223339-4837870f-cfcd-4447-b0b1-f958531a6db0.png) 70 | 71 | You can use the `Roggle.crashlytics()` to send errors and logs using the `FirebaseCrashlytics` API in release mode. 72 | 73 | ```dart 74 | final logger = Roggle.crashlytics( 75 | printer: CrashlyticsPrinter( 76 | errorLevel: Level.error, // Logs above this level will call onError 77 | onError: (event) { 78 | // Send an error to Firebase Crashlytics as follows 79 | FirebaseCrashlytics.instance.recordError( 80 | event.exception, 81 | event.stack, 82 | fatal: true, 83 | ); 84 | }, 85 | onLog: (event) { 86 | // Send logs to Firebase Crashlytics as follows 87 | FirebaseCrashlytics.instance.log(event.message); 88 | }, 89 | loggerName: '[APP]', 90 | printCaller: true, // Print a caller for each log message 91 | printLocation: true, // Print a location of caller for each log message 92 | printFunctionName: true, // Print a function name of caller for each log message 93 | printEmojis: true, // Print an emoji for each log message 94 | printLabels: true, // Print a level string for each log message 95 | levelEmojis: {}, // Replace level emojis map 96 | levelLabels: {}, // Replace level labels map 97 | ), 98 | ); 99 | ``` 100 | 101 | ## Tips 102 | 103 | If you want to stop the process at the same time as the error log, do the following. 104 | 105 | ```dart 106 | import 'package:roggle/roggle.dart'; 107 | 108 | final logger = Roggle( 109 | output: AssertionOutput(), 110 | ); 111 | 112 | class AssertionOutput extends ConsoleOutput { 113 | @override 114 | void output(OutputEvent event) { 115 | super.output(event); 116 | if (event.level.index >= Level.error.index) { 117 | throw AssertionError('Stopped by logger.'); 118 | } 119 | } 120 | } 121 | ``` 122 | 123 | ## License 124 | 125 | ``` 126 | MIT License 127 | 128 | Copyright (c) 2022 Keyber Inc. 129 | 130 | Permission is hereby granted, free of charge, to any person obtaining a copy 131 | of this software and associated documentation files (the "Software"), to deal 132 | in the Software without restriction, including without limitation the rights 133 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 134 | copies of the Software, and to permit persons to whom the Software is 135 | furnished to do so, subject to the following conditions: 136 | 137 | The above copyright notice and this permission notice shall be included in all 138 | copies or substantial portions of the Software. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 144 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 145 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 146 | SOFTWARE. 147 | ``` 148 | -------------------------------------------------------------------------------- /lib/src/roggle.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | import 'outputs/dump_output.dart'; 4 | import 'printers/crashlytics_printer.dart'; 5 | import 'printers/single_pretty_printer.dart'; 6 | 7 | typedef LogCallback = void Function(LogEvent event); 8 | 9 | typedef OutputCallback = void Function(OutputEvent event); 10 | 11 | /// Use instances of roggle to send log messages to the [LogPrinter]. 12 | class Roggle { 13 | /// Create a new instance of Roggle. 14 | /// 15 | /// You can provide a custom [printer], [filter] and [output]. Otherwise the 16 | /// defaults: [SinglePrettyPrinter], [DevelopmentFilter] and [ConsoleOutput] 17 | /// will be used. 18 | Roggle({ 19 | LogFilter? filter, 20 | LogPrinter? printer, 21 | LogOutput? output, 22 | Level? level, 23 | }) : _filter = filter ?? defaultFilter(), 24 | _printer = printer ?? defaultPrinter(), 25 | _output = output ?? defaultOutput() { 26 | _filter.init(); 27 | // ignore: cascade_invocations 28 | _filter.level = level ?? Roggle.level; 29 | _printer.init(); 30 | _output.init(); 31 | } 32 | 33 | /// Create a new instance of Roggle for Crashlytics. 34 | /// 35 | /// You can provide a custom [printer]. 36 | factory Roggle.crashlytics({ 37 | required CrashlyticsPrinter printer, 38 | }) => 39 | Roggle( 40 | filter: ProductionFilter(), 41 | printer: printer, 42 | output: DumpOutput(), 43 | ); 44 | 45 | /// The current logging level of the app. 46 | /// 47 | /// All logs with levels below this level will be omitted. 48 | static Level level = Level.trace; 49 | 50 | /// The current default implementation of log filter. 51 | static LogFilter Function() defaultFilter = DevelopmentFilter.new; 52 | 53 | /// The current default implementation of log printer. 54 | static LogPrinter Function() defaultPrinter = SinglePrettyPrinter.new; 55 | 56 | /// The current default implementation of log output. 57 | static LogOutput Function() defaultOutput = ConsoleOutput.new; 58 | 59 | static final Set _logCallbacks = {}; 60 | 61 | static final Set _outputCallbacks = {}; 62 | 63 | final LogFilter _filter; 64 | LogFilter get filter => _filter; 65 | 66 | final LogPrinter _printer; 67 | LogPrinter get printer => _printer; 68 | 69 | final LogOutput _output; 70 | LogOutput get output => _output; 71 | 72 | bool _active = true; 73 | @Deprecated( 74 | '[active] is being deprecated, use [isClosed()] instead.', 75 | ) 76 | bool get active => _active; 77 | 78 | /// Log a message at level [Level.verbose]. 79 | @Deprecated( 80 | '[Level.verbose] is being deprecated in favor of [Level.trace], ' 81 | 'use [t] instead.', 82 | ) 83 | void v( 84 | Object? message, { 85 | DateTime? time, 86 | Object? error, 87 | StackTrace? stackTrace, 88 | }) { 89 | t(message, time: time, error: error, stackTrace: stackTrace); 90 | } 91 | 92 | /// Log a message at level [Level.trace]. 93 | void t( 94 | Object? message, { 95 | DateTime? time, 96 | Object? error, 97 | StackTrace? stackTrace, 98 | }) { 99 | log(Level.trace, message, time: time, error: error, stackTrace: stackTrace); 100 | } 101 | 102 | /// Log a message at level [Level.debug]. 103 | void d( 104 | Object? message, { 105 | DateTime? time, 106 | Object? error, 107 | StackTrace? stackTrace, 108 | }) { 109 | log(Level.debug, message, time: time, error: error, stackTrace: stackTrace); 110 | } 111 | 112 | /// Log a message at level [Level.info]. 113 | void i( 114 | Object? message, { 115 | DateTime? time, 116 | Object? error, 117 | StackTrace? stackTrace, 118 | }) { 119 | log(Level.info, message, time: time, error: error, stackTrace: stackTrace); 120 | } 121 | 122 | /// Log a message at level [Level.warning]. 123 | void w( 124 | Object? message, { 125 | DateTime? time, 126 | Object? error, 127 | StackTrace? stackTrace, 128 | }) { 129 | log( 130 | Level.warning, 131 | message, 132 | time: time, 133 | error: error, 134 | stackTrace: stackTrace, 135 | ); 136 | } 137 | 138 | /// Log a message at level [Level.error]. 139 | void e( 140 | Object? message, { 141 | DateTime? time, 142 | Object? error, 143 | StackTrace? stackTrace, 144 | }) { 145 | log(Level.error, message, time: time, error: error, stackTrace: stackTrace); 146 | } 147 | 148 | /// Log a message at level [Level.wtf]. 149 | @Deprecated( 150 | '[Level.wtf] is being deprecated in favor of [Level.fatal], ' 151 | 'use [f] instead.', 152 | ) 153 | void wtf( 154 | Object? message, { 155 | DateTime? time, 156 | Object? error, 157 | StackTrace? stackTrace, 158 | }) { 159 | f(message, time: time, error: error, stackTrace: stackTrace); 160 | } 161 | 162 | /// Log a message at level [Level.fatal]. 163 | void f( 164 | Object? message, { 165 | DateTime? time, 166 | Object? error, 167 | StackTrace? stackTrace, 168 | }) { 169 | log(Level.fatal, message, time: time, error: error, stackTrace: stackTrace); 170 | } 171 | 172 | /// Log a message with [level]. 173 | void log( 174 | Level level, 175 | Object? message, { 176 | DateTime? time, 177 | Object? error, 178 | StackTrace? stackTrace, 179 | }) { 180 | if (!_active) { 181 | throw ArgumentError('Logger has already been closed.'); 182 | } else if (error != null && error is StackTrace) { 183 | throw ArgumentError('Error parameter cannot take a StackTrace!'); 184 | } else if (level == Level.all) { 185 | throw ArgumentError('Log events cannot have Level.all'); 186 | } else if (level == Level.off) { 187 | throw ArgumentError('Log events cannot have Level.off'); 188 | // ignore: deprecated_member_use 189 | } else if (level == Level.nothing) { 190 | throw ArgumentError('Log events cannot have Level.nothing'); 191 | // ignore: deprecated_member_use 192 | } else if (level == Level.verbose) { 193 | throw ArgumentError('Log events cannot have Level.verbose'); 194 | // ignore: deprecated_member_use 195 | } else if (level == Level.wtf) { 196 | throw ArgumentError('Log events cannot have Level.wtf'); 197 | } 198 | final logEvent = LogEvent( 199 | level, 200 | message, 201 | time: time, 202 | error: error, 203 | stackTrace: stackTrace, 204 | ); 205 | for (final callback in _logCallbacks) { 206 | callback(logEvent); 207 | } 208 | 209 | if (_filter.shouldLog(logEvent)) { 210 | final output = _printer.log(logEvent); 211 | 212 | if (output.isNotEmpty) { 213 | final outputEvent = OutputEvent(logEvent, output); 214 | for (final callback in _outputCallbacks) { 215 | callback(outputEvent); 216 | } 217 | // I didn't try to catch it because I wanted 218 | // to stop the app on purpose. 219 | _output.output(outputEvent); 220 | } 221 | } 222 | } 223 | 224 | bool isClosed() { 225 | return !_active; 226 | } 227 | 228 | /// Closes the logger and releases all resources. 229 | Future close() async { 230 | if (!_active) { 231 | return; 232 | } 233 | 234 | _active = false; 235 | await _filter.destroy(); 236 | await _printer.destroy(); 237 | await _output.destroy(); 238 | } 239 | 240 | /// Register a [LogCallback] which is called for each new [LogEvent]. 241 | static void addLogListener(LogCallback callback) { 242 | _logCallbacks.add(callback); 243 | } 244 | 245 | /// Removes a [LogCallback] which was previously registered. 246 | /// 247 | /// Returns whether the callback was successfully removed. 248 | static bool removeLogListener(LogCallback callback) { 249 | return _logCallbacks.remove(callback); 250 | } 251 | 252 | /// Register an [OutputCallback] which is called for each new [OutputEvent]. 253 | static void addOutputListener(OutputCallback callback) { 254 | _outputCallbacks.add(callback); 255 | } 256 | 257 | /// Removes a [OutputCallback] which was previously registered. 258 | /// 259 | /// Returns whether the callback was successfully removed. 260 | static void removeOutputListener(OutputCallback callback) { 261 | _outputCallbacks.remove(callback); 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /lib/src/printers/single_pretty_printer.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:stack_trace/stack_trace.dart'; 4 | 5 | /// Default implementation of [LogPrinter]. 6 | /// 7 | /// Output looks like this: 8 | /// ``` 9 | /// 💡 [INFO] 06:46:15.354 demo (file:///your/file/path/roggle/example/main.dart:16:10): Log message 10 | /// ``` 11 | class SinglePrettyPrinter extends LogPrinter { 12 | SinglePrettyPrinter({ 13 | this.loggerName, 14 | this.colors = true, 15 | this.printCaller = true, 16 | this.printLocation = true, 17 | this.printFunctionName = true, 18 | this.printEmojis = true, 19 | this.printLabels = true, 20 | this.printTime = true, 21 | this.stackTraceLevel = Level.off, 22 | this.stackTraceMethodCount = defaultStackTraceMethodCount, 23 | this.stackTracePrefix = defaultStackTracePrefix, 24 | Map? levelColors, 25 | this.levelEmojis = defaultLevelEmojis, 26 | this.levelLabels = defaultLevelLabels, 27 | this.timeFormatter = formatTime, 28 | }) : _levelColors = levelColors ?? defaultLevelColors; 29 | 30 | /// If specified, it will be output at the beginning of the log. 31 | final String? loggerName; 32 | 33 | /// If set to true, the log will be colorful. 34 | final bool colors; 35 | 36 | /// If set to true, caller will be output to the log. 37 | final bool printCaller; 38 | 39 | /// If set to true, location of caller will be output to the log. 40 | /// 41 | /// Set [printCaller] to true if you want to enable [printLocation]. 42 | final bool printLocation; 43 | 44 | /// If set to true, function name of caller will be output to the log. 45 | /// 46 | /// Set [printCaller] to true if you want to enable [printFunctionName]. 47 | final bool printFunctionName; 48 | 49 | /// If set to true, the emoji will be output to the log. 50 | final bool printEmojis; 51 | 52 | /// If set to true, the log level string will be output to the log. 53 | final bool printLabels; 54 | 55 | /// If set to true, the time stamp will be output to the log. 56 | final bool printTime; 57 | 58 | /// The current logging level to display stack trace. 59 | /// 60 | /// All stack traces with levels below this level will be omitted. 61 | final Level stackTraceLevel; 62 | 63 | /// Number of stack trace methods to display. 64 | final int? stackTraceMethodCount; 65 | 66 | /// Stack trace prefix. 67 | final String stackTracePrefix; 68 | 69 | /// Color for each log level. 70 | final Map _levelColors; 71 | 72 | /// Emoji for each log level. 73 | final Map levelEmojis; 74 | 75 | /// String for each log level. 76 | final Map levelLabels; 77 | 78 | /// Formats the current time. 79 | final TimeFormatter timeFormatter; 80 | 81 | /// Stack trace method count default. 82 | static const defaultStackTraceMethodCount = 20; 83 | 84 | /// Stack trace prefix default. 85 | static const defaultStackTracePrefix = '│ '; 86 | 87 | /// Color default for each log level. 88 | static final defaultLevelColors = { 89 | Level.trace: AnsiColor.fg(AnsiColor.grey(0.5)), 90 | Level.debug: const AnsiColor.none(), 91 | Level.info: const AnsiColor.fg(12), 92 | Level.warning: const AnsiColor.fg(208), 93 | Level.error: const AnsiColor.fg(196), 94 | Level.fatal: const AnsiColor.fg(199), 95 | }; 96 | 97 | /// Emoji default for each log level. 98 | static const defaultLevelEmojis = { 99 | Level.trace: '🐱', 100 | Level.debug: '🐛', 101 | Level.info: '👀', 102 | Level.warning: '❗', 103 | Level.error: '⛔', 104 | Level.fatal: '🔥', 105 | }; 106 | 107 | /// String default for each log level. 108 | static const defaultLevelLabels = { 109 | Level.trace: '[TRACE] ', 110 | Level.debug: '[DEBUG] ', 111 | Level.info: '[INFO] ', 112 | Level.warning: '[WARNING]', 113 | Level.error: '[ERROR] ', 114 | Level.fatal: '[FATAL] ', 115 | }; 116 | 117 | @override 118 | List log(LogEvent event) { 119 | List? stackTraceLines; 120 | if (event.stackTrace != null) { 121 | // If stackTrace is not null, it will be displayed with priority. 122 | stackTraceLines = getStackTrace(stackTrace: event.stackTrace); 123 | } else if (event.level.index >= stackTraceLevel.index) { 124 | stackTraceLines = getStackTrace(); 125 | } 126 | 127 | return _formatMessage( 128 | level: event.level, 129 | message: stringifyMessage(event.message), 130 | time: event.time, 131 | error: event.error?.toString(), 132 | stackTrace: stackTraceLines, 133 | ); 134 | } 135 | 136 | @visibleForTesting 137 | String? getCaller({ 138 | StackTrace? stackTrace, 139 | }) { 140 | final trace = stackTrace != null ? Trace.from(stackTrace) : Trace.current(); 141 | final caller = trace.caller; 142 | if (caller == null) { 143 | return null; 144 | } 145 | return convertToCallerString( 146 | caller, 147 | printFunctionName: printFunctionName, 148 | printLocation: printLocation, 149 | ); 150 | } 151 | 152 | @protected 153 | List getStackTrace({ 154 | StackTrace? stackTrace, 155 | }) { 156 | final trace = stackTrace != null ? Trace.from(stackTrace) : Trace.current(); 157 | final formatted = []; 158 | var count = 0; 159 | for (final frame in trace.frames) { 160 | if (!frame.isUserFrame) { 161 | continue; 162 | } 163 | if (stackTraceMethodCount != null && count >= stackTraceMethodCount!) { 164 | break; 165 | } 166 | 167 | final caller = convertToCallerString(frame) ?? ''; 168 | final countPart = count.toString().padRight(7); 169 | formatted.add('$stackTracePrefix#$countPart$caller'); 170 | count++; 171 | } 172 | return formatted; 173 | } 174 | 175 | @protected 176 | String? convertToCallerString( 177 | Frame frame, { 178 | bool printFunctionName = true, 179 | bool printLocation = true, 180 | }) { 181 | if (!printFunctionName && !printLocation) { 182 | return null; 183 | } 184 | 185 | final parts = []; 186 | if (printFunctionName) { 187 | final member = frame.member; 188 | if (member != null) { 189 | parts.add(member); 190 | } 191 | } 192 | if (printLocation) { 193 | parts.add('(${frame.locationEx})'); 194 | } 195 | return parts.join(' '); 196 | } 197 | 198 | @visibleForTesting 199 | static String formatTime(DateTime now) { 200 | String threeDigits(int n) { 201 | if (n >= 100) { 202 | return '$n'; 203 | } 204 | if (n >= 10) { 205 | return '0$n'; 206 | } 207 | return '00$n'; 208 | } 209 | 210 | String twoDigits(int n) { 211 | if (n >= 10) { 212 | return '$n'; 213 | } 214 | return '0$n'; 215 | } 216 | 217 | final h = twoDigits(now.hour); 218 | final min = twoDigits(now.minute); 219 | final sec = twoDigits(now.second); 220 | final ms = threeDigits(now.millisecond); 221 | return '$h:$min:$sec.$ms'; 222 | } 223 | 224 | @protected 225 | String stringifyMessage(dynamic message) { 226 | if (message is dynamic Function()) { 227 | return message().toString(); 228 | } else if (message is String) { 229 | return message; 230 | } 231 | return message.toString(); 232 | } 233 | 234 | @protected 235 | AnsiColor getLevelColor(Level level) { 236 | if (colors) { 237 | return _levelColors[level]!; 238 | } else { 239 | return const AnsiColor.none(); 240 | } 241 | } 242 | 243 | List _formatMessage({ 244 | required Level level, 245 | required String message, 246 | required DateTime time, 247 | String? error, 248 | List? stackTrace, 249 | }) { 250 | final color = getLevelColor(level); 251 | final fixed = formatFixed(level: level, time: time); 252 | final logs = [ 253 | color('$fixed$message'), 254 | ]; 255 | 256 | if (error != null) { 257 | logs.add(color('$fixed$stackTracePrefix$error')); 258 | } 259 | 260 | if (stackTrace != null && stackTrace.isNotEmpty) { 261 | for (final line in stackTrace) { 262 | logs.add(color('$fixed$line')); 263 | } 264 | } 265 | return logs; 266 | } 267 | 268 | @protected 269 | String formatFixed({ 270 | required Level level, 271 | required DateTime time, 272 | }) { 273 | final buffer = []; 274 | 275 | if (printEmojis) { 276 | buffer.add(levelEmojis[level]!); 277 | } 278 | if (loggerName != null) { 279 | buffer.add(loggerName!); 280 | } 281 | if (printLabels) { 282 | buffer.add(levelLabels[level]!); 283 | } 284 | if (printTime) { 285 | buffer.add(timeFormatter(time)); 286 | } 287 | if (printCaller) { 288 | final caller = getCaller(); 289 | if (caller != null) { 290 | buffer.add(caller); 291 | } 292 | } 293 | return buffer.isNotEmpty ? '${buffer.join(' ')}: ' : ''; 294 | } 295 | } 296 | 297 | /// Function to format the current time 298 | typedef TimeFormatter = String Function(DateTime now); 299 | 300 | extension _TraceEx on Trace { 301 | /// Return first frame in user frames 302 | Frame? get caller { 303 | for (final frame in frames) { 304 | if (frame.isUserFrame) { 305 | return frame; 306 | } 307 | } 308 | return null; 309 | } 310 | } 311 | 312 | extension _FrameEx on Frame { 313 | /// If user frame is true, other false 314 | bool get isUserFrame => !isCoreEx && packageEx != 'roggle'; 315 | 316 | /// Whether this stack frame comes from the Web. 317 | bool get isWeb => uri.scheme == 'http' || uri.scheme == 'https'; 318 | 319 | /// Extension of [isCore] 320 | bool get isCoreEx { 321 | if (isWeb && packageEx == null) { 322 | return true; 323 | } 324 | return isCore; 325 | } 326 | 327 | /// Extension of [library] 328 | String get libraryEx => library 329 | .replaceAll('\\', '/') 330 | .replaceFirst(RegExp(r'^packages\/'), 'package:'); 331 | 332 | /// Extension of [package] 333 | String? get packageEx { 334 | if (isWeb) { 335 | final paths = 336 | uri.path.split('/').where((path) => path.isNotEmpty).toList(); 337 | if (paths.length >= 2 && paths[0] == 'packages') { 338 | return paths[1]; 339 | } 340 | } 341 | return package; 342 | } 343 | 344 | /// Extension of [location] 345 | String get locationEx { 346 | if (line == null) { 347 | return libraryEx; 348 | } 349 | if (column == null) { 350 | return '$libraryEx:$line'; 351 | } 352 | return '$libraryEx:$line:$column'; 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /test/test_utils/stack_trace_factory.dart: -------------------------------------------------------------------------------- 1 | class StackTraceFactory { 2 | /// Dart on Mac 3 | static StackTrace dartMac() => StackTrace.fromString(''' 4 | #0 SinglePrettyPrinter.getCaller (package:roggle/src/printers/single_pretty_printer.dart:127:26) 5 | #1 SinglePrettyPrinter.formatFixed (package:roggle/src/printers/single_pretty_printer.dart:259:22) 6 | #2 SinglePrettyPrinter._formatMessage (package:roggle/src/printers/single_pretty_printer.dart:223:19) 7 | #3 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:115:12) 8 | #4 Roggle.log (package:roggle/src/roggle.dart:104:31) 9 | #5 Roggle.v (package:roggle/src/roggle.dart:60:5) 10 | #6 demo (file:///Users/dummy/Develop/roggle/example/main.dart:66:10) 11 | #7 main (file:///Users/dummy/Develop/roggle/example/main.dart:62:3) 12 | #8 _delayEntrypointInvocation. (dart:isolate-patch/isolate_patch.dart:297:19) 13 | #9 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12) 14 | '''); 15 | 16 | /// Dart on Windows 17 | static StackTrace dartWindows() => StackTrace.fromString(''' 18 | package:roggle/src/printers/single_pretty_printer.dart 127:21 SinglePrettyPrinter.getCaller 19 | package:roggle/src/printers/single_pretty_printer.dart 260:22 SinglePrettyPrinter.formatFixed 20 | package:roggle/src/printers/single_pretty_printer.dart 224:19 SinglePrettyPrinter._formatMessage 21 | package:roggle/src/printers/single_pretty_printer.dart 115:12 SinglePrettyPrinter.log 22 | package:roggle/src/roggle.dart 104:31 Roggle.log 23 | package:roggle/src/roggle.dart 60:5 Roggle.v 24 | example\\main.dart 67:10 demo 25 | example\\main.dart 63:3 main 26 | dart:isolate-patch/isolate_patch.dart 297:19 _delayEntrypointInvocation. 27 | dart:isolate-patch/isolate_patch.dart 192:12 _RawReceivePortImpl._handleMessage 28 | '''); 29 | 30 | /// Flutter on Web 31 | static StackTrace flutterWeb() => StackTrace.fromString(''' 32 | http://localhost:5000/packages/roggle/src/printers/single_pretty_printer.dart 127:26 getCaller 33 | http://localhost:5000/packages/roggle/src/printers/single_pretty_printer.dart 258:22 formatFixed 34 | http://localhost:5000/packages/roggle/src/printers/single_pretty_printer.dart 222:19 [_formatMessage] 35 | http://localhost:5000/packages/roggle/src/printers/single_pretty_printer.dart 115:12 log 36 | http://localhost:5000/packages/roggle/src/roggle.dart 104:31 log 37 | http://localhost:5000/packages/roggle/src/roggle.dart 60:5 v 38 | http://localhost:5000/packages/flutter_sample_roggle/main.dart 55:14 [_incrementCounter] 39 | http://localhost:5000/packages/flutter/src/material/ink_well.dart 1072:21 handleTap 40 | http://localhost:5000/packages/flutter/src/gestures/recognizer.dart 253:24 invokeCallback 41 | http://localhost:5000/packages/flutter/src/gestures/tap.dart 627:11 handleTapUp 42 | http://localhost:5000/packages/flutter/src/gestures/tap.dart 306:5 [_checkUp] 43 | http://localhost:5000/packages/flutter/src/gestures/tap.dart 276:7 acceptGesture 44 | http://localhost:5000/packages/flutter/src/gestures/arena.dart 163:12 sweep 45 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 464:20 handleEvent 46 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 440:14 dispatchEvent 47 | http://localhost:5000/packages/flutter/src/rendering/binding.dart 337:11 dispatchEvent 48 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 395:7 [_handlePointerEventImmediately] 49 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 357:5 handlePointerEvent 50 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 314:7 [_flushPointerEventQueue] 51 | http://localhost:5000/packages/flutter/src/gestures/binding.dart 295:7 [_handlePointerDataPacket] 52 | http://localhost:5000/lib/_engine/engine/platform_dispatcher.dart 1183:13 invoke1 53 | http://localhost:5000/lib/_engine/engine/platform_dispatcher.dart 244:5 invokeOnPointerDataPacket 54 | http://localhost:5000/lib/_engine/engine/pointer_binding.dart 147:39 [_onPointerData] 55 | http://localhost:5000/lib/_engine/engine/pointer_binding.dart 653:20 56 | http://localhost:5000/lib/_engine/engine/pointer_binding.dart 594:14 57 | http://localhost:5000/lib/_engine/engine/pointer_binding.dart 288:16 loggedHandler 58 | http://localhost:5000/lib/_engine/engine/pointer_binding.dart 179:80 59 | http://localhost:5000/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14 _checkAndCall 60 | http://localhost:5000/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39 dcall 61 | '''); 62 | 63 | /// Flutter on Android 64 | static StackTrace flutterAndroid() => StackTrace.fromString(''' 65 | #0 SinglePrettyPrinter.getCaller (package:roggle/src/printers/single_pretty_printer.dart:127:26) 66 | #1 SinglePrettyPrinter.formatFixed (package:roggle/src/printers/single_pretty_printer.dart:258:22) 67 | #2 SinglePrettyPrinter._formatMessage (package:roggle/src/printers/single_pretty_printer.dart:222:19) 68 | #3 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:115:12) 69 | #4 Roggle.log (package:roggle/src/roggle.dart:104:31) 70 | #5 Roggle.v (package:roggle/src/roggle.dart:60:5) 71 | #6 _MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:55:14) 72 | #7 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21) 73 | #8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24) 74 | #9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11) 75 | #10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5) 76 | #11 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:276:7) 77 | #12 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:163:27) 78 | #13 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:464:20) 79 | #14 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22) 80 | #15 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11) 81 | #16 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7) 82 | #17 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5) 83 | #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7) 84 | #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7) 85 | #20 _invoke1 (dart:ui/hooks.dart:167:13) 86 | #21 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7) 87 | #22 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31) 88 | '''); 89 | 90 | /// Flutter on iOS 91 | static StackTrace flutterIOS() => StackTrace.fromString(''' 92 | #0 SinglePrettyPrinter.getCaller (package:roggle/src/printers/single_pretty_printer.dart:127:26) 93 | #1 SinglePrettyPrinter.formatFixed (package:roggle/src/printers/single_pretty_printer.dart:258:22) 94 | #2 SinglePrettyPrinter._formatMessage (package:roggle/src/printers/single_pretty_printer.dart:222:19) 95 | #3 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:115:12) 96 | #4 Roggle.log (package:roggle/src/roggle.dart:104:31) 97 | #5 Roggle.v (package:roggle/src/roggle.dart:60:5) 98 | #6 _MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:55:14) 99 | #7 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21) 100 | #8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24) 101 | #9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11) 102 | #10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5) 103 | #11 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:276:7) 104 | #12 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:163:27) 105 | #13 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:464:20) 106 | #14 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22) 107 | #15 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11) 108 | #16 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7) 109 | #17 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5) 110 | #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7) 111 | #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7) 112 | #20 _invoke1 (dart:ui/hooks.dart:167:13) 113 | #21 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7) 114 | #22 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31) 115 | '''); 116 | 117 | /// Flutter on Mac 118 | static StackTrace flutterMac() => StackTrace.fromString(''' 119 | #0 SinglePrettyPrinter.getCaller (package:roggle/src/printers/single_pretty_printer.dart:127:26) 120 | #1 SinglePrettyPrinter.formatFixed (package:roggle/src/printers/single_pretty_printer.dart:258:22) 121 | #2 SinglePrettyPrinter._formatMessage (package:roggle/src/printers/single_pretty_printer.dart:222:19) 122 | #3 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:115:12) 123 | #4 Roggle.log (package:roggle/src/roggle.dart:104:31) 124 | #5 Roggle.v (package:roggle/src/roggle.dart:60:5) 125 | #6 _MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:53:14) 126 | #7 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21) 127 | #8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24) 128 | #9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11) 129 | #10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5) 130 | #11 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:276:7) 131 | #12 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:163:27) 132 | #13 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:464:20) 133 | #14 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22) 134 | #15 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11) 135 | #16 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7) 136 | #17 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5) 137 | #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7) 138 | #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7) 139 | #20 _invoke1 (dart:ui/hooks.dart:167:13) 140 | #21 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7) 141 | #22 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31) 142 | '''); 143 | 144 | /// Flutter on Windows 145 | static StackTrace flutterWindows() => StackTrace.fromString(''' 146 | #0 SinglePrettyPrinter.getCaller (package:roggle/src/printers/single_pretty_printer.dart:127:26) 147 | #1 SinglePrettyPrinter.formatFixed (package:roggle/src/printers/single_pretty_printer.dart:259:22) 148 | #2 SinglePrettyPrinter._formatMessage (package:roggle/src/printers/single_pretty_printer.dart:223:19) 149 | #3 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:115:12) 150 | #4 Roggle.log (package:roggle/src/roggle.dart:104:31) 151 | #5 Roggle.v (package:roggle/src/roggle.dart:60:5) 152 | #6 _MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:52:14) 153 | #7 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:1005:21) 154 | #8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:198:24) 155 | #9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:613:11) 156 | #10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:298:5) 157 | #11 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:269:7) 158 | #12 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27) 159 | #13 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:449:20) 160 | #14 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:425:22) 161 | #15 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:329:11) 162 | #16 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:380:7) 163 | #17 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:344:5) 164 | #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:302:7) 165 | #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:285:7) 166 | #20 _rootRunUnary (dart:async/zone.dart:1442:13) 167 | #21 _CustomZone.runUnary (dart:async/zone.dart:1335:19) 168 | #22 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7) 169 | #23 _invoke1 (dart:ui/hooks.dart:170:10) 170 | #24 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7) 171 | #25 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31) 172 | '''); 173 | } 174 | -------------------------------------------------------------------------------- /test/roggle_test.dart: -------------------------------------------------------------------------------- 1 | // ignore: lines_longer_than_80_chars 2 | // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package 3 | 4 | import 'dart:math'; 5 | 6 | import 'package:roggle/roggle.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | import 'test_utils/utils.dart'; 10 | 11 | typedef PrinterCallback = List Function( 12 | Level level, 13 | dynamic message, 14 | dynamic time, 15 | dynamic error, 16 | StackTrace? stackTrace, 17 | ); 18 | 19 | class _AlwaysFilter extends LogFilter { 20 | @override 21 | bool shouldLog(LogEvent event) => true; 22 | } 23 | 24 | class _NeverFilter extends LogFilter { 25 | @override 26 | bool shouldLog(LogEvent event) => false; 27 | } 28 | 29 | class _CallbackPrinter extends LogPrinter { 30 | _CallbackPrinter(this.callback); 31 | 32 | final PrinterCallback callback; 33 | 34 | @override 35 | List log(LogEvent event) { 36 | return callback( 37 | event.level, 38 | event.message, 39 | event.time, 40 | event.error, 41 | event.stackTrace, 42 | ); 43 | } 44 | } 45 | 46 | void main() { 47 | Level? printedLevel; 48 | dynamic printedMessage; 49 | dynamic printedTime; 50 | dynamic printedError; 51 | StackTrace? printedStackTrace; 52 | final callbackPrinter = 53 | _CallbackPrinter((l, dynamic m, dynamic t, dynamic e, s) { 54 | printedLevel = l; 55 | printedMessage = m; 56 | printedTime = t; 57 | printedError = e; 58 | printedStackTrace = s; 59 | return []; 60 | }); 61 | 62 | setUp(() { 63 | printedLevel = null; 64 | printedMessage = null; 65 | printedTime = null; 66 | printedError = null; 67 | printedStackTrace = null; 68 | }); 69 | 70 | group('Constructor', () { 71 | test('default', () { 72 | Roggle().d('some message'); 73 | }); 74 | test('filter', () { 75 | Roggle(filter: _NeverFilter(), printer: callbackPrinter) 76 | .log(Level.debug, 'Some message'); 77 | 78 | expect(printedMessage, null); 79 | }); 80 | }); 81 | 82 | group('factory Roggle.crashlytics()', () { 83 | test('default', () { 84 | Roggle.crashlytics( 85 | printer: CrashlyticsPrinter( 86 | errorLevel: Level.off, 87 | onError: (_) {}, 88 | ), 89 | ).d('some message'); 90 | }); 91 | }); 92 | 93 | test('Roggle.log', () { 94 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 95 | 96 | final levels = getAvailableLogLevel(); 97 | for (final level in levels) { 98 | var message = Random().nextInt(999999999).toString(); 99 | logger.log(level, message); 100 | expect(printedLevel, level); 101 | expect(printedMessage, message); 102 | expect(printedTime, isNotNull); 103 | expect(printedError, null); 104 | expect(printedStackTrace, null); 105 | 106 | logger.log(level, null); 107 | expect(printedLevel, level); 108 | expect(printedMessage, null); 109 | expect(printedTime, isNotNull); 110 | expect(printedError, null); 111 | expect(printedStackTrace, null); 112 | 113 | // 2023/10/14 09:00:00 114 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 115 | logger.log(level, message, time: time); 116 | expect(printedLevel, level); 117 | expect(printedMessage, message); 118 | expect(printedTime, time); 119 | expect(printedError, null); 120 | expect(printedStackTrace, null); 121 | 122 | message = Random().nextInt(999999999).toString(); 123 | logger.log(level, message, error: 'MyError'); 124 | expect(printedLevel, level); 125 | expect(printedMessage, message); 126 | expect(printedTime, isNotNull); 127 | expect(printedError, 'MyError'); 128 | expect(printedStackTrace, null); 129 | 130 | message = Random().nextInt(999999999).toString(); 131 | final stackTrace = StackTrace.current; 132 | logger.log(level, message, error: 'MyError', stackTrace: stackTrace); 133 | expect(printedLevel, level); 134 | expect(printedMessage, message); 135 | expect(printedTime, isNotNull); 136 | expect(printedError, 'MyError'); 137 | expect(printedStackTrace, stackTrace); 138 | } 139 | 140 | expect( 141 | () => logger.log(Level.trace, 'Test', error: StackTrace.current), 142 | throwsArgumentError, 143 | ); 144 | 145 | expect(() => logger.log(Level.verbose, 'Test'), throwsArgumentError); 146 | expect(() => logger.log(Level.wtf, 'Test'), throwsArgumentError); 147 | expect(() => logger.log(Level.off, 'Test'), throwsArgumentError); 148 | expect(() => logger.log(Level.all, 'Test'), throwsArgumentError); 149 | expect(() => logger.log(Level.nothing, 'Test'), throwsArgumentError); 150 | 151 | logger.close(); 152 | expect(() => logger.log(Level.trace, 'Test'), throwsArgumentError); 153 | 154 | // Execute close() twice 155 | logger.close(); 156 | expect(() => logger.log(Level.trace, 'Test'), throwsArgumentError); 157 | }); 158 | 159 | test('Roggle.v', () { 160 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 161 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 162 | final stackTrace = StackTrace.current; 163 | logger.v('Test', time: time, error: 'Error', stackTrace: stackTrace); 164 | expect(printedLevel, Level.trace); 165 | expect(printedTime, time); 166 | expect(printedMessage, 'Test'); 167 | expect(printedError, 'Error'); 168 | expect(printedStackTrace, stackTrace); 169 | 170 | logger.v(null); 171 | expect(printedLevel, Level.trace); 172 | expect(printedTime, isNotNull); 173 | expect(printedMessage, null); 174 | expect(printedError, null); 175 | expect(printedStackTrace, null); 176 | }); 177 | 178 | test('Roggle.t', () { 179 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 180 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 181 | final stackTrace = StackTrace.current; 182 | logger.t('Test', time: time, error: 'Error', stackTrace: stackTrace); 183 | expect(printedLevel, Level.trace); 184 | expect(printedTime, time); 185 | expect(printedMessage, 'Test'); 186 | expect(printedError, 'Error'); 187 | expect(printedStackTrace, stackTrace); 188 | 189 | logger.t(null); 190 | expect(printedLevel, Level.trace); 191 | expect(printedTime, isNotNull); 192 | expect(printedMessage, null); 193 | expect(printedError, null); 194 | expect(printedStackTrace, null); 195 | }); 196 | 197 | test('Roggle.d', () { 198 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 199 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 200 | final stackTrace = StackTrace.current; 201 | logger.d('Test', time: time, error: 'Error', stackTrace: stackTrace); 202 | expect(printedLevel, Level.debug); 203 | expect(printedTime, time); 204 | expect(printedMessage, 'Test'); 205 | expect(printedError, 'Error'); 206 | expect(printedStackTrace, stackTrace); 207 | 208 | logger.d(null); 209 | expect(printedLevel, Level.debug); 210 | expect(printedTime, isNotNull); 211 | expect(printedMessage, null); 212 | expect(printedError, null); 213 | expect(printedStackTrace, null); 214 | }); 215 | 216 | test('Roggle.i', () { 217 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 218 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 219 | final stackTrace = StackTrace.current; 220 | logger.i('Test', time: time, error: 'Error', stackTrace: stackTrace); 221 | expect(printedLevel, Level.info); 222 | expect(printedTime, time); 223 | expect(printedMessage, 'Test'); 224 | expect(printedError, 'Error'); 225 | expect(printedStackTrace, stackTrace); 226 | 227 | logger.i(null); 228 | expect(printedLevel, Level.info); 229 | expect(printedTime, isNotNull); 230 | expect(printedMessage, null); 231 | expect(printedError, null); 232 | expect(printedStackTrace, null); 233 | }); 234 | 235 | test('Roggle.w', () { 236 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 237 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 238 | final stackTrace = StackTrace.current; 239 | logger.w('Test', time: time, error: 'Error', stackTrace: stackTrace); 240 | expect(printedLevel, Level.warning); 241 | expect(printedTime, time); 242 | expect(printedMessage, 'Test'); 243 | expect(printedError, 'Error'); 244 | expect(printedStackTrace, stackTrace); 245 | 246 | logger.w(null); 247 | expect(printedLevel, Level.warning); 248 | expect(printedTime, isNotNull); 249 | expect(printedMessage, null); 250 | expect(printedError, null); 251 | expect(printedStackTrace, null); 252 | }); 253 | 254 | test('Roggle.e', () { 255 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 256 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 257 | final stackTrace = StackTrace.current; 258 | logger.e('Test', time: time, error: 'Error', stackTrace: stackTrace); 259 | expect(printedLevel, Level.error); 260 | expect(printedTime, time); 261 | expect(printedMessage, 'Test'); 262 | expect(printedError, 'Error'); 263 | expect(printedStackTrace, stackTrace); 264 | 265 | logger.e(null); 266 | expect(printedLevel, Level.error); 267 | expect(printedTime, isNotNull); 268 | expect(printedMessage, null); 269 | expect(printedError, null); 270 | expect(printedStackTrace, null); 271 | }); 272 | 273 | test('Roggle.wtf', () { 274 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 275 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 276 | final stackTrace = StackTrace.current; 277 | logger.wtf('Test', time: time, error: 'Error', stackTrace: stackTrace); 278 | expect(printedLevel, Level.fatal); 279 | expect(printedTime, time); 280 | expect(printedMessage, 'Test'); 281 | expect(printedError, 'Error'); 282 | expect(printedStackTrace, stackTrace); 283 | 284 | logger.wtf(null); 285 | expect(printedLevel, Level.fatal); 286 | expect(printedTime, isNotNull); 287 | expect(printedMessage, null); 288 | expect(printedError, null); 289 | expect(printedStackTrace, null); 290 | }); 291 | 292 | test('Roggle.f', () { 293 | final logger = Roggle(filter: _AlwaysFilter(), printer: callbackPrinter); 294 | final time = DateTime.fromMillisecondsSinceEpoch(1684177200000); 295 | final stackTrace = StackTrace.current; 296 | logger.f('Test', time: time, error: 'Error', stackTrace: stackTrace); 297 | expect(printedLevel, Level.fatal); 298 | expect(printedTime, time); 299 | expect(printedMessage, 'Test'); 300 | expect(printedError, 'Error'); 301 | expect(printedStackTrace, stackTrace); 302 | 303 | logger.f(null); 304 | expect(printedLevel, Level.fatal); 305 | expect(printedTime, isNotNull); 306 | expect(printedMessage, null); 307 | expect(printedError, null); 308 | expect(printedStackTrace, null); 309 | }); 310 | 311 | test('setting log level above log level of message', () { 312 | printedMessage = null; 313 | final logger = Roggle( 314 | filter: ProductionFilter(), 315 | printer: callbackPrinter, 316 | level: Level.warning, 317 | )..d('This isn\'t logged'); 318 | expect(printedMessage, isNull); 319 | 320 | logger.w('This is'); 321 | expect(printedMessage, 'This is'); 322 | }); 323 | 324 | test('setting static log level above log level of message', () { 325 | printedMessage = null; 326 | Roggle.level = Level.warning; 327 | final logger = Roggle( 328 | filter: ProductionFilter(), 329 | printer: callbackPrinter, 330 | )..d('This isn\'t logged'); 331 | expect(printedMessage, isNull); 332 | 333 | logger.w('This is'); 334 | expect(printedMessage, 'This is'); 335 | 336 | Roggle.level = Level.trace; 337 | }); 338 | 339 | test('get filter', () { 340 | final filter = ProductionFilter(); 341 | final logger = Roggle( 342 | filter: filter, 343 | ); 344 | expect(logger.filter.hashCode, filter.hashCode); 345 | }); 346 | 347 | test('get printer', () { 348 | final printer = SinglePrettyPrinter(); 349 | final logger = Roggle( 350 | printer: printer, 351 | ); 352 | expect(logger.printer.hashCode, printer.hashCode); 353 | }); 354 | 355 | test('get output', () { 356 | final output = ConsoleOutput(); 357 | final logger = Roggle( 358 | output: output, 359 | ); 360 | expect(logger.output.hashCode, output.hashCode); 361 | }); 362 | 363 | test('isClosed', () { 364 | final logger = Roggle(); 365 | expect(logger.active, true); 366 | expect(logger.isClosed(), false); 367 | 368 | logger.close(); 369 | expect(logger.active, false); 370 | expect(logger.isClosed(), true); 371 | }); 372 | 373 | test('default filter', () { 374 | var logger = Roggle(); 375 | expect(logger.filter is DevelopmentFilter, true); 376 | Roggle.defaultFilter = ProductionFilter.new; 377 | logger = Roggle(); 378 | expect(logger.filter is ProductionFilter, true); 379 | }); 380 | 381 | test('default printer', () { 382 | var logger = Roggle(); 383 | expect(logger.printer is SinglePrettyPrinter, true); 384 | Roggle.defaultPrinter = PrettyPrinter.new; 385 | logger = Roggle(); 386 | expect(logger.printer is PrettyPrinter, true); 387 | }); 388 | 389 | test('default output', () { 390 | var logger = Roggle(); 391 | expect(logger.output is ConsoleOutput, true); 392 | Roggle.defaultOutput = StreamOutput.new; 393 | logger = Roggle(); 394 | expect(logger.output is StreamOutput, true); 395 | }); 396 | 397 | test('logCallback', () { 398 | LogEvent? receivedEvent; 399 | void callback(LogEvent event) { 400 | receivedEvent = event; 401 | } 402 | 403 | final logger = Roggle(); 404 | Roggle.addLogListener(callback); 405 | logger.t('Test'); 406 | expect(receivedEvent, isNotNull); 407 | 408 | receivedEvent = null; 409 | Roggle.removeLogListener(callback); 410 | logger.t('Test'); 411 | expect(receivedEvent, isNull); 412 | }); 413 | 414 | test('outputCallback', () { 415 | OutputEvent? receivedEvent; 416 | void callback(OutputEvent event) { 417 | receivedEvent = event; 418 | } 419 | 420 | final logger = Roggle(); 421 | Roggle.addOutputListener(callback); 422 | logger.t('Test'); 423 | expect(receivedEvent, isNotNull); 424 | 425 | receivedEvent = null; 426 | Roggle.removeOutputListener(callback); 427 | logger.t('Test'); 428 | expect(receivedEvent, isNull); 429 | }); 430 | } 431 | -------------------------------------------------------------------------------- /test/printers/crashlytics_printer_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: deprecated_member_use 2 | 3 | import 'package:roggle/roggle.dart'; 4 | import 'package:stack_trace/stack_trace.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../test_utils/utils.dart'; 8 | 9 | void main() { 10 | Level? printedErrorLevel; 11 | dynamic printedErrorException; 12 | StackTrace? printedErrorStack; 13 | Level? printedLogLevel; 14 | String? printedLogMessage; 15 | 16 | void onError(CrashlyticsErrorEvent event) { 17 | printedErrorLevel = event.level; 18 | printedErrorException = event.exception; 19 | printedErrorStack = event.stack; 20 | } 21 | 22 | void onLog(CrashlyticsLogEvent event) { 23 | printedLogLevel = event.level; 24 | printedLogMessage = event.message; 25 | } 26 | 27 | setUp(() { 28 | printedErrorLevel = null; 29 | printedErrorException = null; 30 | printedErrorStack = null; 31 | printedLogLevel = null; 32 | printedLogMessage = null; 33 | }); 34 | 35 | group('errorLevel', () { 36 | test('errorLevel is nothing', () { 37 | final printer = CrashlyticsPrinter( 38 | errorLevel: Level.nothing, 39 | onError: onError, 40 | onLog: onLog, 41 | ); 42 | 43 | final levels = getAvailableLogLevel(); 44 | for (final level in levels) { 45 | final event = LogEvent( 46 | level, 47 | '', 48 | ); 49 | printer.log(event); 50 | expect(printedLogLevel, level); 51 | expect(printedLogMessage, isNotNull); 52 | switch (level) { 53 | case Level.verbose: 54 | case Level.trace: 55 | case Level.debug: 56 | case Level.info: 57 | case Level.warning: 58 | case Level.error: 59 | case Level.wtf: 60 | case Level.fatal: 61 | expect(printedErrorLevel, isNull); 62 | expect(printedErrorException, isNull); 63 | expect(printedErrorStack, isNull); 64 | break; 65 | case Level.all: 66 | case Level.nothing: 67 | case Level.off: 68 | // not reachable 69 | break; 70 | } 71 | } 72 | }); 73 | test('errorLevel is off', () { 74 | final printer = CrashlyticsPrinter( 75 | errorLevel: Level.off, 76 | onError: onError, 77 | onLog: onLog, 78 | ); 79 | 80 | final levels = getAvailableLogLevel(); 81 | for (final level in levels) { 82 | final event = LogEvent( 83 | level, 84 | '', 85 | ); 86 | printer.log(event); 87 | expect(printedLogLevel, level); 88 | expect(printedLogMessage, isNotNull); 89 | switch (level) { 90 | case Level.verbose: 91 | case Level.trace: 92 | case Level.debug: 93 | case Level.info: 94 | case Level.warning: 95 | case Level.error: 96 | case Level.wtf: 97 | case Level.fatal: 98 | expect(printedErrorLevel, isNull); 99 | expect(printedErrorException, isNull); 100 | expect(printedErrorStack, isNull); 101 | break; 102 | case Level.all: 103 | case Level.nothing: 104 | case Level.off: 105 | // not reachable 106 | break; 107 | } 108 | } 109 | }); 110 | test('errorLevel is verbose', () { 111 | const expectedMessage = 'some message'; 112 | final printer = CrashlyticsPrinter( 113 | errorLevel: Level.verbose, 114 | onError: onError, 115 | onLog: onLog, 116 | ); 117 | 118 | final levels = getAvailableLogLevel(); 119 | for (final level in levels) { 120 | final event = LogEvent( 121 | level, 122 | expectedMessage, 123 | ); 124 | printer.log(event); 125 | expect(printedLogLevel, level); 126 | expect(printedLogMessage, isNotNull); 127 | switch (level) { 128 | case Level.verbose: 129 | case Level.trace: 130 | case Level.debug: 131 | case Level.info: 132 | case Level.warning: 133 | case Level.error: 134 | case Level.wtf: 135 | case Level.fatal: 136 | expect(printedErrorLevel, level); 137 | expect(printedErrorException, expectedMessage); 138 | expect(printedErrorStack, isNotNull); 139 | break; 140 | case Level.all: 141 | case Level.nothing: 142 | case Level.off: 143 | // not reachable 144 | break; 145 | } 146 | } 147 | }); 148 | test('errorLevel is trace', () { 149 | const expectedMessage = 'some message'; 150 | final printer = CrashlyticsPrinter( 151 | errorLevel: Level.trace, 152 | onError: onError, 153 | onLog: onLog, 154 | ); 155 | 156 | final levels = getAvailableLogLevel(); 157 | for (final level in levels) { 158 | final event = LogEvent( 159 | level, 160 | expectedMessage, 161 | ); 162 | printer.log(event); 163 | expect(printedLogLevel, level); 164 | expect(printedLogMessage, isNotNull); 165 | switch (level) { 166 | case Level.verbose: 167 | expect(printedErrorLevel, isNull); 168 | expect(printedErrorException, isNull); 169 | expect(printedErrorStack, isNull); 170 | break; 171 | case Level.trace: 172 | case Level.debug: 173 | case Level.info: 174 | case Level.warning: 175 | case Level.error: 176 | case Level.wtf: 177 | case Level.fatal: 178 | expect(printedErrorLevel, level); 179 | expect(printedErrorException, expectedMessage); 180 | expect(printedErrorStack, isNotNull); 181 | break; 182 | case Level.all: 183 | case Level.nothing: 184 | case Level.off: 185 | // not reachable 186 | break; 187 | } 188 | } 189 | }); 190 | test('errorLevel is debug', () { 191 | const expectedMessage = 'some message'; 192 | final printer = CrashlyticsPrinter( 193 | errorLevel: Level.debug, 194 | onError: onError, 195 | onLog: onLog, 196 | ); 197 | 198 | final levels = getAvailableLogLevel(); 199 | for (final level in levels) { 200 | final event = LogEvent( 201 | level, 202 | expectedMessage, 203 | ); 204 | printer.log(event); 205 | expect(printedLogLevel, level); 206 | expect(printedLogMessage, isNotNull); 207 | switch (level) { 208 | case Level.verbose: 209 | case Level.trace: 210 | expect(printedErrorLevel, isNull); 211 | expect(printedErrorException, isNull); 212 | expect(printedErrorStack, isNull); 213 | break; 214 | case Level.debug: 215 | case Level.info: 216 | case Level.warning: 217 | case Level.error: 218 | case Level.wtf: 219 | case Level.fatal: 220 | expect(printedErrorLevel, level); 221 | expect(printedErrorException, expectedMessage); 222 | expect(printedErrorStack, isNotNull); 223 | break; 224 | case Level.all: 225 | case Level.nothing: 226 | case Level.off: 227 | // not reachable 228 | break; 229 | } 230 | } 231 | }); 232 | test('errorLevel is info', () { 233 | const expectedMessage = 'some message'; 234 | final printer = CrashlyticsPrinter( 235 | errorLevel: Level.info, 236 | onError: onError, 237 | onLog: onLog, 238 | ); 239 | 240 | final levels = getAvailableLogLevel(); 241 | for (final level in levels) { 242 | final event = LogEvent( 243 | level, 244 | expectedMessage, 245 | ); 246 | printer.log(event); 247 | expect(printedLogLevel, level); 248 | expect(printedLogMessage, isNotNull); 249 | switch (level) { 250 | case Level.verbose: 251 | case Level.trace: 252 | case Level.debug: 253 | expect(printedErrorLevel, isNull); 254 | expect(printedErrorException, isNull); 255 | expect(printedErrorStack, isNull); 256 | break; 257 | case Level.info: 258 | case Level.warning: 259 | case Level.error: 260 | case Level.wtf: 261 | case Level.fatal: 262 | expect(printedErrorLevel, level); 263 | expect(printedErrorException, expectedMessage); 264 | expect(printedErrorStack, isNotNull); 265 | break; 266 | case Level.all: 267 | case Level.nothing: 268 | case Level.off: 269 | // not reachable 270 | break; 271 | } 272 | } 273 | }); 274 | test('errorLevel is warning', () { 275 | const expectedMessage = 'some message'; 276 | final printer = CrashlyticsPrinter( 277 | errorLevel: Level.warning, 278 | onError: onError, 279 | onLog: onLog, 280 | ); 281 | 282 | final levels = getAvailableLogLevel(); 283 | for (final level in levels) { 284 | final event = LogEvent( 285 | level, 286 | expectedMessage, 287 | ); 288 | printer.log(event); 289 | expect(printedLogLevel, level); 290 | expect(printedLogMessage, isNotNull); 291 | switch (level) { 292 | case Level.verbose: 293 | case Level.trace: 294 | case Level.debug: 295 | case Level.info: 296 | expect(printedErrorLevel, isNull); 297 | expect(printedErrorException, isNull); 298 | expect(printedErrorStack, isNull); 299 | break; 300 | case Level.warning: 301 | case Level.error: 302 | case Level.wtf: 303 | case Level.fatal: 304 | expect(printedErrorLevel, level); 305 | expect(printedErrorException, expectedMessage); 306 | expect(printedErrorStack, isNotNull); 307 | break; 308 | case Level.all: 309 | case Level.nothing: 310 | case Level.off: 311 | // not reachable 312 | break; 313 | } 314 | } 315 | }); 316 | test('errorLevel is error', () { 317 | const expectedMessage = 'some message'; 318 | final printer = CrashlyticsPrinter( 319 | errorLevel: Level.error, 320 | onError: onError, 321 | onLog: onLog, 322 | ); 323 | 324 | final levels = getAvailableLogLevel(); 325 | for (final level in levels) { 326 | final event = LogEvent( 327 | level, 328 | expectedMessage, 329 | ); 330 | printer.log(event); 331 | expect(printedLogLevel, level); 332 | expect(printedLogMessage, isNotNull); 333 | switch (level) { 334 | case Level.verbose: 335 | case Level.trace: 336 | case Level.debug: 337 | case Level.info: 338 | case Level.warning: 339 | expect(printedErrorLevel, isNull); 340 | expect(printedErrorException, isNull); 341 | expect(printedErrorStack, isNull); 342 | break; 343 | case Level.error: 344 | case Level.wtf: 345 | case Level.fatal: 346 | expect(printedErrorLevel, level); 347 | expect(printedErrorException, expectedMessage); 348 | expect(printedErrorStack, isNotNull); 349 | break; 350 | case Level.all: 351 | case Level.nothing: 352 | case Level.off: 353 | // not reachable 354 | break; 355 | } 356 | } 357 | }); 358 | test('errorLevel is wtf', () { 359 | const expectedMessage = 'some message'; 360 | final printer = CrashlyticsPrinter( 361 | errorLevel: Level.wtf, 362 | onError: onError, 363 | onLog: onLog, 364 | ); 365 | 366 | final levels = getAvailableLogLevel(); 367 | for (final level in levels) { 368 | final event = LogEvent( 369 | level, 370 | expectedMessage, 371 | ); 372 | printer.log(event); 373 | expect(printedLogLevel, level); 374 | expect(printedLogMessage, isNotNull); 375 | switch (level) { 376 | case Level.verbose: 377 | case Level.trace: 378 | case Level.debug: 379 | case Level.info: 380 | case Level.warning: 381 | case Level.error: 382 | expect(printedErrorLevel, isNull); 383 | expect(printedErrorException, isNull); 384 | expect(printedErrorStack, isNull); 385 | break; 386 | case Level.wtf: 387 | case Level.fatal: 388 | expect(printedErrorLevel, level); 389 | expect(printedErrorException, expectedMessage); 390 | expect(printedErrorStack, isNotNull); 391 | break; 392 | case Level.all: 393 | case Level.nothing: 394 | case Level.off: 395 | // not reachable 396 | break; 397 | } 398 | } 399 | }); 400 | test('errorLevel is fatal', () { 401 | const expectedMessage = 'some message'; 402 | final printer = CrashlyticsPrinter( 403 | errorLevel: Level.fatal, 404 | onError: onError, 405 | onLog: onLog, 406 | ); 407 | 408 | final levels = getAvailableLogLevel(); 409 | for (final level in levels) { 410 | final event = LogEvent( 411 | level, 412 | expectedMessage, 413 | ); 414 | printer.log(event); 415 | expect(printedLogLevel, level); 416 | expect(printedLogMessage, isNotNull); 417 | switch (level) { 418 | case Level.verbose: 419 | case Level.trace: 420 | case Level.debug: 421 | case Level.info: 422 | case Level.warning: 423 | case Level.error: 424 | case Level.wtf: 425 | expect(printedErrorLevel, isNull); 426 | expect(printedErrorException, isNull); 427 | expect(printedErrorStack, isNull); 428 | break; 429 | case Level.fatal: 430 | expect(printedErrorLevel, level); 431 | expect(printedErrorException, expectedMessage); 432 | expect(printedErrorStack, isNotNull); 433 | break; 434 | case Level.all: 435 | case Level.nothing: 436 | case Level.off: 437 | // not reachable 438 | break; 439 | } 440 | } 441 | }); 442 | }); 443 | group('onError', () { 444 | test('error is null and message is String', () { 445 | const expectedMessage = 'some message'; 446 | final printer = CrashlyticsPrinter( 447 | errorLevel: Level.error, 448 | onError: onError, 449 | ); 450 | final event = LogEvent( 451 | Level.error, 452 | expectedMessage, 453 | ); 454 | printer.log(event); 455 | expect(printedErrorException, expectedMessage); 456 | }); 457 | test('error is null and message is Exception', () { 458 | final expectedMessage = Exception('some exception'); 459 | final printer = CrashlyticsPrinter( 460 | errorLevel: Level.error, 461 | onError: onError, 462 | ); 463 | final event = LogEvent( 464 | Level.error, 465 | expectedMessage, 466 | ); 467 | printer.log(event); 468 | expect(printedErrorException, expectedMessage); 469 | }); 470 | test('error is null and message is Error', () { 471 | final expectedMessage = TypeError(); 472 | final printer = CrashlyticsPrinter( 473 | errorLevel: Level.error, 474 | onError: onError, 475 | ); 476 | final event = LogEvent( 477 | Level.error, 478 | expectedMessage, 479 | ); 480 | printer.log(event); 481 | expect(printedErrorException, expectedMessage); 482 | }); 483 | test('error is null and message is null', () { 484 | final printer = CrashlyticsPrinter( 485 | errorLevel: Level.error, 486 | onError: onError, 487 | ); 488 | final event = LogEvent( 489 | Level.error, 490 | null, 491 | ); 492 | printer.log(event); 493 | expect(printedErrorException, 'null'); 494 | }); 495 | test('error is exception and message is string', () { 496 | final expectedError = Exception('some exception'); 497 | final printer = CrashlyticsPrinter( 498 | errorLevel: Level.error, 499 | onError: onError, 500 | ); 501 | final event = LogEvent( 502 | Level.error, 503 | 'some message', 504 | error: expectedError, 505 | ); 506 | printer.log(event); 507 | expect(printedErrorException, expectedError); 508 | }); 509 | test('error is exception and message is string', () { 510 | final expectedError = TypeError(); 511 | final printer = CrashlyticsPrinter( 512 | errorLevel: Level.error, 513 | onError: onError, 514 | ); 515 | final event = LogEvent( 516 | Level.error, 517 | 'some message', 518 | error: expectedError, 519 | ); 520 | printer.log(event); 521 | expect(printedErrorException, expectedError); 522 | }); 523 | test('error is string and message is string', () { 524 | const expectedError = 'some error message'; 525 | final printer = CrashlyticsPrinter( 526 | errorLevel: Level.error, 527 | onError: onError, 528 | ); 529 | final event = LogEvent( 530 | Level.error, 531 | 'some message', 532 | error: expectedError, 533 | ); 534 | printer.log(event); 535 | expect(printedErrorException, expectedError); 536 | }); 537 | test('error is string and message is string and stackTrace is not null', 538 | () { 539 | const expectedError = 'some error message'; 540 | final printer = CrashlyticsPrinter( 541 | errorLevel: Level.error, 542 | onError: onError, 543 | ); 544 | final stackTrace = StackTrace.fromString( 545 | ''' 546 | #0 main.. (file:///Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24) 547 | #1 Declarer.test.. (package:test_api/src/backend/declarer.dart:215:19) 548 | #2 Declarer.test. (package:test_api/src/backend/declarer.dart:213:7) 549 | #3 Invoker._waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:257:7) 550 | ''', 551 | ); 552 | final event = LogEvent( 553 | Level.error, 554 | 'some message', 555 | error: expectedError, 556 | stackTrace: stackTrace, 557 | ); 558 | printer.log(event); 559 | expect(printedErrorException, expectedError); 560 | expect(printedErrorStack, isNotNull); 561 | expect( 562 | printedErrorStack?.toString().contains( 563 | 'main.. (/Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24)', 564 | ), 565 | true, 566 | ); 567 | }); 568 | }); 569 | group('onLog', () { 570 | test('error is string and message is string', () { 571 | const expectedError = 'some error message'; 572 | final printer = CrashlyticsPrinter( 573 | errorLevel: Level.error, 574 | onError: onError, 575 | onLog: onLog, 576 | ); 577 | final event = LogEvent( 578 | Level.error, 579 | 'some message', 580 | error: expectedError, 581 | ); 582 | printer.log(event); 583 | expect(printedErrorException, expectedError); 584 | }); 585 | }); 586 | group('loggerName', () { 587 | test('loggerName is not null', () { 588 | const loggerName = 'LOGGER_NAME'; 589 | final printer = CrashlyticsPrinter( 590 | errorLevel: Level.error, 591 | onError: onError, 592 | onLog: onLog, 593 | loggerName: loggerName, 594 | ); 595 | printer.log(generalEvent); 596 | expect(printedLogMessage?.contains(loggerName), true); 597 | }); 598 | test('loggerName is default(null)', () { 599 | const loggerName = 'LOGGER_NAME'; 600 | final printer = CrashlyticsPrinter( 601 | errorLevel: Level.error, 602 | onError: onError, 603 | onLog: onLog, 604 | ); 605 | printer.log(generalEvent); 606 | expect(printedLogMessage?.contains(loggerName), false); 607 | }); 608 | }); 609 | group('printCaller', () { 610 | test('printCaller is false', () { 611 | final printer = CrashlyticsPrinter( 612 | errorLevel: Level.error, 613 | onError: onError, 614 | onLog: onLog, 615 | printCaller: false, 616 | ); 617 | printer.log(generalEvent); 618 | expect(printedLogMessage?.contains(_getSelfPath()), false); 619 | expect(printedLogMessage?.contains('main..'), false); 620 | }); 621 | test('printCaller is default(true)', () { 622 | final printer = CrashlyticsPrinter( 623 | errorLevel: Level.error, 624 | onError: onError, 625 | onLog: onLog, 626 | ); 627 | printer.log(generalEvent); 628 | expect(printedLogMessage?.contains(_getSelfPath()), true); 629 | expect(printedLogMessage?.contains('main..'), true); 630 | }); 631 | }); 632 | group('printFunctionName', () { 633 | test('printFunctionName is false', () { 634 | final printer = CrashlyticsPrinter( 635 | errorLevel: Level.error, 636 | onError: onError, 637 | onLog: onLog, 638 | printFunctionName: false, 639 | ); 640 | printer.log(generalEvent); 641 | expect(printedLogMessage?.contains(_getSelfPath()), true); 642 | expect(printedLogMessage?.contains('main..'), false); 643 | }); 644 | test('printFunctionName is default(true)', () { 645 | final printer = CrashlyticsPrinter( 646 | errorLevel: Level.error, 647 | onError: onError, 648 | onLog: onLog, 649 | ); 650 | printer.log(generalEvent); 651 | expect(printedLogMessage?.contains(_getSelfPath()), true); 652 | expect(printedLogMessage?.contains('main..'), true); 653 | }); 654 | }); 655 | group('printLocation', () { 656 | test('printLocation is false', () { 657 | final printer = CrashlyticsPrinter( 658 | errorLevel: Level.error, 659 | onError: onError, 660 | onLog: onLog, 661 | printLocation: false, 662 | ); 663 | printer.log(generalEvent); 664 | expect(printedLogMessage?.contains(_getSelfPath()), false); 665 | expect(printedLogMessage?.contains('main..'), true); 666 | }); 667 | test('printLocation is default(true)', () { 668 | final printer = CrashlyticsPrinter( 669 | errorLevel: Level.error, 670 | onError: onError, 671 | onLog: onLog, 672 | ); 673 | printer.log(generalEvent); 674 | expect(printedLogMessage?.contains(_getSelfPath()), true); 675 | expect(printedLogMessage?.contains('main..'), true); 676 | }); 677 | }); 678 | group('printEmojis', () { 679 | test('printEmojis is false', () { 680 | final printer = CrashlyticsPrinter( 681 | errorLevel: Level.error, 682 | onError: onError, 683 | onLog: onLog, 684 | printEmojis: false, 685 | ); 686 | printer.log(generalEvent); 687 | expect( 688 | printedLogMessage?.contains( 689 | SinglePrettyPrinter.defaultLevelEmojis[Level.error]!, 690 | ), 691 | false, 692 | ); 693 | }); 694 | test('printEmojis is default(true)', () { 695 | final printer = CrashlyticsPrinter( 696 | errorLevel: Level.error, 697 | onError: onError, 698 | onLog: onLog, 699 | ); 700 | printer.log(generalEvent); 701 | expect( 702 | printedLogMessage?.contains( 703 | SinglePrettyPrinter.defaultLevelEmojis[Level.error]!, 704 | ), 705 | true, 706 | ); 707 | }); 708 | }); 709 | group('printLabels', () { 710 | test('printLabels is false', () { 711 | final printer = CrashlyticsPrinter( 712 | errorLevel: Level.error, 713 | onError: onError, 714 | onLog: onLog, 715 | printLabels: false, 716 | ); 717 | printer.log(generalEvent); 718 | expect( 719 | printedLogMessage?.contains( 720 | SinglePrettyPrinter.defaultLevelLabels[Level.error]!, 721 | ), 722 | false, 723 | ); 724 | }); 725 | test('printLabels is default(true)', () { 726 | final printer = CrashlyticsPrinter( 727 | errorLevel: Level.error, 728 | onError: onError, 729 | onLog: onLog, 730 | ); 731 | printer.log(generalEvent); 732 | expect( 733 | printedLogMessage?.contains( 734 | SinglePrettyPrinter.defaultLevelLabels[Level.error]!, 735 | ), 736 | true, 737 | ); 738 | }); 739 | }); 740 | group('levelEmojis', () { 741 | test('levelEmojis is specified', () { 742 | final printer = CrashlyticsPrinter( 743 | errorLevel: Level.error, 744 | onError: onError, 745 | onLog: onLog, 746 | levelEmojis: { 747 | ...SinglePrettyPrinter.defaultLevelEmojis, 748 | Level.error: '😅', 749 | }, 750 | ); 751 | printer.log(generalEvent); 752 | expect( 753 | printedLogMessage?.contains('😅'), 754 | true, 755 | ); 756 | expect( 757 | printedLogMessage?.contains( 758 | SinglePrettyPrinter.defaultLevelEmojis[Level.error]!, 759 | ), 760 | false, 761 | ); 762 | }); 763 | test('levelEmojis is not specified', () { 764 | final printer = CrashlyticsPrinter( 765 | errorLevel: Level.error, 766 | onError: onError, 767 | onLog: onLog, 768 | ); 769 | printer.log(generalEvent); 770 | expect( 771 | printedLogMessage?.contains('😅'), 772 | false, 773 | ); 774 | expect( 775 | printedLogMessage?.contains( 776 | SinglePrettyPrinter.defaultLevelEmojis[Level.error]!, 777 | ), 778 | true, 779 | ); 780 | }); 781 | }); 782 | group('levelLabels', () { 783 | test('levelLabels is specified', () { 784 | final printer = CrashlyticsPrinter( 785 | errorLevel: Level.error, 786 | onError: onError, 787 | onLog: onLog, 788 | levelLabels: { 789 | ...SinglePrettyPrinter.defaultLevelLabels, 790 | Level.error: '[error]', 791 | }, 792 | ); 793 | printer.log(generalEvent); 794 | expect( 795 | printedLogMessage?.contains('[error]'), 796 | true, 797 | ); 798 | expect( 799 | printedLogMessage?.contains( 800 | SinglePrettyPrinter.defaultLevelLabels[Level.error]!, 801 | ), 802 | false, 803 | ); 804 | }); 805 | test('levelLabels is not specified', () { 806 | final printer = CrashlyticsPrinter( 807 | errorLevel: Level.error, 808 | onError: onError, 809 | onLog: onLog, 810 | ); 811 | printer.log(generalEvent); 812 | expect( 813 | printedLogMessage?.contains('[error]'), 814 | false, 815 | ); 816 | expect( 817 | printedLogMessage?.contains( 818 | SinglePrettyPrinter.defaultLevelLabels[Level.error]!, 819 | ), 820 | true, 821 | ); 822 | }); 823 | }); 824 | } 825 | 826 | String _getSelfPath() { 827 | final match = RegExp(r'^(.+.dart)') 828 | .firstMatch(Frame.caller(0).toString().replaceAll('\\', '/')); 829 | if (match == null) { 830 | return ''; 831 | } 832 | return match.group(1)!; 833 | } 834 | 835 | LogEvent get generalEvent => LogEvent( 836 | Level.error, 837 | 'some message', 838 | ); 839 | -------------------------------------------------------------------------------- /test/printers/single_pretty_printer_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: invalid_use_of_protected_member 2 | 3 | import 'package:roggle/roggle.dart'; 4 | import 'package:stack_trace/stack_trace.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../test_utils/frame_factory.dart'; 8 | import '../test_utils/platform.dart'; 9 | import '../test_utils/stack_trace_factory.dart'; 10 | 11 | void main() { 12 | group('loggerName', () { 13 | test('loggerName is not null', () { 14 | const loggerName = 'LOGGER_NAME'; 15 | final printer = SinglePrettyPrinter(loggerName: loggerName); 16 | _wrapPropertyTest(printer, loggerName, true); 17 | }); 18 | test('loggerName is default(null)', () { 19 | const loggerName = 'LOGGER_NAME'; 20 | final printer = SinglePrettyPrinter(); 21 | _wrapPropertyTest(printer, loggerName, false); 22 | }); 23 | }); 24 | group('colors', () { 25 | test('colors is false', () { 26 | final printer = SinglePrettyPrinter(colors: false); 27 | _wrapPropertyTest(printer, AnsiColor.ansiEsc, false); 28 | }); 29 | test('colors is default(true)', () { 30 | final printer = SinglePrettyPrinter(); 31 | _wrapPropertyTest(printer, AnsiColor.ansiEsc, true); 32 | }); 33 | }); 34 | group('printCaller', () { 35 | test('printCaller is false', () { 36 | final printer = SinglePrettyPrinter(printCaller: false); 37 | _wrapPropertyTest(printer, _getSelfPath(), false); 38 | }); 39 | test('printCaller is default(true)', () { 40 | final printer = SinglePrettyPrinter(); 41 | _wrapPropertyTest(printer, _getSelfPath(), true); 42 | }); 43 | }); 44 | group('printLocation', () { 45 | test('printLocation is false', () { 46 | final printer = SinglePrettyPrinter(printLocation: false); 47 | _wrapPropertyTest(printer, '_wrapPropertyTest', true); 48 | _wrapPropertyTest(printer, _getSelfPath(), false); 49 | }); 50 | test('printLocation is default(true)', () { 51 | final printer = SinglePrettyPrinter(); 52 | _wrapPropertyTest(printer, '_wrapPropertyTest', true); 53 | _wrapPropertyTest(printer, _getSelfPath(), true); 54 | }); 55 | }); 56 | group('printFunctionName', () { 57 | test('printFunctionName is false', () { 58 | final printer = SinglePrettyPrinter(printFunctionName: false); 59 | _wrapPropertyTest(printer, '_wrapPropertyTest', false); 60 | _wrapPropertyTest(printer, _getSelfPath(), true); 61 | }); 62 | test('printFunctionName is default(true)', () { 63 | final printer = SinglePrettyPrinter(); 64 | _wrapPropertyTest(printer, '_wrapPropertyTest', true); 65 | _wrapPropertyTest(printer, _getSelfPath(), true); 66 | }); 67 | }); 68 | group('printEmojis', () { 69 | test('printEmojis is false', () { 70 | final printer = SinglePrettyPrinter(printEmojis: false); 71 | _wrapPropertyTest( 72 | printer, 73 | SinglePrettyPrinter.defaultLevelEmojis[Level.info]!, 74 | false, 75 | ); 76 | }); 77 | test('printEmojis is default(true)', () { 78 | final printer = SinglePrettyPrinter(); 79 | _wrapPropertyTest( 80 | printer, 81 | SinglePrettyPrinter.defaultLevelEmojis[Level.info]!, 82 | true, 83 | ); 84 | }); 85 | }); 86 | group('printLabels', () { 87 | test('printLabels is false', () { 88 | final printer = SinglePrettyPrinter(printLabels: false); 89 | _wrapPropertyTest( 90 | printer, 91 | SinglePrettyPrinter.defaultLevelLabels[Level.info]!, 92 | false, 93 | ); 94 | }); 95 | test('printLabels is default(true)', () { 96 | final printer = SinglePrettyPrinter(); 97 | _wrapPropertyTest( 98 | printer, 99 | SinglePrettyPrinter.defaultLevelLabels[Level.info]!, 100 | true, 101 | ); 102 | }); 103 | }); 104 | group('printTime', () { 105 | test('printTime is false', () { 106 | final printer = SinglePrettyPrinter(printTime: false); 107 | _wrapPropertyTest(printer, RegExp(r'\d{2}:\d{2}:\d{2}.\d{3}'), false); 108 | }); 109 | test('printTime is default(true)', () { 110 | final printer = SinglePrettyPrinter(); 111 | _wrapPropertyTest(printer, RegExp(r'\d{2}:\d{2}:\d{2}.\d{3}'), true); 112 | }); 113 | }); 114 | group('stackTraceLevel', () { 115 | test('stackTraceLevel is Level.info', () { 116 | final printer = SinglePrettyPrinter(stackTraceLevel: Level.warning); 117 | _wrapPropertyTest( 118 | printer, 119 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 120 | false, 121 | ); 122 | _wrapPropertyTest( 123 | printer, 124 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 125 | false, 126 | ); 127 | }); 128 | test('stackTraceLevel is Level.info', () { 129 | final printer = SinglePrettyPrinter(stackTraceLevel: Level.info); 130 | _wrapPropertyTest( 131 | printer, 132 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 133 | true, 134 | ); 135 | _wrapPropertyTest( 136 | printer, 137 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 138 | true, 139 | ); 140 | }); 141 | test('stackTraceLevel is default(Level.nothing)', () { 142 | final printer = SinglePrettyPrinter(); 143 | _wrapPropertyTest( 144 | printer, 145 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 146 | false, 147 | ); 148 | _wrapPropertyTest( 149 | printer, 150 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 151 | false, 152 | ); 153 | }); 154 | }); 155 | group('stackTraceMethodCount', () { 156 | test('stackTraceMethodCount is null', () { 157 | final printer = SinglePrettyPrinter( 158 | stackTraceLevel: Level.info, 159 | stackTraceMethodCount: null, 160 | ); 161 | _wrapPropertyTest( 162 | printer, 163 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 164 | true, 165 | ); 166 | _wrapPropertyTest( 167 | printer, 168 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 169 | true, 170 | ); 171 | _wrapPropertyTest( 172 | printer, 173 | '${SinglePrettyPrinter.defaultStackTracePrefix}#2', 174 | true, 175 | ); 176 | _wrapPropertyTest( 177 | printer, 178 | '${SinglePrettyPrinter.defaultStackTracePrefix}#3', 179 | true, 180 | ); 181 | }); 182 | test('stackTraceMethodCount is 0', () { 183 | final printer = SinglePrettyPrinter( 184 | stackTraceLevel: Level.info, 185 | stackTraceMethodCount: 0, 186 | ); 187 | _wrapPropertyTest( 188 | printer, 189 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 190 | false, 191 | ); 192 | _wrapPropertyTest( 193 | printer, 194 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 195 | false, 196 | ); 197 | _wrapPropertyTest( 198 | printer, 199 | '${SinglePrettyPrinter.defaultStackTracePrefix}#2', 200 | false, 201 | ); 202 | _wrapPropertyTest( 203 | printer, 204 | '${SinglePrettyPrinter.defaultStackTracePrefix}#3', 205 | false, 206 | ); 207 | }); 208 | test('stackTraceMethodCount is 1', () { 209 | final printer = SinglePrettyPrinter( 210 | stackTraceLevel: Level.info, 211 | stackTraceMethodCount: 1, 212 | ); 213 | _wrapPropertyTest( 214 | printer, 215 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 216 | true, 217 | ); 218 | _wrapPropertyTest( 219 | printer, 220 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 221 | false, 222 | ); 223 | _wrapPropertyTest( 224 | printer, 225 | '${SinglePrettyPrinter.defaultStackTracePrefix}#2', 226 | false, 227 | ); 228 | _wrapPropertyTest( 229 | printer, 230 | '${SinglePrettyPrinter.defaultStackTracePrefix}#3', 231 | false, 232 | ); 233 | }); 234 | test('stackTraceMethodCount is 2', () { 235 | final printer = SinglePrettyPrinter( 236 | stackTraceLevel: Level.info, 237 | stackTraceMethodCount: 2, 238 | ); 239 | _wrapPropertyTest( 240 | printer, 241 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 242 | true, 243 | ); 244 | _wrapPropertyTest( 245 | printer, 246 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 247 | true, 248 | ); 249 | _wrapPropertyTest( 250 | printer, 251 | '${SinglePrettyPrinter.defaultStackTracePrefix}#2', 252 | false, 253 | ); 254 | _wrapPropertyTest( 255 | printer, 256 | '${SinglePrettyPrinter.defaultStackTracePrefix}#3', 257 | false, 258 | ); 259 | }); 260 | test('stackTraceMethodCount is 3', () { 261 | final printer = SinglePrettyPrinter( 262 | stackTraceLevel: Level.info, 263 | stackTraceMethodCount: 3, 264 | ); 265 | _wrapPropertyTest( 266 | printer, 267 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 268 | true, 269 | ); 270 | _wrapPropertyTest( 271 | printer, 272 | '${SinglePrettyPrinter.defaultStackTracePrefix}#1', 273 | true, 274 | ); 275 | _wrapPropertyTest( 276 | printer, 277 | '${SinglePrettyPrinter.defaultStackTracePrefix}#2', 278 | true, 279 | ); 280 | _wrapPropertyTest( 281 | printer, 282 | '${SinglePrettyPrinter.defaultStackTracePrefix}#3', 283 | false, 284 | ); 285 | }); 286 | }); 287 | group('stackTracePrefix', () { 288 | test('stackTracePrefix is >>> ', () { 289 | final printer = SinglePrettyPrinter( 290 | stackTraceLevel: Level.info, 291 | stackTracePrefix: '>>> ', 292 | ); 293 | _wrapPropertyTest(printer, '>>> #0', true); 294 | _wrapPropertyTest( 295 | printer, 296 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 297 | false, 298 | ); 299 | }); 300 | test('stackTracePrefix is default', () { 301 | final printer = SinglePrettyPrinter( 302 | stackTraceLevel: Level.info, 303 | ); 304 | _wrapPropertyTest(printer, '>>> #0', false); 305 | _wrapPropertyTest( 306 | printer, 307 | '${SinglePrettyPrinter.defaultStackTracePrefix}#0', 308 | true, 309 | ); 310 | }); 311 | }); 312 | group('levelColors', () { 313 | test('levelColors is specified', () { 314 | final printer = SinglePrettyPrinter( 315 | levelColors: { 316 | ...SinglePrettyPrinter.defaultLevelColors, 317 | Level.info: const AnsiColor.none(), 318 | }, 319 | ); 320 | _wrapPropertyTest(printer, AnsiColor.ansiEsc, false); 321 | }); 322 | test('levelColors is not specified', () { 323 | final printer = SinglePrettyPrinter(); 324 | _wrapPropertyTest(printer, AnsiColor.ansiEsc, true); 325 | }); 326 | }); 327 | group('levelEmojis', () { 328 | test('levelEmojis is specified', () { 329 | final printer = SinglePrettyPrinter( 330 | levelEmojis: { 331 | ...SinglePrettyPrinter.defaultLevelEmojis, 332 | Level.info: '😅', 333 | }, 334 | ); 335 | _wrapPropertyTest(printer, '😅', true); 336 | _wrapPropertyTest( 337 | printer, 338 | SinglePrettyPrinter.defaultLevelEmojis[Level.info]!, 339 | false, 340 | ); 341 | }); 342 | test('levelEmojis is not specified', () { 343 | final printer = SinglePrettyPrinter(); 344 | _wrapPropertyTest(printer, '😅', false); 345 | _wrapPropertyTest( 346 | printer, 347 | SinglePrettyPrinter.defaultLevelEmojis[Level.info]!, 348 | true, 349 | ); 350 | }); 351 | }); 352 | group('levelLabels', () { 353 | test('levelLabels is specified', () { 354 | final printer = SinglePrettyPrinter( 355 | levelLabels: { 356 | ...SinglePrettyPrinter.defaultLevelLabels, 357 | Level.info: '[info]', 358 | }, 359 | ); 360 | _wrapPropertyTest(printer, '[info]', true); 361 | _wrapPropertyTest( 362 | printer, 363 | SinglePrettyPrinter.defaultLevelLabels[Level.info]!, 364 | false, 365 | ); 366 | }); 367 | test('levelLabels is not specified', () { 368 | final printer = SinglePrettyPrinter(); 369 | _wrapPropertyTest(printer, '[info]', false); 370 | _wrapPropertyTest( 371 | printer, 372 | SinglePrettyPrinter.defaultLevelLabels[Level.info]!, 373 | true, 374 | ); 375 | }); 376 | }); 377 | group('timeFormatter', () { 378 | test('timeFormatter is specified', () { 379 | const expectedText = 'expectedText'; 380 | final printer = SinglePrettyPrinter( 381 | timeFormatter: (now) { 382 | return expectedText; 383 | }, 384 | ); 385 | _wrapPropertyTest(printer, expectedText, true); 386 | }); 387 | test('timeFormatter is not specified', () { 388 | const expectedText = 'expectedText'; 389 | final printer = SinglePrettyPrinter(); 390 | _wrapPropertyTest(printer, expectedText, false); 391 | }); 392 | }); 393 | group('any properties is specified', () { 394 | test('very simple', () { 395 | final printer = SinglePrettyPrinter( 396 | colors: false, 397 | printCaller: false, 398 | printEmojis: false, 399 | printLabels: false, 400 | printTime: false, 401 | ); 402 | _wrapPropertyTest(printer, AnsiColor.ansiEsc, false); 403 | _wrapPropertyTest(printer, _getSelfPath(), false); 404 | _wrapPropertyTest( 405 | printer, 406 | SinglePrettyPrinter.defaultLevelEmojis[Level.info]!, 407 | false, 408 | ); 409 | _wrapPropertyTest( 410 | printer, 411 | SinglePrettyPrinter.defaultLevelLabels[Level.info]!, 412 | false, 413 | ); 414 | _wrapPropertyTest(printer, RegExp(r'\d{2}:\d{2}:\d{2}.\d{3}'), false); 415 | }); 416 | }); 417 | group('log()', () { 418 | test('event.message is null', () { 419 | final printer = SinglePrettyPrinter(); 420 | final event = LogEvent( 421 | Level.info, 422 | null, 423 | ); 424 | final actualLogString = _readMessage(printer.log(event)); 425 | expect(actualLogString.contains('null'), true); 426 | }); 427 | test('event.message is function', () { 428 | final printer = SinglePrettyPrinter(); 429 | final event = LogEvent( 430 | Level.info, 431 | () => 'function message', 432 | ); 433 | final actualLogString = _readMessage(printer.log(event)); 434 | expect(actualLogString.contains('function message'), true); 435 | }); 436 | test('event.error is not null', () { 437 | const expectedMessage = 'some message'; 438 | final expectedError = Exception('some exception'); 439 | final printer = SinglePrettyPrinter(); 440 | final event = LogEvent( 441 | Level.info, 442 | expectedMessage, 443 | error: expectedError, 444 | ); 445 | final actualLogString = _readMessage(printer.log(event)); 446 | expect(actualLogString.contains(expectedMessage), true); 447 | expect(actualLogString.contains(expectedError.toString()), true); 448 | }); 449 | test('event.stackTrace is not null', () { 450 | const expectedMessage = 'some message'; 451 | final stackTrace = StackTrace.fromString( 452 | ''' 453 | #0 main.. (file:///Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24) 454 | #1 Declarer.test.. (package:test_api/src/backend/declarer.dart:215:19) 455 | #2 Declarer.test. (package:test_api/src/backend/declarer.dart:213:7) 456 | #3 Invoker._waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:257:7) 457 | ''', 458 | ); 459 | final printer = SinglePrettyPrinter(); 460 | final event = LogEvent( 461 | Level.info, 462 | expectedMessage, 463 | stackTrace: stackTrace, 464 | ); 465 | final actualLogString = _readMessage(printer.log(event)); 466 | expect(actualLogString.contains(expectedMessage), true); 467 | expect( 468 | actualLogString.contains( 469 | 'main.. (/Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24)', 470 | ), 471 | true, 472 | ); 473 | }); 474 | test('event.error is not null and event.stackTrace is not null', () { 475 | const expectedMessage = 'some message'; 476 | final expectedError = Exception('some exception'); 477 | final stackTrace = StackTrace.fromString( 478 | ''' 479 | #0 main.. (file:///Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24) 480 | #1 Declarer.test.. (package:test_api/src/backend/declarer.dart:215:19) 481 | #2 Declarer.test. (package:test_api/src/backend/declarer.dart:213:7) 482 | #3 Invoker._waitForOutstandingCallbacks. (package:test_api/src/backend/invoker.dart:257:7) 483 | ''', 484 | ); 485 | final printer = SinglePrettyPrinter(); 486 | final event = LogEvent( 487 | Level.info, 488 | expectedMessage, 489 | error: expectedError, 490 | stackTrace: stackTrace, 491 | ); 492 | final actualLogString = _readMessage(printer.log(event)); 493 | expect(actualLogString.contains(expectedMessage), true); 494 | expect(actualLogString.contains(expectedError.toString()), true); 495 | expect( 496 | actualLogString.contains( 497 | 'main.. (/Users/dummy/Develop/roggle/test/printers/single_pretty_printer_test.dart:384:24)', 498 | ), 499 | true, 500 | ); 501 | }); 502 | }); 503 | group('getCaller()', () { 504 | test('device', () { 505 | _wrapCallerTest( 506 | '#0 demo (file:///Users/dummy/Develop/roggle/example/main.dart:22:20)', 507 | 'demo (/Users/dummy/Develop/roggle/example/main.dart:22:20)', 508 | ); 509 | _wrapCallerTest( 510 | '#0 _MyHomePageState._incrementCounter (package:flutter_sample_custom_logger/main.dart:51:22)', 511 | '_MyHomePageState._incrementCounter (package:flutter_sample_custom_logger/main.dart:51:22)', 512 | ); 513 | _wrapCallerTest( 514 | ''' 515 | #0 SinglePrettyPrinter.log (package:roggle/src/printers/single_pretty_printer.dart:116:22) 516 | #1 Roggle.log (package:roggle/src/roggle.dart:90:31) 517 | #2 Roggle.v (package:roggle/src/roggle.dart:46:5) 518 | #3 main (package:flutter_sample_custom_logger/main.dart:5:10) 519 | #4 _runMainZoned.. (dart:ui/hooks.dart:130:25) 520 | ''', 521 | 'main (package:flutter_sample_custom_logger/main.dart:5:10)', 522 | ); 523 | }); 524 | test('web', () { 525 | _wrapCallerTest( 526 | ''' 527 | dart:/lib/_internal/js_dev_runtime/patch/core_patch.dart 910:28 get current 528 | packages/flutter_sample_custom_logger/logger.dart 116:11 log 529 | packages/roggle/src/roggle.dart 90:31 log 530 | packages/roggle/src/roggle.dart 46:5 v 531 | packages/flutter_sample_custom_logger/main.dart 5:10 main 532 | ''', 533 | 'log (package:flutter_sample_custom_logger/logger.dart:116:11)', 534 | ); 535 | }); 536 | test('Dart on Mac', () { 537 | _wrapCallerTest2( 538 | StackTraceFactory.dartMac(), 539 | 'demo (/Users/dummy/Develop/roggle/example/main.dart:66:10)', 540 | ); 541 | }); 542 | test('Dart on Windows', () { 543 | _wrapCallerTest2( 544 | StackTraceFactory.dartWindows(), 545 | 'demo (example/main.dart:67:10)', 546 | ); 547 | }); 548 | test('Flutter on Web', () { 549 | var expectCaller = 550 | '[_incrementCounter] (http://localhost:5000/packages/flutter_sample_roggle/main.dart:55:14)'; 551 | if (kIsWeb) { 552 | expectCaller = 553 | '[_incrementCounter] (package:flutter_sample_roggle/main.dart:55:14)'; 554 | } 555 | _wrapCallerTest2( 556 | StackTraceFactory.flutterWeb(), 557 | expectCaller, 558 | ); 559 | }); 560 | test('Flutter on Android', () { 561 | _wrapCallerTest2( 562 | StackTraceFactory.flutterAndroid(), 563 | '_MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:55:14)', 564 | ); 565 | }); 566 | test('Flutter on iOS', () { 567 | _wrapCallerTest2( 568 | StackTraceFactory.flutterIOS(), 569 | '_MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:55:14)', 570 | ); 571 | }); 572 | test('Flutter on Mac', () { 573 | _wrapCallerTest2( 574 | StackTraceFactory.flutterMac(), 575 | '_MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:53:14)', 576 | ); 577 | }); 578 | test('Flutter on Windows', () { 579 | _wrapCallerTest2( 580 | StackTraceFactory.flutterWindows(), 581 | '_MyHomePageState._incrementCounter (package:flutter_sample_roggle/main.dart:52:14)', 582 | ); 583 | }); 584 | }); 585 | group('formatTime()', () { 586 | test('formatTime()', () { 587 | _wrapCurrentTimeTest( 588 | DateTime(2022), 589 | '00:00:00.000', 590 | ); 591 | _wrapCurrentTimeTest( 592 | DateTime(2022, 1, 1, 10, 10, 10, 10), 593 | '10:10:10.010', 594 | ); 595 | _wrapCurrentTimeTest( 596 | DateTime(2022, 1, 1, 20, 20, 20, 100), 597 | '20:20:20.100', 598 | ); 599 | }); 600 | }); 601 | group('getStackTrace()', () { 602 | test('with stackTracePrefix', () { 603 | final printer = SinglePrettyPrinter(); 604 | final lines = printer.getStackTrace( 605 | stackTrace: StackTrace.current, 606 | ); 607 | for (final line in lines) { 608 | final isMatch = RegExp(r'^\│\s#[0-9\s]{7}').hasMatch(line); 609 | expect(isMatch, true); 610 | } 611 | }); 612 | test('without stackTracePrefix', () { 613 | final printer = SinglePrettyPrinter( 614 | stackTracePrefix: '', 615 | ); 616 | final lines = printer.getStackTrace( 617 | stackTrace: StackTrace.current, 618 | ); 619 | for (final line in lines) { 620 | final isMatch = RegExp(r'^#[0-9\s]{7}').hasMatch(line); 621 | expect(isMatch, true); 622 | } 623 | }); 624 | }); 625 | group('convertToCallerString()', () { 626 | test('Dart on Mac', () { 627 | _wrapConvertToCallerStringTest( 628 | FrameFactory.dartMac(), 629 | 'dummy (/Users/dummy/Develop/roggle/example/main.dart:66:10)', 630 | ); 631 | }); 632 | if (kIsWindows) { 633 | test('Dart on Windows', () { 634 | _wrapConvertToCallerStringTest( 635 | FrameFactory.dartWindows(), 636 | 'dummy (C:/Users/dummy/Develop/roggle/example/main.dart:66:10)', 637 | ); 638 | }); 639 | } 640 | test('Flutter on Web', () { 641 | var expectCallerString = 642 | 'dummy (http://localhost:62180/packages/flutter_sample_roggle/main.dart:66:10)'; 643 | if (kIsWeb) { 644 | expectCallerString = 645 | 'dummy (package:flutter_sample_roggle/main.dart:66:10)'; 646 | } 647 | _wrapConvertToCallerStringTest( 648 | FrameFactory.flutterWeb(), 649 | expectCallerString, 650 | ); 651 | }); 652 | test('Flutter on Android', () { 653 | _wrapConvertToCallerStringTest( 654 | FrameFactory.flutterAndroid(), 655 | 'dummy (package:flutter_sample_roggle/main.dart:66:10)', 656 | ); 657 | }); 658 | test('Flutter on iOS', () { 659 | _wrapConvertToCallerStringTest( 660 | FrameFactory.flutterIOS(), 661 | 'dummy (package:flutter_sample_roggle/main.dart:66:10)', 662 | ); 663 | }); 664 | test('Flutter on Mac', () { 665 | _wrapConvertToCallerStringTest( 666 | FrameFactory.flutterMac(), 667 | 'dummy (package:flutter_sample_roggle/main.dart:66:10)', 668 | ); 669 | }); 670 | test('Flutter on Windows', () { 671 | _wrapConvertToCallerStringTest( 672 | FrameFactory.flutterWindows(), 673 | 'dummy (package:flutter_sample_roggle/main.dart:66:10)', 674 | ); 675 | }); 676 | test('Frame has uri', () { 677 | _wrapConvertToCallerStringTest( 678 | Frame( 679 | Uri.parse('package:flutter_sample_roggle/main.dart'), 680 | null, 681 | null, 682 | null, 683 | ), 684 | '(package:flutter_sample_roggle/main.dart)', 685 | ); 686 | }); 687 | test('Frame has uri, line', () { 688 | _wrapConvertToCallerStringTest( 689 | Frame( 690 | Uri.parse('package:flutter_sample_roggle/main.dart'), 691 | 66, 692 | null, 693 | null, 694 | ), 695 | '(package:flutter_sample_roggle/main.dart:66)', 696 | ); 697 | }); 698 | test('Frame has uri, line, column', () { 699 | _wrapConvertToCallerStringTest( 700 | Frame( 701 | Uri.parse('package:flutter_sample_roggle/main.dart'), 702 | 66, 703 | 10, 704 | null, 705 | ), 706 | '(package:flutter_sample_roggle/main.dart:66:10)', 707 | ); 708 | }); 709 | test('Frame has uri, member', () { 710 | _wrapConvertToCallerStringTest( 711 | Frame( 712 | Uri.parse('package:flutter_sample_roggle/main.dart'), 713 | null, 714 | null, 715 | 'dummy', 716 | ), 717 | 'dummy (package:flutter_sample_roggle/main.dart)', 718 | ); 719 | }); 720 | test('Frame has uri, line, member', () { 721 | _wrapConvertToCallerStringTest( 722 | Frame( 723 | Uri.parse('package:flutter_sample_roggle/main.dart'), 724 | 68, 725 | null, 726 | 'dummy', 727 | ), 728 | 'dummy (package:flutter_sample_roggle/main.dart:68)', 729 | ); 730 | }); 731 | test('Frame has uri, column, member', () { 732 | _wrapConvertToCallerStringTest( 733 | Frame( 734 | Uri.parse('package:flutter_sample_roggle/main.dart'), 735 | null, 736 | 10, 737 | 'dummy', 738 | ), 739 | 'dummy (package:flutter_sample_roggle/main.dart)', 740 | ); 741 | }); 742 | }); 743 | } 744 | 745 | String _readMessage(List log) { 746 | return log.reduce((acc, val) => acc + val); 747 | } 748 | 749 | String _getSelfPath() { 750 | final match = RegExp(r'^(.+.dart)') 751 | .firstMatch(Frame.caller(0).toString().replaceAll('\\', '/')); 752 | if (match == null) { 753 | return ''; 754 | } 755 | return match.group(1)!; 756 | } 757 | 758 | void _wrapPropertyTest( 759 | SinglePrettyPrinter printer, 760 | Pattern pattern, 761 | bool expectContain, 762 | ) { 763 | const expectedMessage = 'some message'; 764 | final event = LogEvent( 765 | Level.info, 766 | expectedMessage, 767 | ); 768 | 769 | final actualLogString = _readMessage(printer.log(event)); 770 | expect(actualLogString.contains(pattern), expectContain); 771 | expect(actualLogString.contains(expectedMessage), true); 772 | } 773 | 774 | void _wrapCallerTest( 775 | String stackTraceString, 776 | String? expectCaller, 777 | ) { 778 | final printer = SinglePrettyPrinter(); 779 | final actualCaller = printer.getCaller( 780 | stackTrace: StackTrace.fromString(stackTraceString), 781 | ); 782 | expect(actualCaller, expectCaller); 783 | } 784 | 785 | void _wrapCallerTest2( 786 | StackTrace stackTrace, 787 | String? expectCaller, 788 | ) { 789 | final printer = SinglePrettyPrinter(); 790 | final actualCaller = printer.getCaller( 791 | stackTrace: stackTrace, 792 | ); 793 | expect(actualCaller, expectCaller); 794 | } 795 | 796 | void _wrapCurrentTimeTest( 797 | DateTime dateTime, 798 | String? expectTime, 799 | ) { 800 | final actualTime = SinglePrettyPrinter.formatTime(dateTime); 801 | expect(actualTime, expectTime); 802 | } 803 | 804 | void _wrapConvertToCallerStringTest( 805 | Frame frame, 806 | String? expectCallerString, 807 | ) { 808 | final printer = SinglePrettyPrinter(); 809 | final actualCallerString = printer.convertToCallerString(frame); 810 | expect(actualCallerString, expectCallerString); 811 | } 812 | --------------------------------------------------------------------------------