-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
318 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
library logging; | ||
|
||
import 'package:das_client/logging/src/das_log_tree.dart'; | ||
import 'package:das_client/logging/src/log_service.dart'; | ||
import 'package:fimber/fimber.dart'; | ||
|
||
class LoggingComponent { | ||
const LoggingComponent._(); | ||
|
||
static LogTree createDasLogTree() { | ||
return DasLogTree(logService: LogService()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:das_client/logging/src/log_entry.dart'; | ||
import 'package:das_client/logging/src/log_level.dart'; | ||
import 'package:das_client/logging/src/log_service.dart'; | ||
import 'package:das_client/util/device_id_info.dart'; | ||
import 'package:device_info_plus/device_info_plus.dart'; | ||
import 'package:fimber/fimber.dart'; | ||
import 'package:package_info_plus/package_info_plus.dart'; | ||
|
||
class DasLogTree extends LogTree { | ||
final LogService _logService; | ||
late Future<void> _initialized; | ||
final Map<String, String> metadata = {}; | ||
|
||
DasLogTree({required LogService logService}) : _logService = logService { | ||
_initialized = _init(); | ||
} | ||
|
||
Future<void> _init() async { | ||
Fimber.i('Initializing DasLogTree...'); | ||
metadata['deviceId'] = await DeviceIdInfo.getDeviceId(); | ||
|
||
final info = await PackageInfo.fromPlatform(); | ||
metadata['appVersion'] = info.version; | ||
|
||
final deviceInfoPlugin = DeviceInfoPlugin(); | ||
if (Platform.isAndroid) { | ||
_processAndroidDeviceInfo(await deviceInfoPlugin.androidInfo); | ||
} else if (Platform.isIOS) { | ||
_processIosDeviceInfo(await deviceInfoPlugin.iosInfo); | ||
} | ||
} | ||
|
||
void _processAndroidDeviceInfo(AndroidDeviceInfo deviceInfo) { | ||
metadata['systemName'] = "android"; | ||
metadata['systemVersion'] = deviceInfo.version.sdkInt.toString(); | ||
metadata['model'] = deviceInfo.model; | ||
} | ||
|
||
void _processIosDeviceInfo(IosDeviceInfo deviceInfo) { | ||
metadata['systemName'] = deviceInfo.systemName; | ||
metadata['systemVersion'] = deviceInfo.systemVersion; | ||
metadata['model'] = deviceInfo.model; | ||
} | ||
|
||
@override | ||
List<String> getLevels() { | ||
return ["I", "W", "E"]; | ||
} | ||
|
||
@override | ||
void log(String level, String message, {String? tag, ex, StackTrace? stacktrace}) { | ||
logInternal(level, message, tag: tag ?? LogTree.getTag(), ex: ex, stacktrace: stacktrace); | ||
} | ||
|
||
void logInternal(String level, String message, {String? tag, ex, StackTrace? stacktrace}) async { | ||
await _initialized; | ||
|
||
final messageBuilder = StringBuffer("$tag:\t $message"); | ||
|
||
if (ex != null) { | ||
messageBuilder.write("\n$ex"); | ||
} | ||
if (stacktrace != null) { | ||
final tmpStacktrace = stacktrace.toString().split('\n'); | ||
final stackTraceMessage = | ||
tmpStacktrace.map((stackLine) => "\t$stackLine").join("\n"); | ||
messageBuilder.write("\n$stackTraceMessage"); | ||
} | ||
|
||
_logService.save(LogEntry(messageBuilder.toString(), _getLogLevel(level), metadata)); | ||
} | ||
|
||
LogLevel _getLogLevel(String level) { | ||
switch (level) { | ||
case "D": | ||
return LogLevel.debug; | ||
case "W": | ||
return LogLevel.warning; | ||
case "E": | ||
return LogLevel.error; | ||
case "V": | ||
return LogLevel.trace; | ||
case "I": | ||
default: | ||
return LogLevel.info; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import 'package:das_client/logging/src/log_level.dart'; | ||
|
||
class LogEntry { | ||
LogEntry(this.message, this.level, this.metadata) | ||
: time = DateTime.now().millisecondsSinceEpoch / 1000, | ||
source = "das_client"; | ||
|
||
final double time; | ||
final String source; | ||
final String message; | ||
final LogLevel level; | ||
final Map<String, dynamic> metadata; | ||
|
||
Map<String, dynamic> toJson() => { | ||
'time': time, | ||
'source': source, | ||
'message': message, | ||
'level': level.name, | ||
'metadata': metadata, | ||
}; | ||
|
||
LogEntry.fromJson(Map<String, dynamic> json) | ||
: time = json['time'], | ||
source = json['source'], | ||
message = json['message'], | ||
level = LogLevel.values.firstWhere((element) => element.name == json['level']), | ||
metadata = json['metadata']; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
enum LogLevel { trace, debug, info, warning, error, fatal } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:das_client/di.dart'; | ||
import 'package:das_client/logging/src/log_entry.dart'; | ||
import 'package:das_client/service/backend_service.dart'; | ||
import 'package:fimber/fimber.dart'; | ||
import 'package:path_provider/path_provider.dart'; | ||
import 'package:synchronized/synchronized.dart'; | ||
|
||
class LogService { | ||
static const _rolloverTimeMinutes = 1; | ||
static const _maxFileSize = 50 * 1024; | ||
static const _prefix = "das-log"; | ||
static const _lastSavedFileName = "$_prefix-lastSavedFile.json"; | ||
final _lock = Lock(); | ||
final _senderLock = Lock(); | ||
|
||
late final Future<void> _initialized; | ||
late String _logPath; | ||
|
||
DateTime _nextRolloverTimeStamp = DateTime.now().add(const Duration(minutes: _rolloverTimeMinutes)); | ||
|
||
LogService() { | ||
_initialized = _init(); | ||
} | ||
|
||
Future<void> _init() async { | ||
Fimber.i('Initializing LogService...'); | ||
_logPath = await _getLogPath(); | ||
} | ||
|
||
Future<String> _getLogPath() async { | ||
return "${(await getApplicationSupportDirectory()).path}/logs"; | ||
} | ||
|
||
void save(LogEntry log) { | ||
_saveInternal(log); | ||
} | ||
|
||
void _saveInternal(LogEntry log) async { | ||
await _initialized; | ||
_lock.synchronized(() { | ||
var lastSavedFile = File("$_logPath/$_lastSavedFileName"); | ||
if (!(lastSavedFile.existsSync())) { | ||
lastSavedFile.createSync(recursive: true); | ||
} | ||
lastSavedFile.writeAsStringSync("${jsonEncode(log)},", mode: FileMode.append); | ||
|
||
// Check rollover | ||
if (lastSavedFile.lengthSync() > _maxFileSize || _nextRolloverTimeStamp.isBefore(DateTime.now())) { | ||
Fimber.d("Rolling over log file"); | ||
lastSavedFile.renameSync("$_logPath/$_prefix-${DateTime.now().millisecondsSinceEpoch}.json"); | ||
_nextRolloverTimeStamp = DateTime.now().add(const Duration(minutes: _rolloverTimeMinutes)); | ||
_sendLogs(); | ||
} | ||
}); | ||
} | ||
|
||
void _sendLogs() async { | ||
_senderLock.synchronized(() async { | ||
var logDir = Directory(_logPath); | ||
var files = logDir.listSync(); | ||
Fimber.d('Found ${files.length} log files in log directory: $_logPath'); | ||
|
||
for (var file in files) { | ||
if (file is File && file.path.endsWith(".json") && !file.path.contains(_lastSavedFileName)) { | ||
Fimber.d('Sending ${file.path} to backend'); | ||
|
||
var content = file.readAsStringSync(); | ||
content = '[${content.substring(0, content.length - 1)}]'; // Remove trailing comma | ||
|
||
Iterable iterable = json.decode(content); | ||
List<LogEntry> logEntries = List<LogEntry>.from(iterable.map((json) => LogEntry.fromJson(json))); | ||
|
||
final backendService = await DI.readyGet<BackendService>(); | ||
if (await backendService.sendLogs(logEntries)) { | ||
file.deleteSync(); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.