Browse Source

Clone super_logging and make it privacy friendly

Vishnu Mohandas 4 years ago
parent
commit
06b679fca8

+ 4 - 4
pubspec.lock

@@ -752,10 +752,10 @@ packages:
   super_logging:
   super_logging:
     dependency: "direct main"
     dependency: "direct main"
     description:
     description:
-      name: super_logging
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
+      path: "thirdparty/super_logging"
+      relative: true
+    source: path
+    version: "1.3.4"
   supercharged:
   supercharged:
     dependency: transitive
     dependency: transitive
     description:
     description:

+ 2 - 1
pubspec.yaml

@@ -37,7 +37,8 @@ dependencies:
   visibility_detector: ^0.1.5
   visibility_detector: ^0.1.5
   event_bus: ^1.1.1
   event_bus: ^1.1.1
   sentry: ">=3.0.0 <4.0.0"
   sentry: ">=3.0.0 <4.0.0"
-  super_logging: ^2.0.1
+  super_logging:
+    path: thirdparty/super_logging
   archive: ^2.0.11
   archive: ^2.0.11
   flutter_email_sender: ^3.0.1
   flutter_email_sender: ^3.0.1
   like_button: ^0.2.0
   like_button: ^0.2.0

+ 239 - 0
thirdparty/super_logging/.gitignore

@@ -0,0 +1,239 @@
+.DS_Store
+.dart_tool/
+
+flutter_export_environment.sh
+.flutter-plugins-dependencies
+
+.packages
+.pub/
+
+build/
+ios/.generated/
+ios/Flutter/Generated.xcconfig
+ios/Runner/GeneratedPluginRegistrant.*
+
+**/.idea
+.flutter-plugins
+
+android/
+
+# Created by https://www.gitignore.io/api/dart,android,androidstudio
+# Edit at https://www.gitignore.io/?templates=dart,android,androidstudio
+
+### Android ###
+# Built application files
+*.apk
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+.idea/caches
+# Android Studio 3 in .gitignore file.
+.idea/caches/build_file_checksums.ser
+.idea/modules.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+### Android Patch ###
+gen-external-apklibs
+output.json
+
+### AndroidStudio ###
+# Covers files to be ignored for android development using Android Studio.
+
+# Built application files
+
+# Files for the ART/Dalvik VM
+
+# Java class files
+
+# Generated files
+
+# Gradle files
+.gradle
+
+# Signing files
+.signing/
+
+# Local configuration file (sdk path, etc)
+
+# Proguard folder generated by Eclipse
+
+# Log Files
+
+# Android Studio
+/*/build/
+/*/local.properties
+/*/out
+/*/*/build
+/*/*/production
+*.ipr
+*~
+*.swp
+
+# Android Patch
+
+# External native build folder generated in Android Studio 2.2 and later
+
+# NDK
+obj/
+
+# IntelliJ IDEA
+*.iws
+/out/
+
+# User-specific configurations
+.idea/caches/
+.idea/libraries/
+.idea/shelf/
+.idea/.name
+.idea/compiler.xml
+.idea/copyright/profiles_settings.xml
+.idea/encodings.xml
+.idea/misc.xml
+.idea/scopes/scope_settings.xml
+.idea/vcs.xml
+.idea/jsLibraryMappings.xml
+.idea/datasources.xml
+.idea/dataSources.ids
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# OS-specific files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Legacy Eclipse project files
+.classpath
+.project
+.cproject
+.settings/
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.ear
+
+# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
+hs_err_pid*
+
+## Plugin-specific files:
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Mongo Explorer plugin
+.idea/mongoSettings.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### AndroidStudio Patch ###
+
+!/gradle/wrapper/gradle-wrapper.jar
+
+### Dart ###
+# See https://www.dartlang.org/guides/libraries/private-files
+
+# Files and directories created by pub
+.dart_tool/
+.packages
+# If you're building an application, you may want to check-in your pubspec.lock
+pubspec.lock
+
+# Directory created by dartdoc
+# If you don't generate documentation locally you can remove this line.
+doc/api/
+
+# Avoid committing generated Javascript files:
+*.dart.js
+*.info.json      # Produced by the --dump-info flag.
+*.js             # When generated by dart2js. Don't specify *.js if your
+                 # project includes source files written in JavaScript.
+*.js_
+*.js.deps
+*.js.map
+
+# End of https://www.gitignore.io/api/dart,android,androidstudio

+ 10 - 0
thirdparty/super_logging/.metadata

@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713
+  channel: stable
+
+project_type: package

+ 21 - 0
thirdparty/super_logging/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 PyCampers
+
+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.

+ 54 - 0
thirdparty/super_logging/README.md

