Skip to content

Commit

Permalink
feat(logging): add amplify cloudwatch logger plugin base implementati…
Browse files Browse the repository at this point in the history
…on (#3736)
  • Loading branch information
NikaHsn committed Sep 12, 2023
1 parent eda0a6a commit ee143fe
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:amplify_logging_cloudwatch/src/amplify_log_stream_name_provider.dart';
import 'package:aws_logging_cloudwatch/aws_logging_cloudwatch.dart';

/// {@macro aws_logging_cloudwatch.cloudwatch_logger_plugin}
class AmplifyCloudWatchLoggerPlugin extends CloudWatchLoggerPlugin {
/// {@macro aws_logging_cloudwatch.cloudwatch_logger_plugin}
AmplifyCloudWatchLoggerPlugin({
required super.credentialsProvider,
required super.pluginConfig,
}) : super(
logStreamProvider: DefaultCloudWatchLogStreamProvider(
credentialsProvider: credentialsProvider,
region: pluginConfig.region,
logGroupName: pluginConfig.logGroupName,
defaultLogStreamNameProvider:
AmplifyLogStreamNameProvider().defaultLogStreamName,
),
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_logging_cloudwatch/src/device_info/device_info.dart';
import 'package:intl/intl.dart';

const _guestUserId = 'guest';

/// {@template amplify_logging_cloudwatch.amplify_log_stream_name_provider}
/// It uses {mm-dd-yyyy}.{deviceId}.{userId|guest} format for log stream name.
///
/// It gets deviceId from platform inforamtion or uuid if it is `null`.
/// It gets userId from `Amplify.Auth.getCurrentUser()).userId` or uses `guest`
/// if userId is not available.
/// {@endtemplate}
class AmplifyLogStreamNameProvider {
/// {@macro amplify_logging_cloudwatch.amplify_log_stream_name_provider}
AmplifyLogStreamNameProvider();
static final DateFormat _dateFormat = DateFormat('yyyy-MM-dd');
String? _deviceID;

/// Returns log stream name in `{mm-dd-yyyy}.{deviceId}.{userId|guest}` format
Future<String> defaultLogStreamName() async {
_deviceID ??= await getDeviceId() ?? UUID.getUUID();
String userId;
userId = await _getUserId();
return '${_dateFormat.format(DateTime.timestamp())}.$_deviceID.$userId';
}

Future<String> _getUserId() async {
String userId;
try {
userId = (await Amplify.Auth.getCurrentUser()).userId;
} on Error {
userId = _guestUserId;
} on Exception {
userId = _guestUserId;
}
return userId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'device_info.stub.dart'
if (dart.library.io) 'device_info.vm.dart'
if (dart.library.html) 'device_info.web.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// {@template amplify_logging_cloudwatch.device_info}
/// Returns device Id from platform information on `vm`.
/// Returns a UUID across browser sessions for web.
/// {@endtemplate}
Future<String?> getDeviceId() {
throw UnimplementedError('getDeviceId() has not been implemented.');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'dart:io';

import 'package:device_info_plus/device_info_plus.dart';

/// {@macro amplify_logging_cloudwatch.device_info}
Future<String?> getDeviceId() async {
final deviceInfo = DeviceInfoPlugin();
String? deviceID;
try {
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
deviceID = androidInfo.id;
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
deviceID = iosInfo.identifierForVendor ?? '';
} else if (Platform.isLinux) {
final linuxInfo = await deviceInfo.linuxInfo;
deviceID = linuxInfo.machineId ?? '';
} else if (Platform.isMacOS) {
final macInfo = await deviceInfo.macOsInfo;
deviceID = macInfo.systemGUID ?? '';
} else if (Platform.isWindows) {
final windowsInfo = await deviceInfo.windowsInfo;
deviceID = windowsInfo.deviceId;
}
} on Exception {
return null;
}
return deviceID;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'dart:html';

import 'package:amplify_core/amplify_core.dart';

const _localStorageKey = 'amplify-cloudwatch-logger-device-id';

/// {@macro amplify_logging_cloudwatch.device_info}
Future<String?> getDeviceId() async {
var deviceID = window.localStorage[_localStorageKey];
if (deviceID != null) {
return deviceID;
}
deviceID = UUID.getUUID();
window.localStorage.putIfAbsent(_localStorageKey, () => deviceID!);
return deviceID;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ dependencies:
aws_common: ">=0.6.0 <0.7.0"
aws_logging_cloudwatch: ^0.1.0
collection: ^1.15.0
device_info_plus: ^9.0.0
drift: ">=2.11.0 <2.12.0"
flutter:
sdk: flutter
intl: ">=0.18.0 <1.0.0"
meta: ^1.7.0
path_provider: ^2.0.0

Expand All @@ -27,4 +29,6 @@ dev_dependencies:
build_test: ^2.0.0
build_web_compilers: ^4.0.0
drift_dev: ^2.2.0+1
flutter_test:
sdk: flutter
test: ^1.22.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@TestOn('vm')

import 'package:amplify_logging_cloudwatch/src/amplify_log_stream_name_provider.dart';
import 'package:flutter_test/flutter_test.dart' as flutter;
import 'package:test/test.dart';

void main() {
flutter.TestWidgetsFlutterBinding.ensureInitialized();
test('it uses uuid and guest when their values are not provided', () async {
final logStreamNameProvider = AmplifyLogStreamNameProvider();
await expectLater(logStreamNameProvider.defaultLogStreamName(), completes);
});

test('it caches the device Id', () async {
final logStreamNameProvider = AmplifyLogStreamNameProvider();
final logStreamName1 = await logStreamNameProvider.defaultLogStreamName();
final logStreamName2 = await logStreamNameProvider.defaultLogStreamName();

expect(logStreamName1, logStreamName2);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,8 @@ class CloudWatchLoggerPlugin extends AWSLoggerPlugin
_logStreamProvider = logStreamProvider ??
DefaultCloudWatchLogStreamProvider(
logGroupName: pluginConfig.logGroupName,
client: CloudWatchLogsClient(
region: pluginConfig.region,
credentialsProvider: credentialsProvider,
),
region: pluginConfig.region,
credentialsProvider: credentialsProvider,
) {
_timer = pluginConfig.flushIntervalInSeconds > Duration.zero
? StoppableTimer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import 'dart:async';

import 'package:aws_common/aws_common.dart';
import 'package:aws_logging_cloudwatch/src/sdk/cloud_watch_logs.dart';
import 'package:intl/intl.dart';

Expand All @@ -22,30 +23,36 @@ abstract class CloudWatchLogStreamProvider {
/// {@template aws_logging_cloudwatch.default_cloudwatch_logstream_provider}
/// The default implementaion of [CloudWatchLogStreamProvider].
///
/// It uses `defaultLogStreamName` if provided otherwise uses `yyyy-MM-dd`
/// date format of UTC time now for the `defaultLogStreamName`.
/// It uses `defaultLogStreamNameProvider` if provided otherwise uses
/// `yyyy-MM-dd` date format of UTC time now for the `defaultLogStreamName`.
/// {@endtemplate}
class DefaultCloudWatchLogStreamProvider
implements CloudWatchLogStreamProvider {
/// {@macro aws_logging_cloudwatch.default_cloudwatch_logstream_provider}
DefaultCloudWatchLogStreamProvider({
required CloudWatchLogsClient client,
required AWSCredentialsProvider credentialsProvider,
required String region,
required String logGroupName,
String? defaultLogStreamName,
}) : _defaultLogStreamName = defaultLogStreamName,
_logGroupName = logGroupName,
_client = client;
FutureOr<String> Function()? defaultLogStreamNameProvider,
}) : _logGroupName = logGroupName,
_client = CloudWatchLogsClient(
region: region,
credentialsProvider: credentialsProvider,
),
_defaultLogStreamNameProvider = defaultLogStreamNameProvider;

final String? _defaultLogStreamName;
final FutureOr<String> Function()? _defaultLogStreamNameProvider;
final String _logGroupName;
final CloudWatchLogsClient _client;

static final DateFormat _dateFormat = DateFormat('yyyy-MM-dd');
final _createdLogStreams = <String>{};

@override
Future<String> get defaultLogStream async {
final logStreamName =
_defaultLogStreamName ?? _dateFormat.format(DateTime.timestamp());
final logStreamNameProvider =
_defaultLogStreamNameProvider ?? _timeBasedLogStreamNameProvider;
final logStreamName = await logStreamNameProvider();
if (_createdLogStreams.contains(logStreamName)) {
return logStreamName;
}
Expand All @@ -54,6 +61,10 @@ class DefaultCloudWatchLogStreamProvider
return logStreamName;
}

String _timeBasedLogStreamNameProvider() {
return _dateFormat.format(DateTime.timestamp());
}

@override
Future<void> createLogStream(String logStreamName) async {
try {
Expand Down

0 comments on commit ee143fe

Please sign in to comment.