diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index eabf7a8..1c6dd4c 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -1,26 +1,46 @@ -name: Dart CI +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# See documentation here: +# https://github.com/dart-lang/setup-dart/blob/main/README.md + +name: Dart on: push: - branches: [ master ] + branches: [ "master" ] pull_request: - branches: [ master ] + branches: [ "master" ] jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [ stable, 2.17.0 ] + steps: + - uses: actions/checkout@v3 - container: - image: google/dart:2.12 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ matrix.sdk }} - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: pub get - - name: Format code - run: dartfmt -n --set-exit-if-changed . - - name: Static analyze project - run: dartanalyzer --fatal-infos --fatal-warnings . - - name: Run tests - run: pub run test + - name: Install dependencies + run: dart pub get + + # Uncomment this step to verify the use of 'dart format' on each commit. + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . + + # Consider passing '--fatal-infos' for slightly stricter analysis. + - name: Analyze project source + run: dart analyze --fatal-infos + + # Your project will need to have tests in test/ and a dependency on + # package:test for this step to succeed. Note that Flutter projects will + # want to change this to 'flutter test'. + - name: Run tests + run: dart test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b2c590a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: dart -dart: - - stable - -dart_task: - - dartfmt - - dartanalyzer: --fatal-warnings . - - test diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6c33c..1e8deb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,36 @@ +## 1.3.0 + +- Fixed stackTrace count when using `stackTraceBeginIndex`. + Addresses [#114](https://github.com/simc/logger/issues/114). +- Added proper FileOutput stub. Addresses [#94](https://github.com/simc/logger/issues/94). +- Added `isClosed`. Addresses [#130](https://github.com/simc/logger/issues/130). +- Added `time` to LogEvent. +- Added `error` handling to LogfmtPrinter. + +## 1.2.2 + +- Fixed conditional LogOutput export. Credits to + @ChristopheOosterlynck [#4](https://github.com/Bungeefan/logger/pull/4). + +## 1.2.1 + +- Reverted `${this}` interpolation and added linter + ignore. [#1](https://github.com/Bungeefan/logger/issues/1) + +## 1.2.0 + +- Added origin LogEvent to OutputEvent. Addresses [#133](https://github.com/simc/logger/pull/133). +- Re-added LogListener and OutputListener (Should restore compatibility with logger_flutter). +- Replaced pedantic with lints. + ## 1.1.0 + - Enhance boxing control with PrettyPrinter. Credits to @timmaffett - Add trailing new line to FileOutput. Credits to @narumishi - Add functions as a log message. Credits to @smotastic ## 1.0.0 + - Stable nullsafety ## 1.0.0-nullsafety.0 @@ -22,7 +49,7 @@ ## 0.9.2 - Add `PrefixPrinter`. Credits to @tkutcher. - Add `HybridPrinter`. Credits to @tkutcher. - + ## 0.9.1 - Fix logging output for Flutter Web. Credits to @nateshmbhat and @Cocotus. @@ -45,7 +72,7 @@ - Fix SimplePrinter showTime #12 - Remove buffer field - Update library structure (thanks @marcgraub!) - + ## 0.7.0+1 - Added `ProductionFilter`, `FileOutput`, `MemoryOutput`, `SimplePrinter` - Breaking: Changed `LogFilter`, `LogPrinter` and `LogOutput` diff --git a/LICENSE b/LICENSE index 4827a51..8b0e45c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2019 Simon Leier +Copyright (c) 2023 Severin Hamader Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b4357ad..ba68428 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Logger [![pub package](https://img.shields.io/pub/v/logger.svg?logo=dart&logoColor=00b9fc)](https://pub.dartlang.org/packages/logger) -[![CI](https://img.shields.io/github/workflow/status/leisim/logger/Dart%20CI/master?logo=github-actions&logoColor=white)](https://github.com/leisim/logger/actions) -[![Last Commits](https://img.shields.io/github/last-commit/leisim/logger?logo=git&logoColor=white)](https://github.com/leisim/logger/commits/master) -[![Pull Requests](https://img.shields.io/github/issues-pr/leisim/logger?logo=github&logoColor=white)](https://github.com/leisim/logger/pulls) -[![Code size](https://img.shields.io/github/languages/code-size/leisim/logger?logo=github&logoColor=white)](https://github.com/leisim/logger) -[![License](https://img.shields.io/github/license/leisim/logger?logo=open-source-initiative&logoColor=green)](https://github.com/leisim/logger/blob/master/LICENSE) +[![CI](https://img.shields.io/github/actions/workflow/status/Bungeefan/logger/dart.yml?branch=master&logo=github-actions&logoColor=white)](https://github.com/Bungeefan/logger/actions) +[![Last Commits](https://img.shields.io/github/last-commit/Bungeefan/logger?logo=git&logoColor=white)](https://github.com/Bungeefan/logger/commits/master) +[![Pull Requests](https://img.shields.io/github/issues-pr/Bungeefan/logger?logo=github&logoColor=white)](https://github.com/Bungeefan/logger/pulls) +[![Code size](https://img.shields.io/github/languages/code-size/Bungeefan/logger?logo=github&logoColor=white)](https://github.com/Bungeefan/logger) +[![License](https://img.shields.io/github/license/Bungeefan/logger?logo=open-source-initiative&logoColor=green)](https://github.com/Bungeefan/logger/blob/master/LICENSE) NOTICE: This repo is NOT LONGER leading! The new repo is here: https://github.com/Bungeefan/logger @@ -16,13 +16,15 @@ Inspired by [logger](https://github.com/orhanobut/logger) for Android. **Show some ❤️ and star the repo to support the project** ### Resources: + - [Documentation](https://pub.dev/documentation/logger/latest/logger/logger-library.html) - [Pub Package](https://pub.dev/packages/logger) -- [GitHub Repository](https://github.com/leisim/logger) +- [GitHub Repository](https://github.com/Bungeefan/logger) ## Getting Started Just create an instance of `Logger` and start logging: + ```dart var logger = Logger(); @@ -33,7 +35,7 @@ Instead of a string message, you can also pass other objects like `List`, `Map` ## Output -![](https://raw.githubusercontent.com/leisim/logger/master/art/screenshot.png) +![](https://raw.githubusercontent.com/Bungeefan/logger/master/art/screenshot.png) # Documentation @@ -80,32 +82,35 @@ If you use the `PrettyPrinter`, there are more options: ```dart var logger = Logger( printer: PrettyPrinter( - methodCount: 2, // number of method calls to be displayed - errorMethodCount: 8, // number of method calls if stacktrace is provided - lineLength: 120, // width of the output - colors: true, // Colorful log messages - printEmojis: true, // Print an emoji for each log message - printTime: false // Should each log print contain a timestamp + methodCount: 2, // Number of method calls to be displayed + errorMethodCount: 8, // Number of method calls if stacktrace is provided + lineLength: 120, // Width of the output + colors: true, // Colorful log messages + printEmojis: true, // Print an emoji for each log message + printTime: false // Should each log print contain a timestamp ), ); ``` ### Auto detecting -With the `io` package you can auto detect the `lineLength` and `colors` arguments. -Assuming you have imported the `io` package with `import 'dart:io' as io;` you -can auto detect `colors` with `io.stdout.supportsAnsiEscapes` and `lineLength` +With the `io` package you can auto detect the `lineLength` and `colors` arguments. +Assuming you have imported the `io` package with `import 'dart:io' as io;` you +can auto detect `colors` with `io.stdout.supportsAnsiEscapes` and `lineLength` with `io.stdout.terminalColumns`. -You should probably do this unless there's a good reason you don't want to +You should probably do this unless there's a good reason you don't want to import `io`, for example when using this library on the web. ## LogFilter The `LogFilter` decides which log events should be shown and which don't.
-The default implementation (`DevelopmentFilter`) shows all logs with `level >= Logger.level` while in debug mode. In release mode all logs are omitted. +The default implementation (`DevelopmentFilter`) shows all logs with `level >= Logger.level` while +in debug mode. +In release mode all logs are omitted. You can create your own `LogFilter` like this: + ```dart class MyFilter extends LogFilter { @override @@ -114,8 +119,8 @@ class MyFilter extends LogFilter { } } ``` -This will show all logs even in release mode. (**NOT** a good idea) +This will show all logs even in release mode. (**NOT** a good idea) ## LogPrinter @@ -123,6 +128,7 @@ The `LogPrinter` creates and formats the output, which is then sent to the `LogO You can implement your own `LogPrinter`. This gives you maximum flexibility. A very basic printer could look like this: + ```dart class MyPrinter extends LogPrinter { @override @@ -132,22 +138,26 @@ class MyPrinter extends LogPrinter { } ``` -If you created a cool `LogPrinter` which might be helpful to others, feel free to open a pull request. :) +If you created a cool `LogPrinter` which might be helpful to others, feel free to open a pull +request. +:) ### Colors -Please note that all IDEs (VSCode, XCode, Android Studio, IntelliJ) do not -support ANSI escape sequences in their terminal outputs. These escape sequences -are used to color output. If using such an IDE do not configure colored output. +Please note that in some cases ANSI escape sequences do not work under macOS. +These escape sequences are used to colorize the output. +This seems to be related to a Flutter bug that affects iOS builds: +https://github.com/flutter/flutter/issues/64491 -However, if you are using a JetBrains IDE (Android Studio, IntelliJ, etc.) -you can make use of the [Grep Console Plugin](https://plugins.jetbrains.com/plugin/7125-grep-console) -and the [`PrefixPrinter`](/lib/src/printers/prefix_printer.dart) -decorator to achieved colored logs for any logger: +However, if you are using a JetBrains IDE (Android Studio, IntelliJ, etc.) +you can make use of +the [Grep Console Plugin](https://plugins.jetbrains.com/plugin/7125-grep-console) +and the [`PrefixPrinter`](/lib/src/printers/prefix_printer.dart) +decorator to achieve colored logs for any logger: ```dart var logger = Logger( - printer: PrefixPrinter(PrettyPrinter(colors: false)) + printer: PrefixPrinter(PrettyPrinter(colors: false)) ); ``` @@ -167,33 +177,15 @@ class ConsoleOutput extends LogOutput { } ``` -Possible future `LogOutput`s could send to a file, firebase or to Logcat. Feel free to open pull requests. - +Possible future `LogOutput`s could send to a file, firebase or to Logcat. Feel free to open pull +requests. ## logger_flutter extension -The [logger_flutter](https://pub.dev/packages/logger_flutter) package is an extension for logger. You can add it to any Flutter app. Just shake the phone to show the console. +The [logger_flutter](https://pub.dev/packages/logger_flutter) package is an extension for logger. +You can add it to any Flutter app. +Just shake the phone to show the console. +# Acknowledgments -## MIT License -``` -Copyright (c) 2019 Simon Leier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` +This package was originally created by [Simon Choi](https://github.com/simc). diff --git a/analysis_options.yaml b/analysis_options.yaml index d4fcc1a..12e713a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1,4 @@ -include: package:pedantic/analysis_options.yaml \ No newline at end of file +include: package:lints/recommended.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/lib/logger.dart b/lib/logger.dart index c5f3951..b3fba84 100644 --- a/lib/logger.dart +++ b/lib/logger.dart @@ -2,25 +2,20 @@ library logger; export 'src/ansi_color.dart'; - export 'src/filters/development_filter.dart'; export 'src/filters/production_filter.dart'; - +export 'src/log_filter.dart'; +export 'src/log_output.dart'; +export 'src/log_printer.dart'; +export 'src/logger.dart'; export 'src/outputs/console_output.dart'; -export 'src/outputs/stream_output.dart'; +export 'src/outputs/file_output_stub.dart' + if (dart.library.io) 'src/outputs/file_output.dart'; export 'src/outputs/memory_output.dart'; export 'src/outputs/multi_output.dart'; - -export 'src/printers/pretty_printer.dart'; -export 'src/printers/logfmt_printer.dart'; -export 'src/printers/simple_printer.dart'; +export 'src/outputs/stream_output.dart'; export 'src/printers/hybrid_printer.dart'; +export 'src/printers/logfmt_printer.dart'; export 'src/printers/prefix_printer.dart'; - -export 'src/log_output.dart' - if (dart.library.io) 'src/outputs/file_output.dart'; - -export 'src/log_filter.dart'; -export 'src/log_output.dart'; -export 'src/log_printer.dart'; -export 'src/logger.dart'; +export 'src/printers/pretty_printer.dart'; +export 'src/printers/simple_printer.dart'; diff --git a/lib/src/ansi_color.dart b/lib/src/ansi_color.dart index a6e0f06..22ea7f5 100644 --- a/lib/src/ansi_color.dart +++ b/lib/src/ansi_color.dart @@ -36,6 +36,7 @@ class AnsiColor { String call(String msg) { if (color) { + // ignore: unnecessary_brace_in_string_interps return '${this}$msg$ansiDefault'; } else { return msg; diff --git a/lib/src/filters/development_filter.dart b/lib/src/filters/development_filter.dart index a60f9b2..821c15f 100644 --- a/lib/src/filters/development_filter.dart +++ b/lib/src/filters/development_filter.dart @@ -1,5 +1,5 @@ -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_filter.dart'; +import 'package:logger/src/logger.dart'; /// Prints all logs with `level >= Logger.level` while in development mode (eg /// when `assert`s are evaluated, Flutter calls this debug mode). diff --git a/lib/src/filters/production_filter.dart b/lib/src/filters/production_filter.dart index 891ece1..d6ceac0 100644 --- a/lib/src/filters/production_filter.dart +++ b/lib/src/filters/production_filter.dart @@ -1,5 +1,5 @@ -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_filter.dart'; +import 'package:logger/src/logger.dart'; /// Prints all logs with `level >= Logger.level` even in production. class ProductionFilter extends LogFilter { diff --git a/lib/src/log_filter.dart b/lib/src/log_filter.dart index e512376..8fa2d03 100644 --- a/lib/src/log_filter.dart +++ b/lib/src/log_filter.dart @@ -6,6 +6,7 @@ import 'package:logger/src/logger.dart'; /// Every implementation should consider [Logger.level]. abstract class LogFilter { Level? level; + void init() {} /// Is called every time a new log message is sent and decides if diff --git a/lib/src/log_printer.dart b/lib/src/log_printer.dart index d0cb53a..a40125c 100644 --- a/lib/src/log_printer.dart +++ b/lib/src/log_printer.dart @@ -3,7 +3,7 @@ import 'package:logger/src/logger.dart'; /// An abstract handler of log events. /// /// A log printer creates and formats the output, which is then sent to -/// [LogOutput]. Every implementation has to use the [LogPrinter.println] +/// [LogOutput]. Every implementation has to use the [LogPrinter.log] /// method to send the output. /// /// You can implement a `LogPrinter` from scratch or extend [PrettyPrinter]. diff --git a/lib/src/logger.dart b/lib/src/logger.dart index 9378662..5fc69ca 100644 --- a/lib/src/logger.dart +++ b/lib/src/logger.dart @@ -1,9 +1,9 @@ import 'package:logger/src/filters/development_filter.dart'; -import 'package:logger/src/printers/pretty_printer.dart'; -import 'package:logger/src/outputs/console_output.dart'; import 'package:logger/src/log_filter.dart'; -import 'package:logger/src/log_printer.dart'; import 'package:logger/src/log_output.dart'; +import 'package:logger/src/log_printer.dart'; +import 'package:logger/src/outputs/console_output.dart'; +import 'package:logger/src/printers/pretty_printer.dart'; /// [Level]s to control logging output. Logging can be enabled to include all /// levels above certain [Level]. @@ -23,20 +23,24 @@ class LogEvent { final dynamic error; final StackTrace? stackTrace; - LogEvent(this.level, this.message, this.error, this.stackTrace); + /// Time when this log was created. + final DateTime time; + + LogEvent(this.level, this.message, [this.error, this.stackTrace]) + : time = DateTime.now(); } class OutputEvent { - final Level level; final List lines; + final LogEvent origin; + + Level get level => origin.level; - OutputEvent(this.level, this.lines); + OutputEvent(this.origin, this.lines); } -@Deprecated('Use a custom LogFilter instead') typedef LogCallback = void Function(LogEvent event); -@Deprecated('Use a custom LogOutput instead') typedef OutputCallback = void Function(OutputEvent event); /// Use instances of logger to send log messages to the [LogPrinter]. @@ -46,6 +50,10 @@ class Logger { /// All logs with levels below this level will be omitted. static Level level = Level.verbose; + static final Set _logCallbacks = {}; + + static final Set _outputCallbacks = {}; + final LogFilter _filter; final LogPrinter _printer; final LogOutput _output; @@ -112,13 +120,19 @@ class Logger { } var logEvent = LogEvent(level, message, error, stackTrace); if (_filter.shouldLog(logEvent)) { + for (var callback in _logCallbacks) { + callback(logEvent); + } var output = _printer.log(logEvent); if (output.isNotEmpty) { - var outputEvent = OutputEvent(level, output); + var outputEvent = OutputEvent(logEvent, output); // Issues with log output should NOT influence // the main software behavior. try { + for (var callback in _outputCallbacks) { + callback(outputEvent); + } _output.output(outputEvent); } catch (e, s) { print(e); @@ -128,6 +142,10 @@ class Logger { } } + bool isClosed() { + return !_active; + } + /// Closes the logger and releases all resources. void close() { _active = false; @@ -135,4 +153,28 @@ class Logger { _printer.destroy(); _output.destroy(); } + + /// Register a [LogCallback] which is called for each new [LogEvent]. + static void addLogListener(LogCallback callback) { + _logCallbacks.add(callback); + } + + /// Removes a [LogCallback] which was previously registered. + /// + /// Returns whether the callback was successfully removed. + static bool removeLogListener(LogCallback callback) { + return _logCallbacks.remove(callback); + } + + /// Register an [OutputCallback] which is called for each new [OutputEvent]. + static void addOutputListener(OutputCallback callback) { + _outputCallbacks.add(callback); + } + + /// Removes a [OutputCallback] which was previously registered. + /// + /// Returns whether the callback was successfully removed. + static void removeOutputListener(OutputCallback callback) { + _outputCallbacks.remove(callback); + } } diff --git a/lib/src/outputs/console_output.dart b/lib/src/outputs/console_output.dart index 277b604..8670db1 100644 --- a/lib/src/outputs/console_output.dart +++ b/lib/src/outputs/console_output.dart @@ -1,5 +1,5 @@ -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_output.dart'; +import 'package:logger/src/logger.dart'; /// Default implementation of [LogOutput]. /// diff --git a/lib/src/outputs/file_output.dart b/lib/src/outputs/file_output.dart index c58c8e0..35b7b32 100644 --- a/lib/src/outputs/file_output.dart +++ b/lib/src/outputs/file_output.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:io'; -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_output.dart'; +import 'package:logger/src/logger.dart'; /// Writes the log output to a file. class FileOutput extends LogOutput { diff --git a/lib/src/outputs/file_output_stub.dart b/lib/src/outputs/file_output_stub.dart new file mode 100644 index 0000000..b4eb4db --- /dev/null +++ b/lib/src/outputs/file_output_stub.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; +import 'dart:io'; + +import '../log_output.dart'; +import '../logger.dart'; + +class FileOutput extends LogOutput { + FileOutput({ + required File file, + bool overrideExisting = false, + Encoding encoding = utf8, + }) { + throw UnsupportedError("Not supported on this platform."); + } + + @override + void output(OutputEvent event) { + throw UnsupportedError("Not supported on this platform."); + } +} diff --git a/lib/src/outputs/memory_output.dart b/lib/src/outputs/memory_output.dart index f0f4100..0a46f23 100644 --- a/lib/src/outputs/memory_output.dart +++ b/lib/src/outputs/memory_output.dart @@ -1,7 +1,7 @@ import 'dart:collection'; -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_output.dart'; +import 'package:logger/src/logger.dart'; /// Buffers [OutputEvent]s. class MemoryOutput extends LogOutput { diff --git a/lib/src/outputs/multi_output.dart b/lib/src/outputs/multi_output.dart index bfa50d7..2e40ea2 100644 --- a/lib/src/outputs/multi_output.dart +++ b/lib/src/outputs/multi_output.dart @@ -8,6 +8,7 @@ class MultiOutput extends LogOutput { MultiOutput(List? outputs) { _outputs = _normalizeOutputs(outputs); } + List _normalizeOutputs(List? outputs) { final normalizedOutputs = []; @@ -24,16 +25,22 @@ class MultiOutput extends LogOutput { @override void init() { - _outputs.forEach((o) => o.init()); + for (var o in _outputs) { + o.init(); + } } @override void output(OutputEvent event) { - _outputs.forEach((o) => o.output(event)); + for (var o in _outputs) { + o.output(event); + } } @override void destroy() { - _outputs.forEach((o) => o.destroy()); + for (var o in _outputs) { + o.destroy(); + } } } diff --git a/lib/src/outputs/stream_output.dart b/lib/src/outputs/stream_output.dart index 8523b76..1a34329 100644 --- a/lib/src/outputs/stream_output.dart +++ b/lib/src/outputs/stream_output.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_output.dart'; +import 'package:logger/src/logger.dart'; class StreamOutput extends LogOutput { late StreamController> _controller; diff --git a/lib/src/printers/hybrid_printer.dart b/lib/src/printers/hybrid_printer.dart index 83d4d64..9b3e655 100644 --- a/lib/src/printers/hybrid_printer.dart +++ b/lib/src/printers/hybrid_printer.dart @@ -1,5 +1,5 @@ -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_printer.dart'; +import 'package:logger/src/logger.dart'; /// A decorator for a [LogPrinter] that allows for the composition of /// different printers to handle different log messages. Provide it's @@ -13,21 +13,26 @@ import 'package:logger/src/log_printer.dart'; /// Will use the pretty printer for all logs except Level.debug /// logs, which will use SimplePrinter(). class HybridPrinter extends LogPrinter { - final LogPrinter _realPrinter; - var _printerMap; + final Map _printerMap; - HybridPrinter(this._realPrinter, - {debug, verbose, wtf, info, warning, error}) { - _printerMap = { - Level.debug: debug ?? _realPrinter, - Level.verbose: verbose ?? _realPrinter, - Level.wtf: wtf ?? _realPrinter, - Level.info: info ?? _realPrinter, - Level.warning: warning ?? _realPrinter, - Level.error: error ?? _realPrinter, - }; - } + HybridPrinter( + LogPrinter realPrinter, { + LogPrinter? debug, + LogPrinter? verbose, + LogPrinter? wtf, + LogPrinter? info, + LogPrinter? warning, + LogPrinter? error, + }) : _printerMap = { + Level.debug: debug ?? realPrinter, + Level.verbose: verbose ?? realPrinter, + Level.wtf: wtf ?? realPrinter, + Level.info: info ?? realPrinter, + Level.warning: warning ?? realPrinter, + Level.error: error ?? realPrinter, + }; @override - List log(LogEvent event) => _printerMap[event.level].log(event); + List log(LogEvent event) => + _printerMap[event.level]?.log(event) ?? []; } diff --git a/lib/src/printers/logfmt_printer.dart b/lib/src/printers/logfmt_printer.dart index 63083e5..85c5024 100644 --- a/lib/src/printers/logfmt_printer.dart +++ b/lib/src/printers/logfmt_printer.dart @@ -29,6 +29,9 @@ class LogfmtPrinter extends LogPrinter { } }); } + if (event.error != null) { + output.write(' error="${event.error}"'); + } return [output.toString()]; } diff --git a/lib/src/printers/prefix_printer.dart b/lib/src/printers/prefix_printer.dart index 256012e..6b89d92 100644 --- a/lib/src/printers/prefix_printer.dart +++ b/lib/src/printers/prefix_printer.dart @@ -1,5 +1,5 @@ -import 'package:logger/src/logger.dart'; import 'package:logger/src/log_printer.dart'; +import 'package:logger/src/logger.dart'; /// A decorator for a [LogPrinter] that allows for the prepending of every /// line in the log output with a string for the level of that log. For @@ -37,7 +37,7 @@ class PrefixPrinter extends LogPrinter { } int _longestPrefixLength() { - var compFunc = (String a, String b) => a.length > b.length ? a : b; + compFunc(String a, String b) => a.length > b.length ? a : b; return _prefixMap.values.reduce(compFunc).length; } } diff --git a/lib/src/printers/pretty_printer.dart b/lib/src/printers/pretty_printer.dart index f58f599..bf2cc74 100644 --- a/lib/src/printers/pretty_printer.dart +++ b/lib/src/printers/pretty_printer.dart @@ -1,8 +1,9 @@ import 'dart:convert'; +import 'dart:math'; -import 'package:logger/src/logger.dart'; -import 'package:logger/src/log_printer.dart'; import 'package:logger/src/ansi_color.dart'; +import 'package:logger/src/log_printer.dart'; +import 'package:logger/src/logger.dart'; /// Default implementation of [LogPrinter]. /// @@ -24,6 +25,7 @@ class PrettyPrinter extends LogPrinter { static const doubleDivider = '─'; static const singleDivider = '┄'; + static final whiteColor = AnsiColor.fg(255); static final levelColors = { Level.verbose: AnsiColor.fg(AnsiColor.grey(0.5)), Level.debug: AnsiColor.none(), @@ -83,6 +85,10 @@ class PrettyPrinter extends LogPrinter { /// (boxing can still be turned on for some levels by using something like excludeBox:{Level.error:false} ) final bool noBoxingByDefault; + /// To exclude user's custom path + /// for example if you made a Mylog util redirect to logger.log, + /// you can add your Mylog path in [excludePaths]. + final List excludePaths; late final Map includeBox; String _topBorder = ''; @@ -99,6 +105,7 @@ class PrettyPrinter extends LogPrinter { this.printTime = false, this.excludeBox = const {}, this.noBoxingByDefault = false, + this.excludePaths = const [], }) { _startTime ??= DateTime.now(); @@ -115,7 +122,9 @@ class PrettyPrinter extends LogPrinter { // Translate excludeBox map (constant if default) to includeBox map with all Level enum possibilities includeBox = {}; - Level.values.forEach((l) => includeBox[l] = !noBoxingByDefault); + for (var l in Level.values) { + includeBox[l] = !noBoxingByDefault; + } excludeBox.forEach((k, v) => includeBox[k] = !v); } @@ -136,7 +145,7 @@ class PrettyPrinter extends LogPrinter { String? timeStr; if (printTime) { - timeStr = getTime(); + timeStr = getTime(event.time); } return _formatAndPrint( @@ -149,23 +158,25 @@ class PrettyPrinter extends LogPrinter { } String? formatStackTrace(StackTrace? stackTrace, int methodCount) { - var lines = stackTrace.toString().split('\n'); - if (stackTraceBeginIndex > 0 && stackTraceBeginIndex < lines.length - 1) { - lines = lines.sublist(stackTraceBeginIndex); - } - var formatted = []; - var count = 0; - for (var line in lines) { - if (_discardDeviceStacktraceLine(line) || - _discardWebStacktraceLine(line) || - _discardBrowserStacktraceLine(line) || - line.isEmpty) { + List lines = stackTrace + .toString() + .split('\n') + .where( + (line) => + !_discardDeviceStacktraceLine(line) && + !_discardWebStacktraceLine(line) && + !_discardBrowserStacktraceLine(line) && + line.isNotEmpty, + ) + .toList(); + List formatted = []; + + for (int count = 0; count < min(lines.length, methodCount); count++) { + var line = lines[count]; + if (count < stackTraceBeginIndex) { continue; } formatted.add('#$count ${line.replaceFirst(RegExp(r'#\d+\s+'), '')}'); - if (++count == methodCount) { - break; - } } if (formatted.isEmpty) { @@ -175,12 +186,25 @@ class PrettyPrinter extends LogPrinter { } } + bool _isInExcludePaths(String segment) { + for (var element in excludePaths) { + if (segment.startsWith(element)) { + return true; + } + } + return false; + } + bool _discardDeviceStacktraceLine(String line) { var match = _deviceStackTraceRegex.matchAsPrefix(line); if (match == null) { return false; } - return match.group(2)!.startsWith('package:logger'); + final segment = match.group(2)!; + if (segment.startsWith('package:logger')) { + return true; + } + return _isInExcludePaths(segment); } bool _discardWebStacktraceLine(String line) { @@ -188,8 +212,12 @@ class PrettyPrinter extends LogPrinter { if (match == null) { return false; } - return match.group(1)!.startsWith('packages/logger') || - match.group(1)!.startsWith('dart-sdk/lib'); + final segment = match.group(1)!; + if (segment.startsWith('packages/logger') || + segment.startsWith('dart-sdk/lib')) { + return true; + } + return _isInExcludePaths(segment); } bool _discardBrowserStacktraceLine(String line) { @@ -197,27 +225,30 @@ class PrettyPrinter extends LogPrinter { if (match == null) { return false; } - return match.group(1)!.startsWith('package:logger') || - match.group(1)!.startsWith('dart:'); + final segment = match.group(1)!; + if (segment.startsWith('package:logger') || segment.startsWith('dart:')) { + return true; + } + return _isInExcludePaths(segment); } - String getTime() { - String _threeDigits(int n) { + String getTime(DateTime time) { + String threeDigits(int n) { if (n >= 100) return '$n'; if (n >= 10) return '0$n'; return '00$n'; } - String _twoDigits(int n) { + String twoDigits(int n) { if (n >= 10) return '$n'; return '0$n'; } - var now = DateTime.now(); - var h = _twoDigits(now.hour); - var min = _twoDigits(now.minute); - var sec = _twoDigits(now.second); - var ms = _threeDigits(now.millisecond); + var now = time; + var h = twoDigits(now.hour); + var min = twoDigits(now.minute); + var sec = twoDigits(now.second); + var ms = threeDigits(now.millisecond); var timeSinceStart = now.difference(_startTime!).toString(); return '$h:$min:$sec.$ms (+$timeSinceStart)'; } @@ -272,10 +303,8 @@ class PrettyPrinter extends LogPrinter { String? error, String? stacktrace, ) { - // This code is non trivial and a type annotation here helps understanding. - // ignore: omit_local_variable_types List buffer = []; - var verticalLineAtLevel = (includeBox[level]!) ? (verticalLine + ' ') : ''; + var verticalLineAtLevel = (includeBox[level]!) ? ('$verticalLine ') : ''; var color = _getLevelColor(level); if (includeBox[level]!) buffer.add(color(_topBorder)); @@ -284,8 +313,7 @@ class PrettyPrinter extends LogPrinter { for (var line in error.split('\n')) { buffer.add( color(verticalLineAtLevel) + - errorColor.resetForeground + - errorColor(line) + + errorColor(whiteColor(line)) + errorColor.resetBackground, ); } diff --git a/lib/src/printers/simple_printer.dart b/lib/src/printers/simple_printer.dart index d7a4adf..7041036 100644 --- a/lib/src/printers/simple_printer.dart +++ b/lib/src/printers/simple_printer.dart @@ -1,8 +1,8 @@ import 'dart:convert'; -import 'package:logger/src/logger.dart'; -import 'package:logger/src/log_printer.dart'; import 'package:logger/src/ansi_color.dart'; +import 'package:logger/src/log_printer.dart'; +import 'package:logger/src/logger.dart'; /// Outputs simple log messages: /// ``` @@ -36,7 +36,7 @@ class SimplePrinter extends LogPrinter { List log(LogEvent event) { var messageStr = _stringifyMessage(event.message); var errorStr = event.error != null ? ' ERROR: ${event.error}' : ''; - var timeStr = printTime ? 'TIME: ${DateTime.now().toIso8601String()}' : ''; + var timeStr = printTime ? 'TIME: ${event.time.toIso8601String()}' : ''; return ['${_labelFor(event.level)} $timeStr $messageStr$errorStr']; } diff --git a/pubspec.yaml b/pubspec.yaml index f0a021e..a441cbb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: logger description: Small, easy to use and extensible logger which prints beautiful logs. -version: 1.1.0 -homepage: https://github.com/leisim/logger +version: 1.3.0 +repository: https://github.com/Bungeefan/logger environment: - sdk: ">=2.12.0 <3.0.0" + sdk: "^2.12.0" dev_dependencies: test: ^1.16.8 - pedantic: ^1.11.0 + lints: ^2.0.1 diff --git a/test/logger_test.dart b/test/logger_test.dart index 1025c25..40db942 100644 --- a/test/logger_test.dart +++ b/test/logger_test.dart @@ -1,7 +1,7 @@ import 'dart:math'; -import 'package:test/test.dart'; import 'package:logger/logger.dart'; +import 'package:test/test.dart'; typedef PrinterCallback = List Function( Level level, @@ -168,4 +168,11 @@ void main() { logger.w('This is'); expect(printedMessage, 'This is'); }); + + test('Logger.close', () { + var logger = Logger(); + expect(logger.isClosed(), false); + logger.close(); + expect(logger.isClosed(), true); + }); } diff --git a/test/outputs/memory_output_test.dart b/test/outputs/memory_output_test.dart index 4d92b2a..10874f4 100644 --- a/test/outputs/memory_output_test.dart +++ b/test/outputs/memory_output_test.dart @@ -5,9 +5,9 @@ void main() { test('Memory output buffer size is limited', () { var output = MemoryOutput(bufferSize: 2); - final event0 = OutputEvent(Level.info, []); - final event1 = OutputEvent(Level.info, []); - final event2 = OutputEvent(Level.info, []); + final event0 = OutputEvent(LogEvent(Level.info, null), []); + final event1 = OutputEvent(LogEvent(Level.info, null), []); + final event2 = OutputEvent(LogEvent(Level.info, null), []); output.output(event0); output.output(event1); diff --git a/test/outputs/multi_output_test.dart b/test/outputs/multi_output_test.dart index ab7be20..0d83f56 100644 --- a/test/outputs/multi_output_test.dart +++ b/test/outputs/multi_output_test.dart @@ -8,7 +8,7 @@ void main() { final multiOutput = MultiOutput([output1, output2]); - final event0 = OutputEvent(Level.info, []); + final event0 = OutputEvent(LogEvent(Level.info, null), []); multiOutput.output(event0); expect(output1.buffer.length, 1); @@ -16,7 +16,7 @@ void main() { expect(output1.buffer.elementAt(0), equals(output2.buffer.elementAt(0))); expect(output1.buffer.elementAt(0), equals(event0)); - final event1 = OutputEvent(Level.info, []); + final event1 = OutputEvent(LogEvent(Level.info, null), []); multiOutput.output(event1); expect(output1.buffer.length, 2); @@ -29,11 +29,11 @@ void main() { test('passing null does not throw an exception', () { final output = MultiOutput(null); - output.output(OutputEvent(Level.info, [])); + output.output(OutputEvent(LogEvent(Level.info, null), [])); }); test('passing null in the list does not throw an exception', () { final output = MultiOutput([null]); - output.output(OutputEvent(Level.info, [])); + output.output(OutputEvent(LogEvent(Level.info, null), [])); }); } diff --git a/test/outputs/stream_output_test.dart b/test/outputs/stream_output_test.dart index 506abad..7a9ba49 100644 --- a/test/outputs/stream_output_test.dart +++ b/test/outputs/stream_output_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:logger/logger.dart'; +import 'package:test/test.dart'; void main() { test('writes to a Stream', () { @@ -9,19 +9,19 @@ void main() { expect(e, ['hi there']); }); - out.output(OutputEvent(Level.debug, ['hi there'])); + out.output(OutputEvent(LogEvent(Level.debug, null), ['hi there'])); }); test('respects listen', () { var out = StreamOutput(); - out.output(OutputEvent(Level.debug, ['dropped'])); + out.output(OutputEvent(LogEvent(Level.debug, null), ['dropped'])); out.stream.listen((var e) { expect(e, ['hi there']); }); - out.output(OutputEvent(Level.debug, ['hi there'])); + out.output(OutputEvent(LogEvent(Level.debug, null), ['hi there'])); }); test('respects pause', () { @@ -32,8 +32,8 @@ void main() { }); sub.pause(); - out.output(OutputEvent(Level.debug, ['dropped'])); + out.output(OutputEvent(LogEvent(Level.debug, null), ['dropped'])); sub.resume(); - out.output(OutputEvent(Level.debug, ['hi there'])); + out.output(OutputEvent(LogEvent(Level.debug, null), ['hi there'])); }); } diff --git a/test/printers/hybrid_printer_test.dart b/test/printers/hybrid_printer_test.dart index ce6e880..53ef327 100644 --- a/test/printers/hybrid_printer_test.dart +++ b/test/printers/hybrid_printer_test.dart @@ -1,14 +1,11 @@ -import 'package:logger/src/log_printer.dart'; -import 'package:logger/src/logger.dart'; -import 'package:logger/src/printers/simple_printer.dart'; +import 'package:logger/logger.dart'; import 'package:test/test.dart'; -import 'package:logger/src/printers/hybrid_printer.dart'; - final realPrinter = SimplePrinter(); class TestLogPrinter extends LogPrinter { LogEvent? latestEvent; + @override List log(LogEvent event) { latestEvent = event; diff --git a/test/printers/logfmt_printer_test.dart b/test/printers/logfmt_printer_test.dart index 4a98cec..ad3d4b9 100644 --- a/test/printers/logfmt_printer_test.dart +++ b/test/printers/logfmt_printer_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:logger/logger.dart'; +import 'package:test/test.dart'; void main() { var printer = LogfmtPrinter(); @@ -18,13 +18,14 @@ void main() { test('with a string message includes a msg key', () { expect( - printer.log(LogEvent( - Level.debug, - 'some message', - Exception('boom'), - StackTrace.current, - ))[0], - contains('msg="some message"')); + printer.log(LogEvent( + Level.debug, + 'some message', + Exception('boom'), + StackTrace.current, + ))[0], + contains('msg="some message"'), + ); }); test('includes random key=value pairs', () { @@ -39,6 +40,21 @@ void main() { expect(output, contains('foo="bar baz"')); }); + test('handles an error/exception', () { + var output = printer.log(LogEvent( + Level.debug, + 'some message', + Exception('boom'), + StackTrace.current, + ))[0]; + expect(output, contains('error="Exception: boom"')); + + output = printer.log(LogEvent( + Level.debug, + 'some message', + ))[0]; + expect(output, isNot(contains('error='))); + }); + test('handles a stacktrace', () {}, skip: 'TODO'); - test('handles an error/exception', () {}, skip: 'TODO'); } diff --git a/test/printers/prefix_printer_test.dart b/test/printers/prefix_printer_test.dart index 7ca0a43..a3011e4 100644 --- a/test/printers/prefix_printer_test.dart +++ b/test/printers/prefix_printer_test.dart @@ -1,7 +1,4 @@ -import 'package:logger/src/logger.dart'; -import 'package:logger/src/printers/prefix_printer.dart'; -import 'package:logger/src/printers/pretty_printer.dart'; -import 'package:logger/src/printers/simple_printer.dart'; +import 'package:logger/logger.dart'; import 'package:test/test.dart'; void main() { @@ -25,22 +22,22 @@ void main() { test('prefixes logs', () { var printer = PrefixPrinter(PrettyPrinter()); var actualLog = printer.log(infoEvent); - actualLog.forEach((logString) { + for (var logString in actualLog) { expect(logString, contains('INFO')); - }); + } var debugLog = printer.log(debugEvent); - debugLog.forEach((logString) { + for (var logString in debugLog) { expect(logString, contains('DEBUG')); - }); + } }); test('can supply own prefixes', () { var printer = PrefixPrinter(PrettyPrinter(), debug: 'BLAH'); var actualLog = printer.log(debugEvent); - actualLog.forEach((logString) { + for (var logString in actualLog) { expect(logString, contains('BLAH')); - }); + } }); test('pads to same length', () { @@ -49,9 +46,9 @@ void main() { var printer = PrefixPrinter(SimplePrinter(), debug: longPrefix); for (var event in allEvents) { var l1 = printer.log(event); - l1.forEach((logString) { + for (var logString in l1) { expect(logString.substring(0, len), isNot(contains('['))); - }); + } } }); } diff --git a/test/printers/pretty_printer_test.dart b/test/printers/pretty_printer_test.dart index 52727a2..0f852c8 100644 --- a/test/printers/pretty_printer_test.dart +++ b/test/printers/pretty_printer_test.dart @@ -2,8 +2,8 @@ import 'package:logger/logger.dart'; import 'package:test/test.dart'; void main() { - String _readMessage(List log) { - return log.reduce((acc, val) => acc + val); + String readMessage(List log) { + return log.reduce((acc, val) => "$acc\n$val"); } final prettyPrinter = PrettyPrinter(printEmojis: false); @@ -19,7 +19,7 @@ void main() { ); final actualLog = emojiPrettyPrinter.log(event); - final actualLogString = _readMessage(actualLog); + final actualLogString = readMessage(actualLog); expect(actualLogString, contains(PrettyPrinter.levelEmojis[Level.debug])); expect(actualLogString, contains(expectedMessage)); }); @@ -34,7 +34,7 @@ void main() { ); final actualLog = prettyPrinter.log(withFunction); - final actualLogString = _readMessage(actualLog); + final actualLogString = readMessage(actualLog); expect( actualLogString, @@ -51,7 +51,7 @@ void main() { ); final actualLog = prettyPrinter.log(withMap); - final actualLogString = _readMessage(actualLog); + final actualLogString = readMessage(actualLog); for (var expectedMsg in expectedMsgMap.entries) { expect( actualLogString, @@ -69,7 +69,7 @@ void main() { StackTrace.current, ); final actualLog = prettyPrinter.log(withIterable); - final actualLogString = _readMessage(actualLog); + final actualLogString = readMessage(actualLog); for (var expectedMsg in expectedMsgItems) { expect( actualLogString, @@ -88,11 +88,35 @@ void main() { ); final actualLog = prettyPrinter.log(withFunction); - final actualLogString = _readMessage(actualLog); + final actualLogString = readMessage(actualLog); expect( actualLogString, contains(expectedMessage), ); }); + + test('stackTraceBeginIndex', () { + final prettyPrinter = PrettyPrinter( + stackTraceBeginIndex: 2, + ); + final withFunction = LogEvent( + Level.debug, + "some message", + 'some error', + StackTrace.current, + ); + + final actualLog = prettyPrinter.log(withFunction); + final actualLogString = readMessage(actualLog); + + expect( + actualLogString, + allOf([ + isNot(contains("#0 ")), + isNot(contains("#1 ")), + contains("#2 "), + ]), + ); + }); } diff --git a/test/printers/simple_printer_test.dart b/test/printers/simple_printer_test.dart index ad3f904..c185d50 100644 --- a/test/printers/simple_printer_test.dart +++ b/test/printers/simple_printer_test.dart @@ -1,5 +1,4 @@ -import 'package:logger/src/logger.dart'; -import 'package:logger/src/printers/simple_printer.dart'; +import 'package:logger/logger.dart'; import 'package:test/test.dart'; const ansiEscapeLiteral = '\x1B';