Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'dart:io' as io;
5 :
6 : import 'package:meta/meta.dart';
7 :
8 : import 'env_constants.dart';
9 : import 'env_from_define.dart';
10 : import 'environment_service.dart';
11 :
12 : /// IO implementation of the environment service.
13 : ///
14 : /// This implementation is used on all platforms except web (Dart VM, Flutter
15 : /// native, and Flutter desktop).
16 : ///
17 : /// Environment variable lookup priority:
18 : /// 1. String.fromEnvironment (--dart-define values)
19 : /// 2. io.Platform.environment (system environment variables)
20 : ///
21 : /// The implementation uses String.fromEnvironment with empty string as the
22 : /// default value. If the result is empty, it falls back to checking
23 : /// io.Platform.environment. This allows --dart-define values to take
24 : /// precedence over system environment variables, as per OpenTelemetry
25 : /// specification recommendations.
26 : class EnvironmentService implements EnvironmentServiceInterface {
27 465 : static final EnvironmentService _instance = EnvironmentService._();
28 :
29 : /// The singleton instance of the EnvironmentService.
30 310 : static EnvironmentService get instance => _instance;
31 :
32 155 : EnvironmentService._();
33 :
34 : /// Test-only replacement for the lookup sources. When non-null, values
35 : /// come exclusively from this map (a missing key reads as unset) and
36 : /// still flow through the semicolon-to-comma conversion, so tests get
37 : /// deterministic, in-process control of the environment without
38 : /// spawning subprocesses.
39 : @visibleForTesting
40 : static Map<String, String>? testOverrides;
41 :
42 : /// Gets the value of an environment variable.
43 : ///
44 : /// Returns the value from the first available source:
45 : /// 1. String.fromEnvironment if defined via --dart-define
46 : /// 2. io.Platform.environment (system environment variables)
47 : /// 3. null if not found
48 : ///
49 : /// Note: String.fromEnvironment only works for compile-time constants that
50 : /// are in our [supportedEnvVars] set. This is a limitation of the Dart
51 : /// language when using --dart-define.
52 : ///
53 : /// @param key The name of the environment variable to retrieve
54 : /// @return The value of the environment variable, or null if not found
55 155 : @override
56 : String? getValue(String key) {
57 : String? value;
58 : final overrides = testOverrides;
59 : if (overrides != null) {
60 19 : value = overrides[key];
61 : } else {
62 : // Priority 1: String.fromEnvironment (--dart-define values)
63 : // Only check if this is a known environment variable
64 142 : if (supportedEnvVars.contains(key)) {
65 142 : final fromEnvironment = getFromEnvironment(key);
66 142 : if (fromEnvironment != null && fromEnvironment.isNotEmpty) {
67 : value = fromEnvironment;
68 : }
69 : }
70 :
71 : // Priority 2: Platform environment variables
72 284 : value ??= io.Platform.environment[key];
73 : }
74 :
75 : // Handle comma-separated values for --define compatibility
76 : // The --define flag cannot handle commas in values, so we use semicolons
77 : // as a delimiter for these specific variables and convert them back
78 : if (value != null) {
79 : switch (key) {
80 155 : case otelResourceAttributes:
81 154 : case otelPropagators:
82 154 : case otelExporterOtlpHeaders:
83 154 : case otelExporterOtlpTracesHeaders:
84 154 : case otelExporterOtlpMetricsHeaders:
85 154 : case otelExporterOtlpLogsHeaders:
86 5 : value = value.replaceAll(';', ',');
87 : break;
88 : }
89 : }
90 :
91 : // Per the OpenTelemetry specification (configuration.md), an empty value
92 : // of an environment variable MUST be interpreted the same way as when the
93 : // variable is unset. Normalize empty strings to null here so every
94 : // consumer reads an empty value as unset (issue #213).
95 155 : return (value == null || value.isEmpty) ? null : value;
96 : }
97 : }
|