blob: 7a227fc006948494c6411bc68c70dacd025fa61c [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of reporting;
/// A generic usage even that does not involve custom dimensions.
///
/// If sending values for custom dimensions is required, extend this class as
/// below.
class UsageEvent {
UsageEvent(this.category, this.parameter, {
this.label,
this.value,
required this.flutterUsage,
});
final String category;
final String parameter;
final String? label;
final int? value;
final Usage flutterUsage;
void send() {
flutterUsage.sendEvent(category, parameter, label: label, value: value);
}
}
/// A usage event related to hot reload/restart.
///
/// On a successful hot reload, we collect stats that help understand scale of
/// the update. For example, [syncedLibraryCount]/[finalLibraryCount] indicates
/// how many libraries were affected by the hot reload request. Relation of
/// [invalidatedSourcesCount] to [syncedLibraryCount] should help understand
/// sync/transfer "overhead" of updating this number of source files.
class HotEvent extends UsageEvent {
HotEvent(String parameter, {
required this.targetPlatform,
required this.sdkName,
required this.emulator,
required this.fullRestart,
required this.fastReassemble,
this.reason,
this.finalLibraryCount,
this.syncedLibraryCount,
this.syncedClassesCount,
this.syncedProceduresCount,
this.syncedBytes,
this.invalidatedSourcesCount,
this.transferTimeInMs,
this.overallTimeInMs,
this.compileTimeInMs,
this.findInvalidatedTimeInMs,
this.scannedSourcesCount,
this.reassembleTimeInMs,
this.reloadVMTimeInMs,
}) : super('hot', parameter, flutterUsage: globals.flutterUsage);
final String? reason;
final String targetPlatform;
final String sdkName;
final bool emulator;
final bool fullRestart;
final bool fastReassemble;
final int? finalLibraryCount;
final int? syncedLibraryCount;
final int? syncedClassesCount;
final int? syncedProceduresCount;
final int? syncedBytes;
final int? invalidatedSourcesCount;
final int? transferTimeInMs;
final int? overallTimeInMs;
final int? compileTimeInMs;
final int? findInvalidatedTimeInMs;
final int? scannedSourcesCount;
final int? reassembleTimeInMs;
final int? reloadVMTimeInMs;
@override
void send() {
final CustomDimensions parameters = CustomDimensions(
hotEventTargetPlatform: targetPlatform,
hotEventSdkName: sdkName,
hotEventEmulator: emulator,
hotEventFullRestart: fullRestart,
hotEventReason: reason,
hotEventFinalLibraryCount: finalLibraryCount,
hotEventSyncedLibraryCount: syncedLibraryCount,
hotEventSyncedClassesCount: syncedClassesCount,
hotEventSyncedProceduresCount: syncedProceduresCount,
hotEventSyncedBytes: syncedBytes,
hotEventInvalidatedSourcesCount: invalidatedSourcesCount,
hotEventTransferTimeInMs: transferTimeInMs,
hotEventOverallTimeInMs: overallTimeInMs,
fastReassemble: fastReassemble,
hotEventCompileTimeInMs: compileTimeInMs,
hotEventFindInvalidatedTimeInMs: findInvalidatedTimeInMs,
hotEventScannedSourcesCount: scannedSourcesCount,
hotEventReassembleTimeInMs: reassembleTimeInMs,
hotEventReloadVMTimeInMs: reloadVMTimeInMs,
);
flutterUsage.sendEvent(category, parameter, parameters: parameters);
}
}
/// An event that reports the result of a [DoctorValidator]
class DoctorResultEvent extends UsageEvent {
DoctorResultEvent({
required this.validator,
required this.result,
Usage? flutterUsage,
}) : super(
'doctor-result',
'${validator.runtimeType}',
label: result.typeStr,
flutterUsage: flutterUsage ?? globals.flutterUsage,
);
final DoctorValidator validator;
final ValidationResult result;
@override
void send() {
if (validator is! GroupedValidator) {
flutterUsage.sendEvent(category, parameter, label: label);
return;
}
final GroupedValidator group = validator as GroupedValidator;
// The validator crashed.
if (group.subResults.isEmpty) {
flutterUsage.sendEvent(category, parameter, label: label);
return;
}
for (int i = 0; i < group.subValidators.length; i++) {
final DoctorValidator v = group.subValidators[i];
final ValidationResult r = group.subResults[i];
DoctorResultEvent(validator: v, result: r, flutterUsage: flutterUsage).send();
}
}
}
/// An event that reports on the result of a pub invocation.
class PubResultEvent extends UsageEvent {
PubResultEvent({
required String context,
required String result,
required Usage usage,
}) : super('pub-result', context, label: result, flutterUsage: usage);
}
/// An event that reports something about a build.
class BuildEvent extends UsageEvent {
BuildEvent(String label, {
String? command,
String? settings,
String? eventError,
required Usage flutterUsage,
required String type,
}) : _command = command,
_settings = settings,
_eventError = eventError,
super(
// category
'build',
// parameter
type,
label: label,
flutterUsage: flutterUsage,
);
final String? _command;
final String? _settings;
final String? _eventError;
@override
void send() {
final CustomDimensions parameters = CustomDimensions(
buildEventCommand: _command,
buildEventSettings: _settings,
buildEventError: _eventError,
);
flutterUsage.sendEvent(
category,
parameter,
label: label,
parameters: parameters,
);
}
}
/// An event that reports the result of a top-level command.
class CommandResultEvent extends UsageEvent {
CommandResultEvent(super.commandPath, super.result)
: assert(commandPath != null),
assert(result != null),
super(flutterUsage: globals.flutterUsage);
@override
void send() {
// An event for the command result.
flutterUsage.sendEvent(
'tool-command-result',
category,
label: parameter,
);
// A separate event for the memory highwater mark. This is a separate event
// so that we can get the command result even if trying to grab maxRss
// throws an exception.
try {
final int maxRss = globals.processInfo.maxRss;
flutterUsage.sendEvent(
'tool-command-max-rss',
category,
label: parameter,
value: maxRss,
);
} on Exception catch (error) {
// If grabbing the maxRss fails for some reason, just don't send an event.
globals.printTrace('Querying maxRss failed with error: $error');
}
}
}
/// An event that reports on changes in the configuration of analytics.
class AnalyticsConfigEvent extends UsageEvent {
AnalyticsConfigEvent({
/// Whether analytics reporting is being enabled (true) or disabled (false).
required bool enabled,
}) : super(
'analytics',
'enabled',
label: enabled ? 'true' : 'false',
flutterUsage: globals.flutterUsage,
);
}
/// An event that reports when the code size measurement is run via `--analyze-size`.
class CodeSizeEvent extends UsageEvent {
CodeSizeEvent(String platform, {
required Usage flutterUsage,
}) : super(
'code-size-analysis',
platform,
flutterUsage: flutterUsage
);
}
/// An event for tracking the usage of specific error handling fallbacks.
class ErrorHandlingEvent extends UsageEvent {
ErrorHandlingEvent(String parameter) : super('error-handling', parameter, flutterUsage: globals.flutterUsage);
}
/// Emit various null safety analytic events.
///
/// 1. The current null safety runtime mode.
/// 2. The number of packages that are migrated, along with the total number of packages
/// 3. The main packages language version.
class NullSafetyAnalysisEvent implements UsageEvent {
NullSafetyAnalysisEvent(
this.packageConfig,
this.nullSafetyMode,
this.currentPackage,
this.flutterUsage,
);
/// The category for analytics events related to null safety.
static const String kNullSafetyCategory = 'null-safety';
final PackageConfig packageConfig;
final NullSafetyMode nullSafetyMode;
final String currentPackage;
@override
final Usage flutterUsage;
@override
void send() {
if (packageConfig.packages.isEmpty) {
return;
}
int migrated = 0;
LanguageVersion? languageVersion;
for (final Package package in packageConfig.packages) {
final LanguageVersion? packageLanguageVersion = package.languageVersion;
if (package.name == currentPackage) {
languageVersion = packageLanguageVersion;
}
if (packageLanguageVersion != null &&
packageLanguageVersion.major >= nullSafeVersion.major &&
packageLanguageVersion.minor >= nullSafeVersion.minor) {
migrated += 1;
}
}
flutterUsage.sendEvent(kNullSafetyCategory, 'runtime-mode', label: nullSafetyMode.toString());
flutterUsage.sendEvent(kNullSafetyCategory, 'stats', parameters: CustomDimensions(
nullSafeMigratedLibraries: migrated,
nullSafeTotalLibraries: packageConfig.packages.length,
));
if (languageVersion != null) {
final String formattedVersion = '${languageVersion.major}.${languageVersion.minor}';
flutterUsage.sendEvent(kNullSafetyCategory, 'language-version', label: formattedVersion);
}
}
@override
String get category => kNullSafetyCategory;
@override
String get label => throw UnsupportedError('');
@override
String get parameter => throw UnsupportedError('');
@override
int get value => throw UnsupportedError('');
}