| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:convert'; |
| |
| import 'enums.dart'; |
| |
| final class Event { |
| final DashEvent eventName; |
| late final Map<String, Object?> eventData; |
| |
| Event._({required this.eventName, required this.eventData}) { |
| if (eventData.containsKey('enabled_features')) { |
| throw ArgumentError.value( |
| eventData, |
| 'eventData', |
| 'The enabled_features key is reserved and cannot ' |
| "be used as part of an event's data.", |
| ); |
| } |
| } |
| |
| /// Event that is emitted whenever a user has opted in |
| /// or out of the analytics collection. |
| /// |
| /// * [status] - Boolean value where `true` indicates user is opting in. |
| Event.analyticsCollectionEnabled({required bool status}) |
| : this._( |
| eventName: DashEvent.analyticsCollectionEnabled, |
| eventData: {'status': status}); |
| |
| /// Event that is emitted when an error occurs within |
| /// `package:unified_analytics`, tools that are using this package |
| /// should not use this event constructor. |
| /// |
| /// Tools using this package should instead use the more generic |
| /// [Event.exception] constructor. |
| /// |
| /// * [workflow] - refers to what process caused the error, such as |
| /// "LogHandler.logFileStats". |
| /// |
| /// * [error] - the name of the error, such as "FormatException". |
| /// |
| /// * [description] - the description of the error being caught. |
| Event.analyticsException({ |
| required String workflow, |
| required String error, |
| String? description, |
| }) : this._( |
| eventName: DashEvent.analyticsException, |
| eventData: { |
| 'workflow': workflow, |
| 'error': error, |
| if (description != null) 'description': description, |
| }, |
| ); |
| |
| /// This is for various workflows within the flutter tool related |
| /// to iOS and macOS workflows. |
| /// |
| /// * [workflow] - which workflow is running, such as "assemble". |
| /// |
| /// * [parameter] - subcategory of the workflow, such as "ios-archive". |
| /// |
| /// * [result] - usually to indicate success or failure of the workflow. |
| Event.appleUsageEvent({ |
| required String workflow, |
| required String parameter, |
| String? result, |
| }) : this._( |
| eventName: DashEvent.appleUsageEvent, |
| eventData: { |
| 'workflow': workflow, |
| 'parameter': parameter, |
| if (result != null) 'result': result, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the performance of the |
| /// analysis server's handling of a specific kind of notification from the |
| /// client. |
| /// |
| /// * [duration] - JSON-encoded percentile values indicating how long it took |
| /// from the time the server started handling the notification until the |
| /// server had finished handling the notification. |
| /// |
| /// * [latency] - JSON-encoded percentile values indicating how long it took |
| /// from the time the notification was sent until the server started |
| /// handling it. |
| /// |
| /// * [method] - the name of the notification method that was sent. |
| Event.clientNotification({ |
| required String duration, |
| required String latency, |
| required String method, |
| }) : this._( |
| eventName: DashEvent.clientNotification, |
| eventData: { |
| 'duration': duration, |
| 'latency': latency, |
| 'method': method, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the performance of the |
| /// analyzer. |
| /// |
| /// * [workingDuration] - JSON-encoded percentile values indicating how long |
| /// the analysis status was "working". |
| /// * [withFineDependencies] - whether the fine-grained feature is enabled. |
| /// |
| /// Then there are three groups of measurements: |
| /// * file modifications |
| /// * workspace shape |
| /// * background analysis |
| /// |
| /// The file modifications group includes: |
| /// * [changedFileEventCount] - the number of file change events received. |
| /// * [removedFileEventCount] - the number of file removal events received. |
| /// * [changedFileUniqueCount] - the number of unique files that were changed. |
| /// * [removedFileUniqueCount] - the number of unique files that were removed. |
| /// |
| /// The workspace shape group includes: |
| /// * [immediateFileCountPercentiles] - JSON-encoded percentile values for the |
| /// number of files in the immediate workspace. |
| /// * [immediateFileLineCountPercentiles] - JSON-encoded percentile values for |
| /// the number of lines in the immediate workspace files. |
| /// * [transitiveFileCountPercentiles] - JSON-encoded percentile values for |
| /// the number of files in the transitive workspace. |
| /// * [transitiveFileLineCountPercentiles] - JSON-encoded percentile values |
| /// for the number of lines in the transitive workspace files. |
| /// |
| /// This allows us to understand how big is the workspace, and how it changed |
| /// over the reported period. |
| /// |
| /// The background analysis group includes: |
| /// * [produceErrorsPotentialFileCount] - the total number of files for which |
| /// the reported diagnostics could potentially change. |
| /// * [produceErrorsPotentialFileLineCount] - the total number of lines in |
| /// files for which the reported diagnostics could potentially change. |
| /// * [produceErrorsActualFileCount] - the total number of files that actually |
| /// were analyzed during background analysis. |
| /// * [produceErrorsActualFileLineCount] - the total number of lines in files |
| /// that actually were analyzed during background analysis. |
| /// * [produceErrorsDurationMs] - the total duration in milliseconds for |
| /// producing diagnostics. |
| /// * [produceErrorsElementsDurationMs] - the total duration in milliseconds |
| /// for preparing elements before analysis. |
| /// * [libraryDiagnosticsBundleFailures] - the counts of requirement failures |
| /// for library diagnostics bundles. The key is the `kindId` of the |
| /// `RequirementFailure`. |
| /// |
| /// This allows us to understand how many files were scheduled for analysis, |
| /// and how many of these files are served from the cache, because we |
| /// determined that the change that caused them to be scheduled for analysis |
| /// actually does not affect resolution and the set of diagnostics, e.g. |
| /// because the change was in a method body, or to an API that does not |
| /// affect this specific file (if fine-grained dependencies enabled). |
| Event.analysisStatistics({ |
| required String workingDuration, |
| required bool withFineDependencies, |
| required int changedFileEventCount, |
| required int removedFileEventCount, |
| required int changedFileUniqueCount, |
| required int removedFileUniqueCount, |
| required String immediateFileCountPercentiles, |
| required String immediateFileLineCountPercentiles, |
| required String transitiveFileCountPercentiles, |
| required String transitiveFileLineCountPercentiles, |
| required int produceErrorsPotentialFileCount, |
| required int produceErrorsPotentialFileLineCount, |
| required int produceErrorsActualFileCount, |
| required int produceErrorsActualFileLineCount, |
| required int produceErrorsDurationMs, |
| required int produceErrorsElementsDurationMs, |
| required String libraryDiagnosticsBundleFailures, |
| }) : this._( |
| eventName: DashEvent.analysisStatistics, |
| eventData: { |
| 'workingDuration': workingDuration, |
| 'withFineDependencies': withFineDependencies, |
| 'changedFileUniqueCount': changedFileUniqueCount, |
| 'removedFileUniqueCount': removedFileUniqueCount, |
| 'changedFileEventCount': changedFileEventCount, |
| 'removedFileEventCount': removedFileEventCount, |
| 'immediateFileCountPercentiles': immediateFileCountPercentiles, |
| 'immediateFileLineCountPercentiles': |
| immediateFileLineCountPercentiles, |
| 'transitiveFileCountPercentiles': transitiveFileCountPercentiles, |
| 'transitiveFileLineCountPercentiles': |
| transitiveFileLineCountPercentiles, |
| 'produceErrorsPotentialFileCount': produceErrorsPotentialFileCount, |
| 'produceErrorsPotentialFileLineCount': |
| produceErrorsPotentialFileLineCount, |
| 'produceErrorsActualFileCount': produceErrorsActualFileCount, |
| 'produceErrorsActualFileLineCount': |
| produceErrorsActualFileLineCount, |
| 'produceErrorsDurationMs': produceErrorsDurationMs, |
| 'produceErrorsElementsDurationMs': produceErrorsElementsDurationMs, |
| 'libraryDiagnosticsBundleFailures': |
| libraryDiagnosticsBundleFailures, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the performance of the |
| /// analysis server's handling of a specific kind of request from the client. |
| /// |
| /// * [duration] - JSON-encoded percentile values indicating how long it took |
| /// from the time the server started handling the request until the server |
| /// had send a response. |
| /// |
| /// * [latency] - JSON-encoded percentile values indicating how long it took |
| /// from the time the request was sent until the server started handling it. |
| /// |
| /// * [method] - the name of the request method that was sent. |
| /// |
| /// If the method is `workspace/didChangeWorkspaceFolders`, then the following |
| /// parameters should be included: |
| /// |
| /// * [added] - JSON-encoded percentile values indicating the number of |
| /// folders that were added. |
| /// |
| /// * [removed] - JSON-encoded percentile values indicating the number of |
| /// folders that were removed. |
| /// |
| /// If the method is `initialized`, then the following parameters should be |
| /// included: |
| /// |
| /// * [openWorkspacePaths] - JSON-encoded percentile values indicating the |
| /// number of workspace paths that were opened. |
| /// |
| /// If the method is `analysis.setAnalysisRoots`, then the following |
| /// parameters should be included: |
| /// |
| /// * [included] - JSON-encoded percentile values indicating the number of |
| /// analysis roots in the included list. |
| /// |
| /// * [excluded] - JSON-encoded percentile values indicating the number of |
| /// analysis roots in the excluded list. |
| /// |
| /// If the method is `analysis.setPriorityFiles`, then the following |
| /// parameters should be included: |
| /// |
| /// * [files] - JSON-encoded percentile values indicating the number of |
| /// priority files. |
| Event.clientRequest({ |
| required String duration, |
| required String latency, |
| required String method, |
| String? added, |
| String? excluded, |
| String? files, |
| String? included, |
| String? openWorkspacePaths, |
| String? removed, |
| }) : this._( |
| eventName: DashEvent.clientRequest, |
| eventData: { |
| if (added != null) 'added': added, |
| 'duration': duration, |
| if (excluded != null) 'excluded': excluded, |
| if (files != null) 'files': files, |
| if (included != null) 'included': included, |
| 'latency': latency, |
| 'method': method, |
| if (openWorkspacePaths != null) |
| 'openWorkspacePaths': openWorkspacePaths, |
| if (removed != null) 'removed': removed, |
| }, |
| ); |
| |
| /// An event that reports when the code size measurement is run |
| /// via `--analyze-size`. |
| /// |
| /// * [platform] - string identifier for which platform was run "ios", "apk", |
| /// "aab", etc. |
| Event.codeSizeAnalysis({required String platform}) |
| : this._( |
| eventName: DashEvent.codeSizeAnalysis, |
| eventData: { |
| 'platform': platform, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the number of times a given |
| /// command has been executed. |
| /// |
| /// * [count] - the number of times the command was executed. |
| /// |
| /// * [name] - the name of the command that was executed. |
| Event.commandExecuted({ |
| required int count, |
| required String name, |
| }) : this._( |
| eventName: DashEvent.commandExecuted, |
| eventData: { |
| 'count': count, |
| 'name': name, |
| }, |
| ); |
| |
| /// Event to capture usage values for different Flutter commands. |
| /// |
| /// There are several implementations of the `FlutterCommand` class within the |
| /// Flutter tool that pass information based on the [workflow] being ran. An |
| /// example of a [workflow] can be "create". The optional parameters for this |
| /// constructor are a superset of all the implementations of `FlutterCommand`. |
| /// There should never be a time where all of the parameters are passed to |
| /// this constructor. |
| Event.commandUsageValues({ |
| required String workflow, |
| required bool commandHasTerminal, |
| |
| // Assemble && build bundle implementation parameters |
| String? buildBundleTargetPlatform, |
| bool? buildBundleIsModule, |
| |
| // Build aar implementation parameters |
| String? buildAarProjectType, |
| String? buildAarTargetPlatform, |
| |
| // Build apk implementation parameters |
| String? buildApkTargetPlatform, |
| String? buildApkBuildMode, |
| bool? buildApkSplitPerAbi, |
| |
| // Build app bundle implementation parameters |
| String? buildAppBundleTargetPlatform, |
| String? buildAppBundleBuildMode, |
| |
| // Create implementation parameters |
| String? createProjectType, |
| String? createAndroidLanguage, |
| String? createIosLanguage, |
| |
| // Packages implementation parameters |
| int? packagesNumberPlugins, |
| bool? packagesProjectModule, |
| String? packagesAndroidEmbeddingVersion, |
| |
| // Run implementation parameters |
| bool? runIsEmulator, |
| String? runTargetName, |
| String? runTargetOsVersion, |
| String? runModeName, |
| bool? runProjectModule, |
| String? runProjectHostLanguage, |
| String? runAndroidEmbeddingVersion, |
| bool? runEnableImpeller, |
| String? runIOSInterfaceType, |
| bool? runIsTest, |
| }) : this._( |
| eventName: DashEvent.commandUsageValues, |
| eventData: { |
| 'workflow': workflow, |
| 'commandHasTerminal': commandHasTerminal, |
| if (buildBundleTargetPlatform != null) |
| 'buildBundleTargetPlatform': buildBundleTargetPlatform, |
| if (buildBundleIsModule != null) |
| 'buildBundleIsModule': buildBundleIsModule, |
| if (buildAarProjectType != null) |
| 'buildAarProjectType': buildAarProjectType, |
| if (buildAarTargetPlatform != null) |
| 'buildAarTargetPlatform': buildAarTargetPlatform, |
| if (buildApkTargetPlatform != null) |
| 'buildApkTargetPlatform': buildApkTargetPlatform, |
| if (buildApkBuildMode != null) |
| 'buildApkBuildMode': buildApkBuildMode, |
| if (buildApkSplitPerAbi != null) |
| 'buildApkSplitPerAbi': buildApkSplitPerAbi, |
| if (buildAppBundleTargetPlatform != null) |
| 'buildAppBundleTargetPlatform': buildAppBundleTargetPlatform, |
| if (buildAppBundleBuildMode != null) |
| 'buildAppBundleBuildMode': buildAppBundleBuildMode, |
| if (createProjectType != null) |
| 'createProjectType': createProjectType, |
| if (createAndroidLanguage != null) |
| 'createAndroidLanguage': createAndroidLanguage, |
| if (createIosLanguage != null) |
| 'createIosLanguage': createIosLanguage, |
| if (packagesNumberPlugins != null) |
| 'packagesNumberPlugins': packagesNumberPlugins, |
| if (packagesProjectModule != null) |
| 'packagesProjectModule': packagesProjectModule, |
| if (packagesAndroidEmbeddingVersion != null) |
| 'packagesAndroidEmbeddingVersion': |
| packagesAndroidEmbeddingVersion, |
| if (runIsEmulator != null) 'runIsEmulator': runIsEmulator, |
| if (runTargetName != null) 'runTargetName': runTargetName, |
| if (runTargetOsVersion != null) |
| 'runTargetOsVersion': runTargetOsVersion, |
| if (runModeName != null) 'runModeName': runModeName, |
| if (runProjectModule != null) 'runProjectModule': runProjectModule, |
| if (runProjectHostLanguage != null) |
| 'runProjectHostLanguage': runProjectHostLanguage, |
| if (runAndroidEmbeddingVersion != null) |
| 'runAndroidEmbeddingVersion': runAndroidEmbeddingVersion, |
| if (runEnableImpeller != null) |
| 'runEnableImpeller': runEnableImpeller, |
| if (runIOSInterfaceType != null) |
| 'runIOSInterfaceType': runIOSInterfaceType, |
| if (runIsTest != null) 'runIsTest': runIsTest, |
| }, |
| ); |
| |
| /// Event that is emitted on shutdown to report the structure of the analysis |
| /// contexts created immediately after startup. |
| /// |
| /// * [immediateFileCount] - the number of files in one of the analysis |
| /// contexts. |
| /// |
| /// * [immediateFileLineCount] - the number of lines in the immediate files. |
| /// |
| /// * [numberOfContexts] - the number of analysis context created. |
| /// |
| /// * [transitiveFileCount] - the number of files reachable from the files in |
| /// each analysis context, where files can be counted multiple times if they |
| /// are reachable from multiple contexts. |
| /// |
| /// * [transitiveFileLineCount] - the number of lines in the transitive files, |
| /// where files can be counted multiple times if they are reachable from |
| /// multiple contexts. |
| /// |
| /// * [transitiveFileUniqueCount] - the number of unique files reachable from |
| /// the files in each analysis context. |
| /// |
| /// * [transitiveFileUniqueLineCount] - the number of lines in the unique |
| /// transitive files. |
| /// |
| /// * [libraryCycleLibraryCounts] - JSON-encoded percentile values indicating |
| /// the number of libraries in a single library cycle. |
| /// |
| /// * [libraryCycleLineCounts] - JSON-encoded percentile values indicating the |
| /// number of lines of code in all of the files in a single library cycle. |
| /// |
| /// * [contextWorkspaceType] - JSON-encoded list with the total number of |
| /// workspaces of each type for all of the contexts: |
| /// * index 0: Blaze, GN or other workspace count |
| /// * index 1: Package workspace count |
| /// * index 2: Pub workspace count |
| /// |
| /// * [numberOfPackagesInWorkspace] - JSON-encoded percentile values for the |
| /// number of packages in the Pub workspaces. |
| Event.contextStructure({ |
| required int immediateFileCount, |
| required int immediateFileLineCount, |
| required int numberOfContexts, |
| required int transitiveFileCount, |
| required int transitiveFileLineCount, |
| required int transitiveFileUniqueCount, |
| required int transitiveFileUniqueLineCount, |
| String libraryCycleLibraryCounts = '', |
| String libraryCycleLineCounts = '', |
| String contextWorkspaceType = '', |
| String numberOfPackagesInWorkspace = '', |
| }) : this._( |
| eventName: DashEvent.contextStructure, |
| eventData: { |
| 'immediateFileCount': immediateFileCount, |
| 'immediateFileLineCount': immediateFileLineCount, |
| 'numberOfContexts': numberOfContexts, |
| 'transitiveFileCount': transitiveFileCount, |
| 'transitiveFileLineCount': transitiveFileLineCount, |
| 'transitiveFileUniqueCount': transitiveFileUniqueCount, |
| 'transitiveFileUniqueLineCount': transitiveFileUniqueLineCount, |
| 'libraryCycleLibraryCounts': libraryCycleLibraryCounts, |
| 'libraryCycleLineCounts': libraryCycleLineCounts, |
| 'contextWorkspaceType': contextWorkspaceType, |
| 'numberOfPackagesInWorkspace': numberOfPackagesInWorkspace, |
| }, |
| ); |
| |
| /// Event that is emitted when a Dart CLI command has been executed. |
| /// |
| /// * [name] - the name of the command that was executed. |
| /// |
| /// * [enabledExperiments] - a set of Dart language experiments enabled when |
| /// running the command. |
| /// |
| /// * [exitCode] - the process exit code set as a result of running the |
| /// command. |
| Event.dartCliCommandExecuted({ |
| required String name, |
| required String enabledExperiments, |
| int? exitCode, |
| }) : this._( |
| eventName: DashEvent.dartCliCommandExecuted, |
| eventData: { |
| 'name': name, |
| 'enabledExperiments': enabledExperiments, |
| if (exitCode != null) 'exitCode': exitCode, |
| }, |
| ); |
| |
| /// Event that is sent from DevTools for various different actions as |
| /// indicated by the [eventCategory]. |
| /// |
| /// The optional parameters in the parameter list contain metadata that is |
| /// sent with each event, when available. |
| /// |
| /// [additionalMetrics] may contain any additional data for the event being |
| /// sent. This often looks like metrics that are unique to the event or to a |
| /// specific screen. |
| Event.devtoolsEvent({ |
| required String screen, |
| required String eventCategory, |
| required String label, |
| required int value, |
| |
| // Defaulted values |
| bool userInitiatedInteraction = true, |
| |
| // Optional parameters |
| String? g3Username, |
| String? userApp, |
| String? userBuild, |
| String? userPlatform, |
| String? devtoolsPlatform, |
| String? devtoolsChrome, |
| String? devtoolsVersion, |
| String? ideLaunched, |
| String? isExternalBuild, |
| String? isEmbedded, |
| String? ideLaunchedFeature, |
| String? isWasm, |
| CustomMetrics? additionalMetrics, |
| }) : this._( |
| eventName: DashEvent.devtoolsEvent, |
| eventData: { |
| 'screen': screen, |
| 'eventCategory': eventCategory, |
| 'label': label, |
| 'value': value, |
| |
| 'userInitiatedInteraction': userInitiatedInteraction, |
| |
| // Optional parameters |
| if (g3Username != null) 'g3Username': g3Username, |
| if (userApp != null) 'userApp': userApp, |
| if (userBuild != null) 'userBuild': userBuild, |
| if (userPlatform != null) 'userPlatform': userPlatform, |
| if (devtoolsPlatform != null) 'devtoolsPlatform': devtoolsPlatform, |
| if (devtoolsChrome != null) 'devtoolsChrome': devtoolsChrome, |
| if (devtoolsVersion != null) 'devtoolsVersion': devtoolsVersion, |
| if (ideLaunched != null) 'ideLaunched': ideLaunched, |
| if (isExternalBuild != null) 'isExternalBuild': isExternalBuild, |
| if (isEmbedded != null) 'isEmbedded': isEmbedded, |
| if (ideLaunchedFeature != null) |
| 'ideLaunchedFeature': ideLaunchedFeature, |
| if (isWasm != null) 'isWasm': isWasm, |
| if (additionalMetrics != null) ...additionalMetrics.toMap(), |
| }, |
| ); |
| |
| /// Event that contains the results for a specific doctor validator. |
| /// |
| /// * [validatorName] - the name for the doctor validator. |
| /// |
| /// * [result] - the final result for a specific doctor validator. |
| /// |
| /// * [partOfGroupedValidator] - `true` indicates that this validator belongs |
| /// to a grouped validator. |
| /// |
| /// * [doctorInvocationId] - epoch formatted timestamp that can be used in |
| /// combination with the client ID in GA4 to group the validators that |
| /// ran in one doctor invocation. |
| /// |
| /// * [statusInfo] - optional description of the result from the doctor |
| /// validator. |
| Event.doctorValidatorResult({ |
| required String validatorName, |
| required String result, |
| required bool partOfGroupedValidator, |
| required int doctorInvocationId, |
| String? statusInfo, |
| }) : this._( |
| eventName: DashEvent.doctorValidatorResult, |
| eventData: { |
| 'validatorName': validatorName, |
| 'result': result, |
| 'partOfGroupedValidator': partOfGroupedValidator, |
| 'doctorInvocationId': doctorInvocationId, |
| if (statusInfo != null) 'statusInfo': statusInfo, |
| }, |
| ); |
| |
| /// Generic event for all dash tools to use when encountering an |
| /// exception that we want to log. |
| /// |
| /// * [exception] - string representation of the exception that occured. |
| /// * [data] - optional structured data to include with the exception event. |
| Event.exception({ |
| required String exception, |
| Map<String, Object?> data = const <String, Object?>{}, |
| }) : this._( |
| eventName: DashEvent.exception, |
| eventData: { |
| 'exception': exception, |
| ...Map.from(data)..removeWhere((key, value) => value == null), |
| }, |
| ); |
| |
| /// Event that is emitted from the flutter tool when a build invocation |
| /// has been run by the user. |
| /// |
| /// * [label] - the identifier for that build event. |
| /// |
| /// * [buildType] - the identifier for which platform the build event was for, |
| /// examples include "ios", "gradle", and "web". |
| /// |
| /// * [command] - the command that was ran to kick off the build event. |
| /// |
| /// * [settings] - the settings used for the build event related to |
| /// configuration and other relevant build information. |
| /// |
| /// * [error] - short identifier used to explain the cause of the build error, |
| /// stacktraces should not be passed to this parameter. |
| Event.flutterBuildInfo({ |
| required String label, |
| required String buildType, |
| String? command, |
| String? settings, |
| String? error, |
| }) : this._( |
| eventName: DashEvent.flutterBuildInfo, |
| eventData: { |
| 'label': label, |
| 'buildType': buildType, |
| if (command != null) 'command': command, |
| if (settings != null) 'settings': settings, |
| if (error != null) 'error': error, |
| }, |
| ); |
| |
| /// Provides information about which flutter command was run |
| /// and whether it was successful. |
| /// |
| /// * [commandPath] - information about the flutter command, such as |
| /// "build/apk". |
| /// |
| /// * [result] - if the command failed or succeeded. |
| /// |
| /// * [commandHasTerminal] - Boolean indicating if the flutter command ran |
| /// with a terminal. |
| /// |
| /// * [maxRss] - maximum resident size for a given flutter command. |
| Event.flutterCommandResult({ |
| required String commandPath, |
| required String result, |
| required bool commandHasTerminal, |
| int? maxRss, |
| }) : this._( |
| eventName: DashEvent.flutterCommandResult, |
| eventData: { |
| 'commandPath': commandPath, |
| 'result': result, |
| 'commandHasTerminal': commandHasTerminal, |
| if (maxRss != null) 'maxRss': maxRss, |
| }, |
| ); |
| |
| Event.flutterWasmDryRun({ |
| required String result, |
| required int exitCode, |
| String? findingsSummary, |
| }) : this._( |
| eventName: DashEvent.flutterWasmDryRun, |
| eventData: { |
| 'result': result, |
| 'exitCode': exitCode, |
| if (findingsSummary != null) 'findings': findingsSummary, |
| }, |
| ); |
| |
| /// Provides information about the results of a WASM dry run including public |
| /// package names and versions. |
| /// |
| /// * [result] - dry run result summary. |
| /// |
| /// * [exitCode] - the exit code of the dry run. |
| /// |
| /// * [findingsInfo] - findings for the dry run, keyed by finding index. |
| /// The value is a comma-separated string containing flags and package |
| /// information in `name:version` format, e.g., `'-ph,pkg1:1.2.3'`. |
| Event.flutterWasmDryRunPackage({ |
| required String result, |
| required int exitCode, |
| required Map<String, String> findingsInfo, |
| }) : this._( |
| eventName: DashEvent.flutterWasmDryRunPackage, |
| eventData: { |
| 'result': result, |
| 'exitCode': exitCode, |
| ...findingsInfo, |
| }, |
| ); |
| |
| /// Provides information about the plugins injected into an iOS or macOS |
| /// project. |
| /// |
| /// This event is not sent if a project has no plugins. |
| /// |
| /// * [platform] - The project's platform. Either 'ios' or 'macos'. |
| /// |
| /// * [isModule] - whether the project is an add-to-app Flutter module. |
| /// |
| /// * [swiftPackageManagerUsable] - if `true`, Swift Package Manager can be |
| /// used for the project's plugins if any are Swift Package Manager |
| /// compatible. |
| /// |
| /// * [swiftPackageManagerFeatureEnabled] - if the Swift Package Manager |
| /// feature flag is on. If false, Swift Package Manager is off for all |
| /// projects on the development machine. |
| /// |
| /// * [projectDisabledSwiftPackageManager] - if the project's .pubspec has |
| /// `disable-swift-package-manager: true`. This turns off Swift Package |
| /// Manager for a single project. |
| /// |
| /// * [projectHasSwiftPackageManagerIntegration] - if the Xcode project has |
| /// Swift Package Manager integration. If `false`, the project needs to be |
| /// migrated. |
| /// |
| /// * [pluginCount] - the total number of plugins for this project. A plugin |
| /// can be compatible with both Swift Package Manager and CocoaPods. Plugins |
| /// compatible with both will be counted in both [swiftPackageCount] and |
| /// [podCount]. Swift Package Manager was used to inject all plugins if |
| /// [pluginCount] is equal to [swiftPackageCount]. |
| /// |
| /// * [swiftPackageCount] - the number of plugins compatible with Swift |
| /// Package Manager. This is less than or equal to [pluginCount]. If |
| /// [swiftPackageCount] is less than [pluginCount], the project uses |
| /// CocoaPods to inject plugins. |
| /// |
| /// * [podCount] - the number of plugins compatible with CocoaPods. This is |
| /// less than or equal to [pluginCount]. |
| Event.flutterInjectDarwinPlugins({ |
| required String platform, |
| required bool isModule, |
| required bool swiftPackageManagerUsable, |
| required bool swiftPackageManagerFeatureEnabled, |
| required bool projectDisabledSwiftPackageManager, |
| required bool projectHasSwiftPackageManagerIntegration, |
| required int pluginCount, |
| required int swiftPackageCount, |
| required int podCount, |
| }) : this._( |
| eventName: DashEvent.flutterInjectDarwinPlugins, |
| eventData: { |
| 'platform': platform, |
| 'isModule': isModule, |
| 'swiftPackageManagerUsable': swiftPackageManagerUsable, |
| 'swiftPackageManagerFeatureEnabled': |
| swiftPackageManagerFeatureEnabled, |
| 'projectDisabledSwiftPackageManager': |
| projectDisabledSwiftPackageManager, |
| 'projectHasSwiftPackageManagerIntegration': |
| projectHasSwiftPackageManagerIntegration, |
| 'pluginCount': pluginCount, |
| 'swiftPackageCount': swiftPackageCount, |
| 'podCount': podCount, |
| }, |
| ); |
| |
| // TODO: eliasyishak, remove this or replace once we have a generic |
| // timing event that can be used by potentially more than one DashTool. |
| Event.hotReloadTime({required int timeMs}) |
| : this._( |
| eventName: DashEvent.hotReloadTime, |
| eventData: {'timeMs': timeMs}, |
| ); |
| |
| /// Events to be sent for the Flutter Hot Runner. |
| Event.hotRunnerInfo({ |
| required String label, |
| required String targetPlatform, |
| required String sdkName, |
| required bool emulator, |
| required bool fullRestart, |
| String? reason, |
| int? finalLibraryCount, |
| int? syncedLibraryCount, |
| int? syncedClassesCount, |
| int? syncedProceduresCount, |
| int? syncedBytes, |
| int? invalidatedSourcesCount, |
| int? transferTimeInMs, |
| int? overallTimeInMs, |
| int? compileTimeInMs, |
| int? findInvalidatedTimeInMs, |
| int? scannedSourcesCount, |
| int? reassembleTimeInMs, |
| int? reloadVMTimeInMs, |
| }) : this._( |
| eventName: DashEvent.hotRunnerInfo, |
| eventData: { |
| 'label': label, |
| 'targetPlatform': targetPlatform, |
| 'sdkName': sdkName, |
| 'emulator': emulator, |
| 'fullRestart': fullRestart, |
| if (reason != null) 'reason': reason, |
| if (finalLibraryCount != null) |
| 'finalLibraryCount': finalLibraryCount, |
| if (syncedLibraryCount != null) |
| 'syncedLibraryCount': syncedLibraryCount, |
| if (syncedClassesCount != null) |
| 'syncedClassesCount': syncedClassesCount, |
| if (syncedProceduresCount != null) |
| 'syncedProceduresCount': syncedProceduresCount, |
| if (syncedBytes != null) 'syncedBytes': syncedBytes, |
| if (invalidatedSourcesCount != null) |
| 'invalidatedSourcesCount': invalidatedSourcesCount, |
| if (transferTimeInMs != null) 'transferTimeInMs': transferTimeInMs, |
| if (overallTimeInMs != null) 'overallTimeInMs': overallTimeInMs, |
| if (compileTimeInMs != null) 'compileTimeInMs': compileTimeInMs, |
| if (findInvalidatedTimeInMs != null) |
| 'findInvalidatedTimeInMs': findInvalidatedTimeInMs, |
| if (scannedSourcesCount != null) |
| 'scannedSourcesCount': scannedSourcesCount, |
| if (reassembleTimeInMs != null) |
| 'reassembleTimeInMs': reassembleTimeInMs, |
| if (reloadVMTimeInMs != null) 'reloadVMTimeInMs': reloadVMTimeInMs, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the number of times each lint |
| /// has been enabled. |
| /// |
| /// * [count] - the number of options files in which the lint was enabled. |
| /// |
| /// * [name] - the name of the lint. |
| Event.lintUsageCount({ |
| required int count, |
| required String name, |
| }) : this._( |
| eventName: DashEvent.lintUsageCount, |
| eventData: { |
| 'count': count, |
| 'name': name, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the amount of memory being |
| /// used. |
| /// |
| /// * [rss] - the resident set size in megabytes. |
| /// |
| /// If this is not the first time memory has been reported for this session, |
| /// then the following parameters should be included: |
| /// |
| /// * [periodSec] - the number of seconds since the last memory usage data was |
| /// gathered. |
| /// |
| /// * [mbPerSec] - the number of megabytes of memory that were added or |
| /// subtracted per second since the last report. |
| Event.memoryInfo({ |
| required int rss, |
| int? periodSec, |
| double? mbPerSec, |
| }) : this._( |
| eventName: DashEvent.memoryInfo, |
| eventData: { |
| 'rss': rss, |
| if (periodSec != null) 'periodSec': periodSec, |
| if (mbPerSec != null) 'mbPerSec': mbPerSec |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the performance of analyzer |
| /// plugins when handling requests. |
| /// |
| /// * [duration] - JSON-encoded percentile values indicating how long it took |
| /// from the time the request was sent to the plugin until the response |
| /// was processed by the server. |
| /// |
| /// * [method] - the name of the request sent to the plugin. |
| /// |
| /// * [pluginId] - the ID of the plugin whose performance is being reported. |
| Event.pluginRequest({ |
| required String duration, |
| required String method, |
| required String pluginId, |
| }) : this._( |
| eventName: DashEvent.pluginRequest, |
| eventData: { |
| 'duration': duration, |
| 'method': method, |
| 'pluginId': pluginId, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report information about how _new_ |
| /// analyzer plugins are used. |
| /// |
| /// - [count] - the number of new plugins that were configured. |
| /// - [lintRuleCounts] - JSON-encoded percentile values indicating the number |
| /// of enabled lint rules for each plugin. |
| /// - [warningRuleCounts] - JSON-encoded percentile values indicating the |
| /// number of enabled warning rules for each plugin. |
| /// - [fixCounts] - JSON-encoded percentile values indicating the number of |
| /// fixes provided by each plugin. |
| /// - [assistCounts] - JSON-encoded percentile values indicating the number of |
| /// assists provided by each plugin. |
| Event.plugins({ |
| required int count, |
| required String lintRuleCounts, |
| required String warningRuleCounts, |
| required String fixCounts, |
| required String assistCounts, |
| }) : this._( |
| eventName: DashEvent.plugins, |
| eventData: { |
| 'count': count, |
| 'lintRuleCounts': lintRuleCounts, |
| 'warningRuleCounts': warningRuleCounts, |
| 'fixCounts': fixCounts, |
| 'assistCounts': assistCounts, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the frequency with which a |
| /// given _legacy_ analyzer plugin has been used. |
| /// |
| /// * [count] - the number of times plugins usage was changed, which will |
| /// always be at least one. |
| /// |
| /// * [enabled] - JSON-encoded percentile values indicating the number of |
| /// contexts for which the plugin was enabled. |
| /// |
| /// * [pluginId] - the ID of the plugin associated with the data. |
| Event.pluginUse({ |
| required int count, |
| required String enabled, |
| required String pluginId, |
| }) : this._( |
| eventName: DashEvent.pluginUse, |
| eventData: { |
| 'count': count, |
| 'enabled': enabled, |
| 'pluginId': pluginId, |
| }, |
| ); |
| |
| /// Event that is emitted when `pub get` is run. |
| /// |
| /// * [packageName] - the name of the package that was resolved. |
| /// |
| /// * [version] - the resolved, canonicalized package version. |
| /// |
| /// * [dependencyType] - the kind of dependency that resulted in this package |
| /// being resolved (e.g., direct, transitive, or dev dependencies). |
| Event.pubGet({ |
| required String packageName, |
| required String version, |
| required String dependencyType, |
| }) : this._( |
| eventName: DashEvent.pubGet, |
| eventData: { |
| 'packageName': packageName, |
| 'version': version, |
| 'dependencyType': dependencyType, |
| }, |
| ); |
| |
| /// Event that is emitted on shutdown to report information about the whole |
| /// session for which the analysis server was running. |
| /// |
| /// * [clientId] - the id of the client that started the server. |
| /// |
| /// * [clientVersion] - the version of the client that started the server. |
| /// |
| /// * [duration] - the number of milliseconds for which the server was |
| /// running. |
| /// |
| /// * [flags] - the flags passed to the analysis server on startup, without |
| /// any argument values for flags that take values, or an empty string if |
| /// there were no arguments. |
| /// |
| /// * [parameters] - the names of the parameters passed to the `initialize` |
| /// request, or an empty string if the `initialize` request was not sent |
| /// or if there were no parameters given. |
| Event.serverSession({ |
| required String clientId, |
| required String clientVersion, |
| required int duration, |
| required String flags, |
| required String parameters, |
| }) : this._( |
| eventName: DashEvent.serverSession, |
| eventData: { |
| 'clientId': clientId, |
| 'clientVersion': clientVersion, |
| 'duration': duration, |
| 'flags': flags, |
| 'parameters': parameters, |
| }, |
| ); |
| |
| /// Event that is emitted periodically to report the number of times the |
| /// severity of a diagnostic was changed in the analysis options file. |
| /// |
| /// * [diagnostic] - the name of the diagnostic whose severity was changed. |
| /// |
| /// * [adjustments] - JSON-encoded map of severities to the number of times |
| /// the diagnostic's severity was changed to the key. |
| Event.severityAdjustment({ |
| required String diagnostic, |
| required String adjustments, |
| }) : this._( |
| eventName: DashEvent.severityAdjustment, |
| eventData: { |
| 'diagnostic': diagnostic, |
| 'adjustments': adjustments, |
| }, |
| ); |
| |
| /// Event that is emitted by `package:unified_analytics` when |
| /// the user takes action when prompted with a survey. |
| /// |
| /// * [surveyId] - the unique id for a given survey. |
| /// |
| /// * [status] - the string identifier for a given `SurveyButton` under the |
| /// `action` field. |
| Event.surveyAction({ |
| required String surveyId, |
| required String status, |
| }) : this._( |
| eventName: DashEvent.surveyAction, |
| eventData: { |
| 'surveyId': surveyId, |
| 'status': status, |
| }, |
| ); |
| |
| /// Event that is emitted by `package:unified_analytics` when the user has |
| /// been shown a survey. |
| /// |
| /// * [surveyId] - the unique ID for a given survey. |
| Event.surveyShown({ |
| required String surveyId, |
| }) : this._( |
| eventName: DashEvent.surveyShown, |
| eventData: { |
| 'surveyId': surveyId, |
| }, |
| ); |
| |
| /// Event that records how long a given process takes to complete. |
| /// |
| /// * [workflow] - the overall process or command being run, for example |
| /// "build" is a possible value for the flutter tool. |
| /// |
| /// * [variableName] - the specific variable being measured, for example |
| /// "gradle" would indicate how long it took for a gradle build under the |
| /// "build" [workflow]. |
| /// |
| /// * [elapsedMilliseconds] - how long the process took in milliseconds. |
| /// |
| /// * [label] - an optional field that can be used for further filtering, for |
| /// example, "success" can indicate how long a successful build in gradle |
| /// takes to complete. |
| Event.timing({ |
| required String workflow, |
| required String variableName, |
| required int elapsedMilliseconds, |
| String? label, |
| }) : this._( |
| eventName: DashEvent.timing, |
| eventData: { |
| 'workflow': workflow, |
| 'variableName': variableName, |
| 'elapsedMilliseconds': elapsedMilliseconds, |
| if (label != null) 'label': label, |
| }, |
| ); |
| |
| /// An event that is sent from the Dart MCP server. |
| /// |
| /// The [client] is the name of the client, as given when it connected to the |
| /// MCP server, and [clientVersion] is the version of the client. |
| /// |
| /// The [serverVersion] is the version of the Dart MCP server. |
| /// |
| /// The [type] identifies the kind of event this is, and [additionalData] is |
| /// the actual data for the event. |
| Event.dartMCPEvent({ |
| required String client, |
| required String clientVersion, |
| required String serverVersion, |
| required String type, |
| CustomMetrics? additionalData, |
| }) : this._( |
| eventName: DashEvent.dartMCPEvent, |
| eventData: { |
| 'client': client, |
| 'clientVersion': clientVersion, |
| 'serverVersion': serverVersion, |
| 'type': type, |
| ...?additionalData?.toMap(), |
| }, |
| ); |
| |
| @override |
| int get hashCode => Object.hash(eventName, jsonEncode(eventData)); |
| |
| @override |
| bool operator ==(Object other) => |
| other is Event && |
| other.runtimeType == runtimeType && |
| other.eventName == eventName && |
| _compareEventData(other.eventData, eventData); |
| |
| /// Converts an instance of [Event] to JSON. |
| String toJson() => jsonEncode({ |
| 'eventName': eventName.label, |
| 'eventData': eventData, |
| }); |
| |
| @override |
| String toString() => toJson(); |
| |
| /// Utility function to take in two maps [a] and [b] and compares them |
| /// to ensure that they have the same keys and values |
| bool _compareEventData(Map<String, Object?> a, Map<String, Object?> b) { |
| final keySetA = a.keys.toSet(); |
| final keySetB = b.keys.toSet(); |
| final intersection = keySetA.intersection(keySetB); |
| |
| // Ensure that the keys are the same for each object. |
| if (intersection.length != keySetA.length || |
| intersection.length != keySetB.length) { |
| return false; |
| } |
| |
| // Ensure that each of the key's values are the same. |
| for (final key in a.keys) { |
| if (a[key] != b[key]) return false; |
| } |
| |
| return true; |
| } |
| |
| /// Returns a valid instance of [Event] if [json] follows the correct schema. |
| /// |
| /// Common use case for this static method involves clients of this package |
| /// that have a client-server setup where the server sends events that the |
| /// client creates. |
| static Event? fromJson(String json) { |
| try { |
| final jsonMap = jsonDecode(json) as Map<String, Object?>; |
| |
| // Ensure that `eventName` is a string and a valid label and `eventData` |
| // is a nested object. |
| if (jsonMap |
| case { |
| 'eventName': final String eventName, |
| 'eventData': final Map<String, Object?> eventData, |
| }) { |
| final dashEvent = DashEvent.fromLabel(eventName); |
| if (dashEvent == null) return null; |
| |
| return Event._( |
| eventName: dashEvent, |
| eventData: eventData, |
| ); |
| } |
| |
| return null; |
| } on FormatException { |
| return null; |
| } |
| } |
| } |
| |
| /// A base class for custom metrics that will be defined by a unified_analytics |
| /// client. |
| /// |
| /// This base type can be used as a parameter in any event constructor that |
| /// allows custom metrics to be added by a unified_analytics client. |
| abstract base class CustomMetrics { |
| /// Converts the custom metrics data to a [Map] object. |
| /// |
| /// This must be a JSON-encodable [Map]. |
| Map<String, Object> toMap(); |
| } |