Clone super_logging and make it privacy friendly

This commit is contained in:
Vishnu Mohandas 2021-01-13 23:08:58 +05:30
parent 7397f08f0f
commit 06b679fca8
9 changed files with 769 additions and 5 deletions

View file

@ -752,10 +752,10 @@ packages:
super_logging:
dependency: "direct main"
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:
dependency: transitive
description:

View file

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

239
thirdparty/super_logging/.gitignore vendored Normal file
View file

@ -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
thirdparty/super_logging/.metadata vendored Normal file
View file

@ -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
thirdparty/super_logging/LICENSE vendored Normal file
View file

@ -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
thirdparty/super_logging/README.md vendored Normal file
View file

@ -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.

View file

@ -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
thirdparty/super_logging/pubspec.yaml vendored Normal file
View file

@ -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

View file

@ -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);
}