[analytics] process and propagate analytics ENV context

Fixes: https://github.com/dart-lang/sdk/issues/62875

Bug: https://github.com/dart-lang/sdk/issues/62875
Change-Id: I864c2fb401dd2eb9859b1277ea971db758ace618
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/488583
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
This commit is contained in:
pq
2026-03-18 14:30:09 -07:00
committed by Commit Queue
parent 4cf95955ee
commit 6cb842ed05
10 changed files with 108 additions and 38 deletions
@@ -312,6 +312,7 @@ abstract class AnalysisServer {
MessageSchedulerListener? messageSchedulerListener,
this.performanceLogger,
required bool usePlugins,
Map<String, String>? environment,
}) : resourceProvider = OverlayResourceProvider(baseResourceProvider),
pubApi = PubApi(
instrumentationService,
@@ -333,7 +334,7 @@ abstract class AnalysisServer {
// don't really exist. If processRunner was supplied, it's likely a mock
// from a test in which case the pub command should still be created.
if (baseResourceProvider is PhysicalResourceProvider) {
processRunner ??= ProcessRunner();
processRunner ??= ProcessRunner(environment: environment);
}
var disablePubCommandVariable =
platform.environment[PubCommand.disablePubCommandEnvironmentKey];
@@ -360,6 +361,7 @@ abstract class AnalysisServer {
notificationManager,
instrumentationService,
sessionLogger,
processRunner: processRunner ?? ProcessRunner(environment: environment),
);
var pluginWatcher = PluginWatcher(
resourceProvider,
@@ -402,6 +402,7 @@ class LegacyAnalysisServer extends AnalysisServer {
super.pluginManager,
super.messageSchedulerListener,
PerformanceLogger? performanceLogger,
super.environment,
}) : lspClientConfiguration = lsp.LspClientConfiguration(
baseResourceProvider.pathContext,
),
@@ -149,6 +149,7 @@ class LspAnalysisServer extends AnalysisServer {
super.dartFixPromptManager,
super.messageSchedulerListener,
super.performanceLogger,
super.environment,
}) : lspClientConfiguration = LspClientConfiguration(
baseResourceProvider.pathContext,
),
@@ -52,6 +52,8 @@ class LspSocketServer implements AbstractSocketServer {
/// The performance logger.
final PerformanceLogger? performanceLogger;
final Map<String, String>? environment;
LspSocketServer(
this.analysisServerOptions,
this.diagnosticServer,
@@ -60,8 +62,9 @@ class LspSocketServer implements AbstractSocketServer {
this.instrumentationService,
this.sessionLogger,
this.detachableFileSystemManager,
this.performanceLogger,
);
this.performanceLogger, {
this.environment,
});
/// Create an analysis server which will communicate with the client using the
/// given serverChannel.
@@ -112,6 +115,7 @@ class LspSocketServer implements AbstractSocketServer {
detachableFileSystemManager: detachableFileSystemManager,
enableBlazeWatcher: true,
performanceLogger: performanceLogger,
environment: environment,
);
detachableFileSystemManager?.setAnalysisServer(server);
}
+33 -23
View File
@@ -30,6 +30,7 @@ import 'package:analysis_server/src/services/perf_witness/perf_witness.dart';
import 'package:analysis_server/src/session_logger/session_logger.dart';
import 'package:analysis_server/src/socket_server.dart';
import 'package:analysis_server/src/status/performance_logger.dart';
import 'package:analysis_server/src/utilities/env.dart' as env;
import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analysis_server/starter.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
@@ -227,13 +228,6 @@ class Driver implements ServerStarter {
analysisServerOptions.usePlugins = results.flag(pluginsFlag);
// Analytics (legacy, and unified)
var disableAnalyticsForSession = results.flag(suppressAnalyticsFlag);
if (results.wasParsed(trainUsingOption)) {
disableAnalyticsForSession = true;
}
var defaultSdkPath = _getSdkPath(results);
var dartSdkManager = DartSdkManager(defaultSdkPath);
@@ -243,22 +237,26 @@ class Driver implements ServerStarter {
var defaultSdk = _createDefaultSdk(defaultSdkPath);
// Create the analytics manager.
Analytics analytics;
if (disableAnalyticsForSession) {
analytics = NoOpAnalytics();
} else {
var tool = switch (clientId) {
'VS-Code' || 'VS-Code-Remote' => DashTool.vscodePlugins,
'IntelliJ-IDEA' => DashTool.intellijPlugins,
'Android-Studio' => DashTool.androidStudioPlugins,
_ => null,
};
if (tool != null) {
analytics = _createAnalytics(defaultSdk, defaultSdkPath, tool);
} else {
analytics = NoOpAnalytics();
}
}
var tool =
env.topLevelTool ??
switch (clientId) {
'VS-Code' || 'VS-Code-Remote' => DashTool.vscodePlugins,
'IntelliJ-IDEA' => DashTool.intellijPlugins,
'Android-Studio' => DashTool.androidStudioPlugins,
_ => null,
};
// Analytics (legacy, and unified).
var disableAnalyticsForSession =
tool == null ||
areAnalyticsSuppressed() ||
results.flag(suppressAnalyticsFlag) ||
results.wasParsed(trainUsingOption);
var analytics = disableAnalyticsForSession
? NoOpAnalytics()
: _createAnalytics(defaultSdk, defaultSdkPath, tool);
var analyticsManager = AnalyticsManager(analytics);
bool shouldSendCallback() {
@@ -375,6 +373,12 @@ class Driver implements ServerStarter {
// TODO(brianwilkerson): Pass the following value to the server and
// implement the debouncing when it hasn't been disabled.
// var disableDebouncing = results[DISABLE_STATUS_NOTIFICATION_DEBOUNCING] as bool;
var environment = env.map(
tool: tool,
suppressAnalytics: disableAnalyticsForSession,
);
if (analysisServerOptions.useLanguageServerProtocol) {
if (sendPort != null) {
throw UnimplementedError(
@@ -391,6 +395,7 @@ class Driver implements ServerStarter {
diagnosticServerPort,
errorNotifier,
performanceLogger,
environment,
);
} else {
startAnalysisServer(
@@ -407,6 +412,7 @@ class Driver implements ServerStarter {
errorNotifier,
sendPort,
performanceLogger,
environment,
);
}
@@ -430,6 +436,7 @@ class Driver implements ServerStarter {
ErrorNotifier errorNotifier,
SendPort? sendPort,
PerformanceLogger? performanceLogger,
Map<String, String>? environment,
) {
var capture = results.flag(disableServerExceptionHandlingOption)
? (_, Function f, {void Function(String)? print}) => f()
@@ -462,6 +469,7 @@ class Driver implements ServerStarter {
analyticsManager,
detachableFileSystemManager,
performanceLogger,
environment: environment,
);
diagnosticServer.httpServer = HttpAnalysisServer(socketServer);
@@ -551,6 +559,7 @@ class Driver implements ServerStarter {
int? diagnosticServerPort,
ErrorNotifier errorNotifier,
PerformanceLogger? performanceLogger,
Map<String, String>? environment,
) {
var capture = args.flag(disableServerExceptionHandlingOption)
? (_, Function f, {void Function(String)? print}) => f()
@@ -571,6 +580,7 @@ class Driver implements ServerStarter {
sessionLogger,
detachableFileSystemManager,
performanceLogger,
environment: environment,
);
errorNotifier.server = socketServer.analysisServer;
diagnosticServer.httpServer = HttpAnalysisServer(socketServer);
@@ -64,6 +64,8 @@ class SocketServer implements AbstractSocketServer {
/// The performance logger.
final PerformanceLogger? performanceLogger;
final Map<String, String>? environment;
SocketServer(
this.analysisServerOptions,
this.sdkManager,
@@ -74,8 +76,9 @@ class SocketServer implements AbstractSocketServer {
this.diagnosticServer,
this.analyticsManager,
this.detachableFileSystemManager,
this.performanceLogger,
);
this.performanceLogger, {
this.environment,
});
/// Create an analysis server which will communicate with the client using the
/// given serverChannel.
@@ -112,6 +115,7 @@ class SocketServer implements AbstractSocketServer {
detachableFileSystemManager: detachableFileSystemManager,
enableBlazeWatcher: true,
performanceLogger: performanceLogger,
environment: environment,
);
detachableFileSystemManager?.setAnalysisServer(server);
}
@@ -0,0 +1,35 @@
// Copyright (c) 2026, 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 'package:platform/platform.dart' as platform;
import 'package:unified_analytics/unified_analytics.dart';
DashTool? get topLevelTool {
var toolValue = _readEnv(DashEnvVar.tool.name);
if (toolValue != null) {
try {
return DashTool.fromLabel(toolValue);
} on Exception {
// Unsupported tools are skipped.
// We could consider logging or crash-reporting but we can't collect
// analytics since telemetry for an unknown tool can't be opted into.
}
}
return null;
}
Map<String, String> get _locaEnvironment =>
const platform.LocalPlatform().environment;
/// Returns a copy of the current environment with the given analytics values set.
Map<String, String> map({
required DashTool? tool,
required bool suppressAnalytics,
}) => <String, String>{
..._locaEnvironment,
DashEnvVar.suppressAnalytics.name: suppressAnalytics.toString(),
if (tool != null) DashEnvVar.tool.name: tool.label,
};
String? _readEnv(String key) => _locaEnvironment[key];
@@ -8,7 +8,9 @@ import 'dart:io';
/// An abstraction over [Process] from 'dart:io' to allow mocking in tests.
class ProcessRunner {
const ProcessRunner();
final Map<String, String>? environment;
const ProcessRunner({this.environment});
ProcessResult runSync(
String executable,
@@ -17,14 +19,16 @@ class ProcessRunner {
Map<String, String>? environment,
Encoding? stderrEncoding,
Encoding? stdoutEncoding,
}) => Process.runSync(
executable,
arguments,
workingDirectory: workingDirectory,
environment: environment,
stderrEncoding: stderrEncoding,
stdoutEncoding: stdoutEncoding,
);
}) {
return Process.runSync(
executable,
arguments,
workingDirectory: workingDirectory,
environment: _mergedEnv(environment),
stderrEncoding: stderrEncoding,
stdoutEncoding: stdoutEncoding,
);
}
Future<Process> start(
String executable,
@@ -39,10 +43,15 @@ class ProcessRunner {
executable,
arguments,
workingDirectory: workingDirectory,
environment: environment,
environment: _mergedEnv(environment),
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
mode: mode,
);
}
Map<String, String>? _mergedEnv(Map<String, String>? environment) {
var merged = <String, String>{...?this.environment, ...?environment};
return merged.isNotEmpty ? merged : null;
}
}
+1
View File
@@ -27,6 +27,7 @@ dependencies:
meta: any
path: any
perf_witness: any
platform: any
pub_semver: any
stream_channel: any
telemetry: any
+3
View File
@@ -92,6 +92,9 @@ class MockProcess implements Process {
}
class MockProcessRunner implements ProcessRunner {
@override
final Map<String, String>? environment = null;
ProcessResult Function(
String executable,
List<String> arguments, {