Skip to content

Commit 743a1d0

Browse files
jwrencommit-bot@chromium.org
authored andcommitted
[dartdev] Reimplement the dartdev analytics with events and custom dimensions, matching the flutter cli analytics patterns where possible
Bug: dart-lang/sdk#43198 Change-Id: Iff3ac619965142c10864559ff396b64130f18daf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161200 Reviewed-by: Devon Carew <[email protected]> Commit-Queue: Jaime Wren <[email protected]>
1 parent e3fcee3 commit 743a1d0

16 files changed

+758
-88
lines changed

pkg/dartdev/lib/dartdev.dart

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'src/commands/pub.dart';
2222
import 'src/commands/run.dart';
2323
import 'src/commands/test.dart';
2424
import 'src/core.dart';
25+
import 'src/events.dart';
2526
import 'src/experiments.dart';
2627
import 'src/sdk.dart';
2728
import 'src/utils.dart';
@@ -35,10 +36,6 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
3536
final stopwatch = Stopwatch();
3637
int result;
3738

38-
// The Analytics instance used to report information back to Google Analytics,
39-
// see lib/src/analytics.dart.
40-
Analytics analytics;
41-
4239
// The exit code for the dartdev process, null indicates that it has not yet
4340
// been set yet. The value is set in the catch and finally blocks below.
4441
int exitCode;
@@ -47,7 +44,9 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
4744
Object exception;
4845
StackTrace stackTrace;
4946

50-
analytics =
47+
// The Analytics instance used to report information back to Google Analytics,
48+
// see lib/src/analytics.dart.
49+
Analytics analytics =
5150
createAnalyticsInstance(args.contains('--disable-dartdev-analytics'));
5251

5352
// If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
@@ -92,11 +91,13 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
9291
// RunCommand.ddsHost = ddsUrl[0];
9392
// RunCommand.ddsPort = ddsUrl[1];
9493
}
94+
9595
String commandName;
9696

9797
try {
9898
stopwatch.start();
9999
final runner = DartdevRunner(args);
100+
100101
// Run can't be called with the '--disable-dartdev-analytics' flag, remove
101102
// it if it is contained in args.
102103
if (args.contains('--disable-dartdev-analytics')) {
@@ -119,14 +120,6 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
119120
)
120121
.toList();
121122

122-
// Before calling to run, send the first ping to analytics to have the first
123-
// ping, as well as the command itself, running in parallel.
124-
if (analytics.enabled) {
125-
commandName = getCommandStr(args, runner.commands.keys.toList());
126-
// ignore: unawaited_futures
127-
analytics.sendEvent(eventCategory, commandName);
128-
}
129-
130123
// If ... help pub ... is in the args list, remove 'help', and add '--help'
131124
// to the end of the list. This will make it possible to use the help
132125
// command to access subcommands of pub such as `dart help pub publish`, see
@@ -135,6 +128,17 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
135128
args = PubUtils.modifyArgs(args);
136129
}
137130

