Skip to content

Commit

Permalink
Adding more information to LogFileStats + minor updates to tests (#31)
Browse files Browse the repository at this point in the history
* Adding duration for start and end times + record count

* Update USAGE_GUIDE.md

* Update to include counts for each event in LogFileStats

* Update CHANGELOG.md

* Use package:clock for LogFileStats calculations

* Update to test to include newly added data points

* Added test to check CHANGELOG for matching versions

* Prep for publishing

* Remove redundant expect statement

* Ensure that no events are being sent for the first run + test

* Fixing tests to account for no events sent on tool first run

* Clean up from dart format

* dart format on test directory

* Use one clock for `now`

* Including formatDateTime to include timezone offset

* Updating documentation of `local_time`
  • Loading branch information
eliasyishak authored Mar 10, 2023
1 parent a51c094 commit e5353aa
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 68 deletions.
3 changes: 2 additions & 1 deletion pkgs/unified_analytics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 0.1.1-dev
## 0.1.1

- Bumping intl package to 0.18.0 to fix version solving issue with flutter_tools
- LogFileStats includes more information about how many events are persisted and total count of how many times each event was sent

## 0.1.0

Expand Down
22 changes: 17 additions & 5 deletions pkgs/unified_analytics/USAGE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,30 @@ print(analytics.logFileStats());
// Prints out the below
// {
// "startDateTime": "2023-02-08 15:07:10.293728",
// "endDateTime": "2023-02-08 15:07:10.299678",
// "sessionCount": 1,
// "flutterChannelCount": 1,
// "toolCount": 1
// "startDateTime": "2023-02-22 15:23:24.410921",
// "minsFromStartDateTime": 20319,
// "endDateTime": "2023-03-08 15:46:36.318211",
// "minsFromEndDateTime": 136,
// "sessionCount": 7,
// "flutterChannelCount": 2,
// "toolCount": 1,
// "recordCount": 23,
// "eventCount": {
// "hot_reload_time": 16,
// "analytics_collection_enabled": 7,
// ... scales up with number of events
// }
// }
```

Explanation of the each key above

- startDateTime: the earliest event that was sent
- minsFromStartDateTime: the number of minutes elapsed since the earliest message
- endDateTime: the latest, most recent event that was sent
- minsFromEndDateTime: the number of minutes elapsed since the latest message
- sessionCount: count of sessions; sessions have a minimum time of 30 minutes
- flutterChannelCount: count of flutter channels (can be 0 if developer is a Dart dev only)
- toolCount: count of the Dart and Flutter tools sending analytics
- recordCount: count of the total number of events in the log file
- eventCount: counts each unique event and how many times they occurred in the log file
11 changes: 9 additions & 2 deletions pkgs/unified_analytics/lib/src/analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,14 @@ class AnalyticsImpl implements Analytics {
required DashEvent eventName,
Map<String, Object?> eventData = const {},
}) {
if (!telemetryEnabled) return null;
// Checking the [telemetryEnabled] boolean reflects what the
// config file reflects
//
// Checking the [_showMessage] boolean indicates if this the first
// time the tool is using analytics or if there has been an update
// the messaging found in constants.dart - in both cases, analytics
// will not be sent until the second time the tool is used
if (!telemetryEnabled || _showMessage) return null;

// Construct the body of the request
final Map<String, Object?> body = generateRequestBody(
Expand Down Expand Up @@ -311,7 +318,7 @@ class TestAnalytics extends AnalyticsImpl {
required DashEvent eventName,
Map<String, Object?> eventData = const {},
}) {
if (!telemetryEnabled) return null;
if (!telemetryEnabled || _showMessage) return null;

// Calling the [generateRequestBody] method will ensure that the
// session file is getting updated without actually making any
Expand Down
2 changes: 1 addition & 1 deletion pkgs/unified_analytics/lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const int kLogFileLength = 2500;
const String kLogFileName = 'dash-analytics.log';

/// The current version of the package, should be in line with pubspec version.
const String kPackageVersion = '0.1.1-dev';
const String kPackageVersion = '0.1.1';

/// The minimum length for a session
const int kSessionDurationMinutes = 30;
Expand Down
62 changes: 58 additions & 4 deletions pkgs/unified_analytics/lib/src/log_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:convert';

import 'package:clock/clock.dart';
import 'package:file/file.dart';
import 'package:path/path.dart' as p;

Expand All @@ -16,9 +17,15 @@ class LogFileStats {
/// The oldest timestamp in the log file
final DateTime startDateTime;

/// Number of minutes from [startDateTime] to [clock.now()]
final int minsFromStartDateTime;

/// The latest timestamp in the log file
final DateTime endDateTime;

/// Number of minutes from [endDateTime] to [clock.now()]
final int minsFromEndDateTime;

/// The number of unique session ids found in the log file
final int sessionCount;

Expand All @@ -28,22 +35,37 @@ class LogFileStats {
/// The number of unique tools found in the log file
final int toolCount;

/// The map containing all of the events in the file along with
/// how many times they have occured
final Map<String, int> eventCount;

/// Total number of records in the log file
final int recordCount;

/// Contains the data from the [LogHandler.logFileStats] method
const LogFileStats({
required this.startDateTime,
required this.minsFromStartDateTime,
required this.endDateTime,
required this.minsFromEndDateTime,
required this.sessionCount,
required this.flutterChannelCount,
required this.toolCount,
required this.recordCount,
required this.eventCount,
});

@override
String toString() => jsonEncode(<String, Object?>{
'startDateTime': startDateTime.toString(),
'minsFromStartDateTime': minsFromStartDateTime,
'endDateTime': endDateTime.toString(),
'minsFromEndDateTime': minsFromEndDateTime,
'sessionCount': sessionCount,
'flutterChannelCount': flutterChannelCount,
'toolCount': toolCount,
'recordCount': recordCount,
'eventCount': eventCount,
});
}

Expand Down Expand Up @@ -89,26 +111,42 @@ class LogHandler {
final DateTime startDateTime = records.first.localTime;
final DateTime endDateTime = records.last.localTime;

// Collection of unique sessions
// Map with counters for user properties
final Map<String, Set<Object>> counter = <String, Set<Object>>{
'sessions': <int>{},
'flutter_channel': <String>{},
'tool': <String>{},
};

// Map of counters for each event
final Map<String, int> eventCount = <String, int>{};
for (LogItem record in records) {
counter['sessions']!.add(record.sessionId);
counter['tool']!.add(record.tool);
if (record.flutterChannel != null) {
counter['flutter_channel']!.add(record.flutterChannel!);
}

// Count each event, if it doesn't exist in the [eventCount]
// it will be added first
if (!eventCount.containsKey(record.eventName)) {
eventCount[record.eventName] = 0;
}
eventCount[record.eventName] = eventCount[record.eventName]! + 1;
}

final DateTime now = clock.now();

return LogFileStats(
startDateTime: startDateTime,
minsFromStartDateTime: now.difference(startDateTime).inMinutes,
endDateTime: endDateTime,
minsFromEndDateTime: now.difference(endDateTime).inMinutes,
sessionCount: counter['sessions']!.length,
flutterChannelCount: counter['flutter_channel']!.length,
toolCount: counter['tool']!.length,
eventCount: eventCount,
recordCount: records.length,
);
}

Expand All @@ -135,6 +173,7 @@ class LogHandler {

/// Data class for each record persisted on the client's machine
class LogItem {
final String eventName;
final int sessionId;
final String? flutterChannel;
final String host;
Expand All @@ -144,6 +183,7 @@ class LogItem {
final DateTime localTime;

LogItem({
required this.eventName,
required this.sessionId,
this.flutterChannel,
required this.host,
Expand Down Expand Up @@ -194,18 +234,27 @@ class LogItem {
/// "value": "flutter-tools"
/// },
/// "local_time": {
/// "value": "2023-01-31 14:32:14.592898"
/// "value": "2023-01-31 14:32:14.592898 -0500"
/// }
/// }
/// }
/// ```
static LogItem? fromRecord(Map<String, Object?> record) {
if (!record.containsKey('user_properties')) return null;
if (!record.containsKey('user_properties') ||
!record.containsKey('events')) {
return null;
}

// Using a try/except here to parse out the fields if possible,
// if not, it will quietly return null and won't get processed
// downstream
try {
// Parse out values from the top level key = 'events' and return
// a map for the one event in the value
final Map<String, Object?> eventProp =
((record['events']! as List<Object?>).first as Map<String, Object?>);
final String eventName = eventProp['name'] as String;

// Parse the data out of the `user_properties` value
final Map<String, Object?> userProps =
record['user_properties'] as Map<String, Object?>;
Expand All @@ -230,6 +279,10 @@ class LogItem {
// indicates the record is malformed; note that `flutter_version`
// and `flutter_channel` are nullable fields in the log file
final List<Object?> values = <Object?>[
// Values associated with the top level key = 'events'
eventName,

// Values associated with the top level key = 'events'
sessionId,
host,
dartVersion,
Expand All @@ -241,9 +294,10 @@ class LogItem {
}

// Parse the local time from the string extracted
final DateTime localTime = DateTime.parse(localTimeString!);
final DateTime localTime = DateTime.parse(localTimeString!).toLocal();

return LogItem(
eventName: eventName,
sessionId: sessionId!,
flutterChannel: flutterChannel,
host: host!,
Expand Down
3 changes: 2 additions & 1 deletion pkgs/unified_analytics/lib/src/user_property.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:clock/clock.dart';

import 'constants.dart';
import 'session.dart';
import 'utils.dart';

class UserProperty {
final Session session;
Expand Down Expand Up @@ -58,6 +59,6 @@ class UserProperty {
'dart_version': dartVersion,
'analytics_pkg_version': kPackageVersion,
'tool': tool,
'local_time': '${clock.now()}',
'local_time': formatDateTime(clock.now()),
};
}
17 changes: 16 additions & 1 deletion pkgs/unified_analytics/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ import 'package:file/file.dart';
import 'enums.dart';
import 'user_property.dart';

/// Format time as 'yyyy-MM-dd HH:mm:ss Z' where Z is the difference between the
/// timezone of t and UTC formatted according to RFC 822.
String formatDateTime(DateTime t) {
final String sign = t.timeZoneOffset.isNegative ? '-' : '+';
final Duration tzOffset = t.timeZoneOffset.abs();
final int hoursOffset = tzOffset.inHours;
final int minutesOffset =
tzOffset.inMinutes - (Duration.minutesPerHour * hoursOffset);
assert(hoursOffset < 24);
assert(minutesOffset < 60);

String twoDigits(int n) => (n >= 10) ? '$n' : '0$n';
return '$t $sign${twoDigits(hoursOffset)}${twoDigits(minutesOffset)}';
}

/// Construct the Map that will be converted to json for the
/// body of the request
///
Expand All @@ -26,7 +41,7 @@ import 'user_property.dart';
/// "flutter_version": { "value": "Flutter 3.6.0-7.0.pre.47" },
/// "dart_version": { "value": "Dart 2.19.0" },
/// "tool": { "value": "flutter-tools" },
/// "local_time": { "value": "2023-01-11 14:53:31.471816" }
/// "local_time": { "value": "2023-01-11 14:53:31.471816 -0500" }
/// }
/// }
/// ```
Expand Down
2 changes: 1 addition & 1 deletion pkgs/unified_analytics/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: >-
to Google Analytics.
# When updating this, keep the version consistent with the changelog and the
# value in lib/src/constants.dart.
version: 0.1.1-dev
version: 0.1.1
repository: https://github.com/dart-lang/tools/tree/main/pkgs/unified_analytics

environment:
Expand Down
Loading

0 comments on commit e5353aa

Please sign in to comment.