@@ -0,0 +1,54 @@
+# Super Logging
+
+[![Sponsor](https://img.shields.io/badge/Sponsor-jaaga_labs-red.svg?style=for-the-badge)](https://www.jaaga.in/labs)
+
+[![pub package](https://img.shields.io/pub/v/super_logging.svg?style=for-the-badge)](https://pub.dartlang.org/packages/super_logging)
+
+This package lets you easily log to:
+- stdout
+- disk
+- sentry.io
+
+```dart
+import 'package:super_logging/super_logging.dart';
+import 'package:logging/logging.dart';
+
+final logger = Logger("main");
+
+main() async {
+  // just call once, and let it handle the rest!
+  await SuperLogging.main();
+  
+  logger.info("hello!");
+}
+```
+
+(Above example will log to stdout and disk.)
+
+## Logging to sentry.io
+
+Just specify your sentry DSN.
+
+```dart
+SuperLogging.main(LogConfig(
+  sentryDsn: 'https://xxxx@sentry.io/yyyy',
+));
+```
+
+## Log uncaught errors
+
+Just provide the contents of your `main()` function to super logging.
+
+```dart
+void main() {
+  SuperLogging.main(LogConfig(
+    body: _main,
+  ));
+}
+
+void _main() {
+  runApp(MyApp());
+}
+```
+
+[Read the docs](https://pub.dev/documentation/super_logging/latest/super_logging/super_logging-library.html) to know about more customization options.

+ 323 - 0
thirdparty/super_logging/lib/super_logging.dart

@@ -0,0 +1,323 @@
+library super_logging;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:intl/intl.dart';
+import 'package:logging/logging.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+import 'package:path/path.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:sentry/sentry.dart';
+
+export 'package:sentry/sentry.dart' show User;
+
+typedef FutureOr<void> FutureOrVoidCallback();
+
+extension SuperString on String {
+  Iterable<String> chunked(int chunkSize) sync* {
+    var start = 0;
+
+    while (true) {
+      var stop = start + chunkSize;
+      if (stop > length) break;
+      yield substring(start, stop);
+      start = stop;
+    }
+
+    if (start < length) {
+      yield substring(start);
+    }
+  }
+}
+
+extension SuperLogRecord on LogRecord {
+  String toPrettyString([String extraLines]) {
+    var header = "[$loggerName] [$level] [$time]";
+
+    var msg = "$header $message";
+
+    if (error != null) {
+      msg += "\n⤷ type: ${error.runtimeType}\n⤷ error: $error";
+    }
+    if (stackTrace != null) {
+      msg += "\n⤷ trace: $stackTrace";
+    }
+
+    for (var line in extraLines?.split('\n') ?? []) {
+      msg += '\n$header $line';
+    }
+
+    return msg;
+  }
+
+  Event toEvent({String appVersion}) {
+    return Event(
+      release: appVersion,
+      level: SeverityLevel.error,
+      culprit: message,
+      loggerName: loggerName,
+      exception: error,
+      stackTrace: stackTrace,
+    );
+  }
+}
+
+class LogConfig {
+  /// The DSN for a Sentry app.
+  /// This can be obtained from the Sentry apps's "settings > Client Keys (DSN)" page.
+  ///
+  /// Only logs containing errors are sent to sentry.
+  /// Errors can be caught using a try-catch block, like so:
+  ///
+  /// ```
+  /// final logger = Logger("main");
+  ///
+  /// try {
+  ///   // do something dangerous here
+  /// } catch(e, trace) {
+  ///   logger.info("Huston, we have a problem", e, trace);
+  /// }
+  /// ```
+  ///
+  /// If this is [null], Sentry logger is completely disabled (default).
+  String sentryDsn;
+
+  /// A built-in retry mechanism for sending errors to sentry.
+  ///
+  /// This parameter defines the time to wait for, before retrying.
+  Duration sentryRetryDelay;
+
+  /// Path of the directory where log files will be stored.
+  ///
+  /// If this is [null], file logging is completely disabled (default).
+  ///
+  /// If this is an empty string (['']),
+  /// then a 'logs' directory will be created in [getTemporaryDirectory()].
+  ///
+  /// A non-empty string will be treated as an explicit path to a directory.
+  ///
+  /// The chosen directory can be accessed using [SuperLogging.logFile.parent].
+  String logDirPath;
+
+  /// The maximum number of log files inside [logDirPath].
+  ///
+  /// One log file is created per day.
+  /// Older log files are deleted automatically.
+  int maxLogFiles;
+
+  /// Whether to enable super logging features in debug mode.
+  ///
+  /// Sentry and file logging are typically not needed in debug mode,
+  /// where a complete logcat is available.
+  bool enableInDebugMode;
+
+  /// If provided, super logging will invoke this function, and
+  /// any uncaught errors during its execution will be reported.
+  ///
+  /// Works by using [FlutterError.onError] and [runZoned].
+  FutureOrVoidCallback body;
+
+  /// The date format for storing log files.
+  ///
+  /// `DateFormat('y-M-d')` by default.
+  DateFormat dateFmt;
+
+  LogConfig({
+    this.sentryDsn,
+    this.sentryRetryDelay = const Duration(seconds: 30),
+    this.logDirPath,
+    this.maxLogFiles = 10,
+    this.enableInDebugMode = false,
+    this.body,
+    this.dateFmt,
+  }) {
+    dateFmt ??= DateFormat("y-M-d");
+  }
+}
+
+class SuperLogging {
+  /// The logger for SuperLogging
+  static final $ = Logger('ente_logging');
+
+  /// The current super logging configuration
+  static LogConfig config;
+
+  static Future<void> main([LogConfig config]) async {
+    config ??= LogConfig();
+    SuperLogging.config = config;
+
+    WidgetsFlutterBinding.ensureInitialized();
+
+    appVersion ??= await getAppVersion();
+
+    final enable = config.enableInDebugMode || kReleaseMode;
+    sentryIsEnabled = enable && config.sentryDsn != null;
+    fileIsEnabled = enable && config.logDirPath != null;
+
+    if (fileIsEnabled) {
+      await setupLogDir();
+    }
+    if (sentryIsEnabled) {
+      sentryUploader();
+    }
+
+    Logger.root.level = Level.ALL;
+    Logger.root.onRecord.listen(onLogRecord);
+
+    if (!enable) {
+      $.info("detected debug mode; sentry & file logging disabled.");
+    }
+    if (fileIsEnabled) {
+      $.info("using this log file for today: $logFile");
+    }
+    if (sentryIsEnabled) {
+      $.info("sentry uploader started");
+    }
+
+    if (config.body == null) return;
+
+    if (enable) {
+      FlutterError.onError = (details) {
+        $.fine(
+          "uncaught error from FlutterError.onError()",
+          details.exception,
+          details.stack,
+        );
+      };
+      await runZoned(config.body, onError: (e, trace) {
+        $.fine("uncaught error from runZoned()", e, trace);
+      });
+    } else {
+      await config.body();
+    }
+  }
+
+  static var _lastExtraLines = '';
+
+  static Future onLogRecord(LogRecord rec) async {
+    // log misc info if it changed
+    var extraLines = "app version: '$appVersion'\n";
+    if (extraLines != _lastExtraLines) {
+      _lastExtraLines = extraLines;
+    } else {
+      extraLines = null;
+    }
+
+    var str = rec.toPrettyString(extraLines);
+
+    // write to stdout
+    printLog(str);
+
+    // write to logfile
+    if (fileIsEnabled) {
+      final strForLogFile = str + '\n';
+      await logFile.writeAsString(strForLogFile,
+          mode: FileMode.append, flush: true);
+    }
+
+    // add error to sentry queue
+    if (sentryIsEnabled && rec.error != null) {
+      var event = rec.toEvent(appVersion: appVersion);
+      sentryQueueControl.add(event);
+    }
+  }
+
+  // Logs on must be chunked or they get truncated otherwise
+  // See https://github.com/flutter/flutter/issues/22665
+  static var logChunkSize = 800;
+
+  static void printLog(String text) {
+    text.chunked(logChunkSize).forEach(print);
+  }
+
+  /// A queue to be consumed by [sentryUploader].
+  static final sentryQueueControl = StreamController<Event>();
+
+  /// Whether sentry logging is currently enabled or not.
+  static bool sentryIsEnabled;
+
+  static Future<void> sentryUploader() async {
+    var client = SentryClient(dsn: config.sentryDsn);
+
+    await for (final event in sentryQueueControl.stream) {
+      dynamic error;
+
+      try {
+        var response = await client.capture(event: event);
+        error = response.error;
+      } catch (e) {
+        error = e;
+      }
+
+      if (error == null) continue;
+      $.fine(
+        "sentry upload failed; will retry after ${config.sentryRetryDelay} ($error)",
+      );
+      doSentryRetry(event);
+    }
+  }
+
+  static void doSentryRetry(Event event) async {
+    await Future.delayed(config.sentryRetryDelay);
+    sentryQueueControl.add(event);
+  }
+
+  /// The log file currently in use.
+  static File logFile;
+
+  /// Whether file logging is currently enabled or not.
+  static bool fileIsEnabled;
+
+  static Future<void> setupLogDir() async {
+    var dirPath = config.logDirPath;
+
+    // choose [logDir]
+    if (dirPath.isEmpty) {
+      var root = await getExternalStorageDirectory();
+      dirPath = '${root.path}/logs';
+    }
+
+    // create [logDir]
+    var dir = Directory(dirPath);
+    await dir.create(recursive: true);
+
+    var files = <File>[];
+    var dates = <File, DateTime>{};
+
+    // collect all log files with valid names
+    await for (final file in dir.list()) {
+      try {
+        var date = config.dateFmt.parse(basename(file.path));
+        dates[file] = date;
+      } on FormatException {}
+    }
+
+    // delete old log files, if [maxLogFiles] is exceeded.
+    if (files.length > config.maxLogFiles) {
+      // sort files based on ascending order of date (older first)
+      files.sort((a, b) => dates[a].compareTo(dates[b]));
+
+      var extra = files.length - config.maxLogFiles;
+      var toDelete = files.sublist(0, extra);
+
+      for (var file in toDelete) {
+        await file.delete();
+      }
+    }
+
+    logFile = File("$dirPath/${config.dateFmt.format(DateTime.now())}.txt");
+  }
+
+  /// Current app version, obtained from package_info plugin.
+  ///
+  /// See: [getAppVersion]
+  static String appVersion;
+
+  static Future<String> getAppVersion() async {
+    var pkgInfo = await PackageInfo.fromPlatform();
+    return "${pkgInfo.version}+${pkgInfo.buildNumber}";
+  }
+}

+ 61 - 0
thirdparty/super_logging/pubspec.yaml

@@ -0,0 +1,61 @@
+name: super_logging
+description: The usual dart logging module, but with superpowers! Let's you easily log to stdout, disk and sentry.io.
+version: 1.3.4
+author: Dev Aggarwal <devpxy@gmail.com>
+homepage: https://github.com/scientifichackers/super_logging
+
+environment:
+  sdk: ">=2.6.0 <3.0.0"
+
+dependencies:
+  flutter:
+    sdk: flutter
+
+  package_info_plus: ^0.5.0
+  device_info: ^0.4.1+4
+  logging: ^0.11.4
+  sentry: ^3.0.1
+  intl: ^0.16.1
+  path: ^1.6.4
+  path_provider: ^1.6.0
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+
+# For information on the generic Dart part of this file, see the
+# following page: https://www.dartlang.org/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+# To add assets to your package, add an assets section, like this:
+# assets:
+#  - images/a_dot_burr.jpeg
+#  - images/a_dot_ham.jpeg
+#
+# For details regarding assets in packages, see
+# https://flutter.io/assets-and-images/#from-packages
+#
+# An image asset can refer to one or more resolution-specific "variants", see
+# https://flutter.io/assets-and-images/#resolution-aware.
+
+# To add custom fonts to your package, add a fonts section here,
+# in this "flutter" section. Each entry in this list should have a
+# "family" key with the font family name, and a "fonts" key with a
+# list giving the asset and other descriptors for the font. For
+# example:
+# fonts:
+#   - family: Schyler
+#     fonts:
+#       - asset: fonts/Schyler-Regular.ttf
+#       - asset: fonts/Schyler-Italic.ttf
+#         style: italic
+#   - family: Trajan Pro
+#     fonts:
+#       - asset: fonts/TrajanPro.ttf
+#       - asset: fonts/TrajanPro_Bold.ttf
+#         weight: 700
+#
+# For details regarding fonts in packages, see
+# https://flutter.io/custom-fonts/#from-packages

+ 55 - 0
thirdparty/super_logging/test/test_string_chunked.dart

@@ -0,0 +1,55 @@
+import 'dart:math';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:super_logging/super_logging.dart';
+
+var random = Random();
+
+void main() {
+  final chunkSize = SuperLogging.logChunkSize;
+
+  test('test with empty text', () {
+    var text = randomText(0);
+
+    var actual = text.chunked(chunkSize).toList();
+    var expected = [];
+
+    expect(expected, actual);
+  });
+
+  test('test with length < chunk size', () {
+    var text = randomText(chunkSize ~/ 2.5);
+
+    var actual = text.chunked(chunkSize).toList();
+    var expected = [text];
+
+    expect(expected, actual);
+  });
+
+  test('test with length = chunk size', () {
+    var text = randomText(chunkSize);
+
+    var actual = text.chunked(chunkSize).toList();
+    var expected = [text];
+
+    expect(expected, actual);
+  });
+
+  test('test with length > chunk size', () {
+    var text = randomText((chunkSize * 2.5).toInt());
+
+    var actual = text.chunked(chunkSize).toList();
+    var expected = [
+      text.substring(0, chunkSize),
+      text.substring(chunkSize, chunkSize * 2),
+      text.substring(chunkSize * 2)
+    ];
+
+    expect(expected, actual);
+  });
+}
+
+String randomText(int len) {
+  var charCodes = List.generate(len, (index) => random.nextInt(0x10FFFF));
+  return String.fromCharCodes(charCodes).substring(0, len);
+}