131+
// For the commands format and migrate, dartdev itself sends the
132+
// sendScreenView notification to analytics, for all other
133+
// dartdev commands (instances of DartdevCommand) the commands send this
134+
// to analytics.
135+
commandName = ArgParserUtils.getCommandStr(args);
136+
if (analytics.enabled &&
137+
(commandName == formatCmdName || commandName == migrateCmdName)) {
138+
// ignore: unawaited_futures
139+
analytics.sendScreenView(commandName);
140+
}
141+
138142
// Finally, call the runner to execute the command, see DartdevRunner.
139143
result = await runner.run(args);
140144
} catch (e, st) {
@@ -157,7 +161,22 @@ Future<void> runDartdev(List<String> args, SendPort port) async {
157161

158162
// Send analytics before exiting
159163
if (analytics.enabled) {
160-
analytics.setSessionValue(exitCodeParam, exitCode);
164+
// For commands that are not DartdevCommand instances, we manually create
165+
// and send the UsageEvent from here:
166+
if (commandName == formatCmdName) {
167+
// ignore: unawaited_futures
168+
FormatUsageEvent(
169+
exitCode: exitCode,
170+
args: args,
171+
).send(analyticsInstance);
172+
} else if (commandName == migrateCmdName) {
173+
// ignore: unawaited_futures
174+
MigrateUsageEvent(
175+
exitCode: exitCode,
176+
args: args,
177+
).send(analyticsInstance);
178+
}
179+
161180
// ignore: unawaited_futures
162181
analytics.sendTiming(commandName, stopwatch.elapsedMilliseconds,
163182
category: 'commands');

pkg/dartdev/lib/src/analytics.dart

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ const String analyticsDisabledNoticeMessage = '''
2626
║ `dart --enable-analytics` ║
2727
╚════════════════════════════════════════════════════════════════════════════╝
2828
''';
29-
const String _unknownCommand = '<unknown>';
3029
const String _appName = 'dartdev';
3130
const String _dartDirectoryName = '.dart';
3231
const String _settingsFileName = 'dartdev.json';
@@ -40,30 +39,32 @@ Dart programming language (https://dart.dev).
4039
const String eventCategory = 'dartdev';
4140
const String exitCodeParam = 'exitCode';
4241

43-
Analytics instance;
42+
Analytics _instance;
43+
44+
Analytics get analyticsInstance => _instance;
4445

4546
/// Create and return an [Analytics] instance, this value is cached and returned
4647
/// on subsequent calls.
4748
Analytics createAnalyticsInstance(bool disableAnalytics) {
48-
if (instance != null) {
49-
return instance;
49+
if (_instance != null) {
50+
return _instance;
5051
}
5152

5253
// Dartdev tests pass a hidden 'disable-dartdev-analytics' flag which is
5354
// handled here.
5455
// Also, stdout.hasTerminal is checked, if there is no terminal we infer that
5556
// a machine is running dartdev so we return analytics shouldn't be set.
5657
if (disableAnalytics) {
57-
instance = DisabledAnalytics(_trackingId, _appName);
58-
return instance;
58+
_instance = DisabledAnalytics(_trackingId, _appName);
59+
return _instance;
5960
}
6061

6162
var settingsDir = getDartStorageDirectory();
6263
if (settingsDir == null) {
6364
// Some systems don't support user home directories; for those, fail
6465
// gracefully by returning a disabled analytics object.
65-
instance = DisabledAnalytics(_trackingId, _appName);
66-
return instance;
66+
_instance = DisabledAnalytics(_trackingId, _appName);
67+
return _instance;
6768
}
6869

6970
if (!settingsDir.existsSync()) {
@@ -72,8 +73,8 @@ Analytics createAnalyticsInstance(bool disableAnalytics) {
7273
} catch (e) {
7374
// If we can't create the directory for the analytics settings, fail
7475
// gracefully by returning a disabled analytics object.
75-
instance = DisabledAnalytics(_trackingId, _appName);
76-
return instance;
76+
_instance = DisabledAnalytics(_trackingId, _appName);
77+
return _instance;
7778
}
7879
}
7980

@@ -85,22 +86,8 @@ Analytics createAnalyticsInstance(bool disableAnalytics) {
8586
}
8687

8788
var settingsFile = File(path.join(settingsDir.path, _settingsFileName));
88-
instance = DartdevAnalytics(_trackingId, settingsFile, _appName);
89-
return instance;
90-
}
91-
92-
/// Return the first member from [args] that occurs in [allCommands], otherwise
93-
/// '<unknown>' is returned.
94-
///
95-
/// 'help' is special cased to have 'dart analyze help', 'dart help analyze',
96-
/// and 'dart analyze --help' all be recorded as a call to 'help' instead of
97-
/// 'help' and 'analyze'.
98-
String getCommandStr(List<String> args, List<String> allCommands) {
99-
if (args.contains('help') || args.contains('-h') || args.contains('--help')) {
100-
return 'help';
101-
}
102-
return args.firstWhere((arg) => allCommands.contains(arg),
103-
orElse: () => _unknownCommand);
89+
_instance = DartdevAnalytics(_trackingId, settingsFile, _appName);
90+
return _instance;
10491
}
10592

10693
/// The directory used to store the analytics settings file.

pkg/dartdev/lib/src/commands/analyze.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,26 @@
55
import 'dart:async';
66
import 'dart:io';
77

8+
import 'package:meta/meta.dart';
89
import 'package:path/path.dart' as path;
910

1011
import '../core.dart';
12+
import '../events.dart';
1113
import '../sdk.dart';
1214
import '../utils.dart';
1315
import 'analyze_impl.dart';
1416

1517
class AnalyzeCommand extends DartdevCommand<int> {
18+
static const String cmdName = 'analyze';
19+
1620
/// The maximum length of any of the existing severity labels.
1721
static const int _severityWidth = 7;
1822

1923
/// The number of spaces needed to indent follow-on lines (the body) under the
2024
/// message. The width left for the severity label plus the separator width.
2125
static const int _bodyIndentWidth = _severityWidth + 3;
2226

23-
AnalyzeCommand() : super('analyze', "Analyze the project's Dart code.") {
27+
AnalyzeCommand() : super(cmdName, "Analyze the project's Dart code.") {
2428
argParser
2529
..addFlag('fatal-infos',
2630
help: 'Treat info level issues as fatal.', negatable: false)
@@ -32,7 +36,7 @@ class AnalyzeCommand extends DartdevCommand<int> {
3236
String get invocation => '${super.invocation} [<directory>]';
3337

3438
@override
35-
FutureOr<int> run() async {
39+
FutureOr<int> runImpl() async {
3640
if (argResults.rest.length > 1) {
3741
usageException('Only one directory is expected.');
3842
}
@@ -153,4 +157,19 @@ class AnalyzeCommand extends DartdevCommand<int> {
153157
return 0;
154158
}
155159
}
160+
161+
@override
162+
UsageEvent createUsageEvent(int exitCode) => AnalyzeUsageEvent(
163+
usagePath,
164+
exitCode: exitCode,
165+
args: argResults.arguments,
166+
);
167+
}
168+
169+
/// The [UsageEvent] for the analyze command.
170+
class AnalyzeUsageEvent extends UsageEvent {
171+
AnalyzeUsageEvent(String usagePath,
172+
{String label, @required int exitCode, @required List<String> args})
173+
: super(AnalyzeCommand.cmdName, usagePath,
174+
label: label, args: args, exitCode: exitCode);
156175
}

pkg/dartdev/lib/src/commands/compile.dart

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import 'dart:async';
66
import 'dart:io';
77

88
import 'package:dart2native/generate.dart';
9+
import 'package:meta/meta.dart';
910
import 'package:path/path.dart' as path;
1011

1112
import '../core.dart';
13+
import '../events.dart';
1214
import '../sdk.dart';
1315
import '../vm_interop_handler.dart';
1416

@@ -39,12 +41,13 @@ bool checkFile(String sourcePath) {
3941
stderr.flush();
4042
return false;
4143
}
42-
4344
return true;
4445
}
4546

46-
class CompileJSCommand extends DartdevCommand<int> {
47-
CompileJSCommand() : super('js', 'Compile Dart to JavaScript.') {
47+
class CompileJSCommand extends CompileSubcommandCommand {
48+
static const String cmdName = 'js';
49+
50+
CompileJSCommand() : super(cmdName, 'Compile Dart to JavaScript.') {
4851
argParser
4952
..addOption(
5053
commonOptions['outputFile'].flag,
@@ -63,7 +66,7 @@ class CompileJSCommand extends DartdevCommand<int> {
6366
String get invocation => '${super.invocation} <dart entry point>';
6467

6568
@override
66-
FutureOr<int> run() async {
69+
FutureOr<int> runImpl() async {
6770
if (!Sdk.checkArtifactExists(sdk.dart2jsSnapshot)) {
6871
return 255;
6972
}
@@ -97,7 +100,10 @@ class CompileJSCommand extends DartdevCommand<int> {
97100
}
98101
}
99102

100-
class CompileSnapshotCommand extends DartdevCommand<int> {
103+
class CompileSnapshotCommand extends CompileSubcommandCommand {
104+
static const String jitSnapshotCmdName = 'jit-snapshot';
105+
static const String kernelCmdName = 'kernel';
106+
101107
final String commandName;
102108
final String help;
103109
final String fileExt;
@@ -121,7 +127,7 @@ class CompileSnapshotCommand extends DartdevCommand<int> {
121127
String get invocation => '${super.invocation} <dart entry point>';
122128

123129
@override
124-
FutureOr<int> run() async {
130+
FutureOr<int> runImpl() async {
125131
// We expect a single rest argument; the dart entry point.
126132
if (argResults.rest.length != 1) {
127133
// This throws.
@@ -157,7 +163,10 @@ class CompileSnapshotCommand extends DartdevCommand<int> {
157163
}
158164
}
159165

160-
class CompileNativeCommand extends DartdevCommand<int> {
166+
class CompileNativeCommand extends CompileSubcommandCommand {
167+
static const String exeCmdName = 'exe';
168+
static const String aotSnapshotCmdName = 'aot-snapshot';
169+
161170
final String commandName;
162171
final String format;
163172
final String help;
@@ -194,7 +203,7 @@ Remove debugging information from the output and save it separately to the speci
194203
String get invocation => '${super.invocation} <dart entry point>';
195204

196205
@override
197-
FutureOr<int> run() async {
206+
FutureOr<int> runImpl() async {
198207
if (!Sdk.checkArtifactExists(genKernel) ||
199208
!Sdk.checkArtifactExists(genSnapshot)) {
200209
return 255;
@@ -230,30 +239,64 @@ Remove debugging information from the output and save it separately to the speci
230239
}
231240
}
232241

233-
class CompileCommand extends DartdevCommand {
234-
CompileCommand() : super('compile', 'Compile Dart to various formats.') {
242+
abstract class CompileSubcommandCommand extends DartdevCommand<int> {
243+
CompileSubcommandCommand(String name, String description,
244+
{bool hidden = false})
245+
: super(name, description, hidden: hidden);
246+
247+
@override
248+
UsageEvent createUsageEvent(int exitCode) => CompileUsageEvent(
249+
usagePath,
250+
exitCode: exitCode,
251+
args: argResults.arguments,
252+
);
253+
}
254+
255+
class CompileCommand extends DartdevCommand<int> {
256+
static const String cmdName = 'compile';
257+
258+
CompileCommand() : super(cmdName, 'Compile Dart to various formats.') {
235259
addSubcommand(CompileJSCommand());
236260
addSubcommand(CompileSnapshotCommand(
237-
commandName: 'jit-snapshot',
261+
commandName: CompileSnapshotCommand.jitSnapshotCmdName,
238262
help: 'to a JIT snapshot.',
239263
fileExt: 'jit',
240264
formatName: 'app-jit',
241265
));
242266
addSubcommand(CompileSnapshotCommand(
243-
commandName: 'kernel',
267+
commandName: CompileSnapshotCommand.kernelCmdName,
244268
help: 'to a kernel snapshot.',
245269
fileExt: 'dill',
246270
formatName: 'kernel',
247271
));
248272
addSubcommand(CompileNativeCommand(
249-
commandName: 'exe',
273+
commandName: CompileNativeCommand.exeCmdName,
250274
help: 'to a self-contained executable.',
251275
format: 'exe',
252276
));
253277
addSubcommand(CompileNativeCommand(
254-
commandName: 'aot-snapshot',
278+
commandName: CompileNativeCommand.aotSnapshotCmdName,
255279
help: 'to an AOT snapshot.',
256280
format: 'aot',
257281
));
258282
}
283+
284+
@override
285+
UsageEvent createUsageEvent(int exitCode) => null;
286+
287+
@override
288+
FutureOr<int> runImpl() {
289+
// do nothing, this command is never run
290+
return 0;
291+
}
292+
}
293+
294+
/// The [UsageEvent] for all compile commands, we could have each compile
295+
/// event be its own class instance, but for the time being [usagePath] takes
296+
/// care of the only difference.
297+
class CompileUsageEvent extends UsageEvent {
298+
CompileUsageEvent(String usagePath,
299+
{String label, @required int exitCode, @required List<String> args})
300+
: super(CompileCommand.cmdName, usagePath,
301+
label: label, exitCode: exitCode, args: args);
259302
}

0 commit comments

Comments
 (0)