events.dart 9.49 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11
// 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 {
12 13
  UsageEvent(this.category, this.parameter, {
    this.label,
14
    this.value,
15
    required this.flutterUsage,
16
  });
17 18 19

  final String category;
  final String parameter;
20 21
  final String? label;
  final int? value;
22
  final Usage flutterUsage;
23 24

  void send() {
25
    flutterUsage.sendEvent(category, parameter, label: label, value: value);
26 27 28 29 30 31 32 33 34 35 36 37
  }
}

/// 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, {
38 39 40 41 42
    required this.targetPlatform,
    required this.sdkName,
    required this.emulator,
    required this.fullRestart,
    required this.fastReassemble,
43 44 45 46 47 48 49 50 51
    this.reason,
    this.finalLibraryCount,
    this.syncedLibraryCount,
    this.syncedClassesCount,
    this.syncedProceduresCount,
    this.syncedBytes,
    this.invalidatedSourcesCount,
    this.transferTimeInMs,
    this.overallTimeInMs,
52 53 54 55 56
    this.compileTimeInMs,
    this.findInvalidatedTimeInMs,
    this.scannedSourcesCount,
    this.reassembleTimeInMs,
    this.reloadVMTimeInMs,
57
  }) : super('hot', parameter, flutterUsage: globals.flutterUsage);
58

59
  final String? reason;
60 61 62 63
  final String targetPlatform;
  final String sdkName;
  final bool emulator;
  final bool fullRestart;
64
  final bool fastReassemble;
65 66 67 68 69 70 71 72
  final int? finalLibraryCount;
  final int? syncedLibraryCount;
  final int? syncedClassesCount;
  final int? syncedProceduresCount;
  final int? syncedBytes;
  final int? invalidatedSourcesCount;
  final int? transferTimeInMs;
  final int? overallTimeInMs;
73 74 75 76 77
  final int? compileTimeInMs;
  final int? findInvalidatedTimeInMs;
  final int? scannedSourcesCount;
  final int? reassembleTimeInMs;
  final int? reloadVMTimeInMs;
78 79 80

  @override
  void send() {
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    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,
96 97 98 99 100
      hotEventCompileTimeInMs: compileTimeInMs,
      hotEventFindInvalidatedTimeInMs: findInvalidatedTimeInMs,
      hotEventScannedSourcesCount: scannedSourcesCount,
      hotEventReassembleTimeInMs: reassembleTimeInMs,
      hotEventReloadVMTimeInMs: reloadVMTimeInMs,
101
    );
102 103 104 105 106 107 108
    flutterUsage.sendEvent(category, parameter, parameters: parameters);
  }
}

/// An event that reports the result of a [DoctorValidator]
class DoctorResultEvent extends UsageEvent {
  DoctorResultEvent({
109 110 111
    required this.validator,
    required this.result,
    Usage? flutterUsage,
112 113 114 115
  }) : super(
    'doctor-result',
    '${validator.runtimeType}',
    label: result.typeStr,
116
    flutterUsage: flutterUsage ?? globals.flutterUsage,
117
  );
118 119 120 121 122 123 124

  final DoctorValidator validator;
  final ValidationResult result;

  @override
  void send() {
    if (validator is! GroupedValidator) {
125
      flutterUsage.sendEvent(category, parameter, label: label);
126 127
      return;
    }
128
    final GroupedValidator group = validator as GroupedValidator;
129
    // The validator crashed.
130
    if (group.subResults.isEmpty) {
131 132 133
      flutterUsage.sendEvent(category, parameter, label: label);
      return;
    }
134 135 136
    for (int i = 0; i < group.subValidators.length; i++) {
      final DoctorValidator v = group.subValidators[i];
      final ValidationResult r = group.subResults[i];
137
      DoctorResultEvent(validator: v, result: r, flutterUsage: flutterUsage).send();
138 139
    }
  }
140 141
}

142
/// An event that reports on the result of a pub invocation.
143 144
class PubResultEvent extends UsageEvent {
  PubResultEvent({
145 146 147
    required String context,
    required String result,
    required Usage usage,
148
  }) : super('pub-result', context, label: result, flutterUsage: usage);
149 150 151 152
}

/// An event that reports something about a build.
class BuildEvent extends UsageEvent {
153
  BuildEvent(String label, {
154 155 156 157 158
    String? command,
    String? settings,
    String? eventError,
    required Usage flutterUsage,
    required String type,
159 160 161 162
  }) : _command = command,
  _settings = settings,
  _eventError = eventError,
      super(
163 164 165
    // category
    'build',
    // parameter
166
    type,
167
    label: label,
168
    flutterUsage: flutterUsage,
169
  );
170

171 172 173
  final String? _command;
  final String? _settings;
  final String? _eventError;
174 175 176

  @override
  void send() {
177 178 179 180 181
    final CustomDimensions parameters = CustomDimensions(
      buildEventCommand: _command,
      buildEventSettings: _settings,
      buildEventError: _eventError,
    );
182 183 184 185 186 187
    flutterUsage.sendEvent(
      category,
      parameter,
      label: label,
      parameters: parameters,
    );
188 189 190 191 192
  }
}

/// An event that reports the result of a top-level command.
class CommandResultEvent extends UsageEvent {
193
  CommandResultEvent(super.commandPath, super.result)
194 195
      : assert(commandPath != null),
        assert(result != null),
196
        super(flutterUsage: globals.flutterUsage);
197 198 199

  @override
  void send() {
200 201 202 203 204 205 206 207 208 209
    // 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.
210
    try {
211
      final int maxRss = globals.processInfo.maxRss;
212 213 214 215 216 217
      flutterUsage.sendEvent(
        'tool-command-max-rss',
        category,
        label: parameter,
        value: maxRss,
      );
218
    } on Exception catch (error) {
219
      // If grabbing the maxRss fails for some reason, just don't send an event.
220
      globals.printTrace('Querying maxRss failed with error: $error');
221 222
    }
  }
223
}
224 225 226 227 228

/// 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).
229
    required bool enabled,
230 231 232 233
  }) : super(
    'analytics',
    'enabled',
    label: enabled ? 'true' : 'false',
234
    flutterUsage: globals.flutterUsage,
235 236
  );
}
237 238 239 240

/// An event that reports when the code size measurement is run via `--analyze-size`.
class CodeSizeEvent extends UsageEvent {
  CodeSizeEvent(String platform, {
241
    required Usage flutterUsage,
242 243 244
  }) : super(
    'code-size-analysis',
    platform,
245
    flutterUsage: flutterUsage
246 247
  );
}
248 249 250 251 252

/// 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);
}
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

/// 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;
282
    LanguageVersion? languageVersion;
283
    for (final Package package in packageConfig.packages) {
284
      final LanguageVersion? packageLanguageVersion = package.languageVersion;
285
      if (package.name == currentPackage) {
286
        languageVersion = packageLanguageVersion;
287
      }
288 289 290
      if (packageLanguageVersion != null &&
          packageLanguageVersion.major >= nullSafeVersion.major &&
          packageLanguageVersion.minor >= nullSafeVersion.minor) {
291 292 293 294
        migrated += 1;
      }
    }
    flutterUsage.sendEvent(kNullSafetyCategory, 'runtime-mode', label: nullSafetyMode.toString());
295 296 297 298
    flutterUsage.sendEvent(kNullSafetyCategory, 'stats', parameters: CustomDimensions(
      nullSafeMigratedLibraries: migrated,
      nullSafeTotalLibraries: packageConfig.packages.length,
    ));
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    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('');
}