| // 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(''); |
| } |