2020import static io .opentelemetry .api .common .AttributeKey .stringKey ;
2121import static java .util .Arrays .stream ;
2222import static java .util .stream .Collectors .joining ;
23+ import static java .util .stream .Collectors .toList ;
2324import static java .util .stream .Collectors .toMap ;
2425
2526import com .google .auth .oauth2 .GoogleCredentials ;
2627import com .google .auto .service .AutoService ;
2728import io .opentelemetry .api .common .Attributes ;
2829import io .opentelemetry .exporter .otlp .http .metrics .OtlpHttpMetricExporter ;
30+ import io .opentelemetry .exporter .otlp .http .metrics .OtlpHttpMetricExporterBuilder ;
2931import io .opentelemetry .exporter .otlp .http .trace .OtlpHttpSpanExporter ;
32+ import io .opentelemetry .exporter .otlp .http .trace .OtlpHttpSpanExporterBuilder ;
3033import io .opentelemetry .exporter .otlp .metrics .OtlpGrpcMetricExporter ;
34+ import io .opentelemetry .exporter .otlp .metrics .OtlpGrpcMetricExporterBuilder ;
3135import io .opentelemetry .exporter .otlp .trace .OtlpGrpcSpanExporter ;
36+ import io .opentelemetry .exporter .otlp .trace .OtlpGrpcSpanExporterBuilder ;
3237import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizer ;
3338import io .opentelemetry .sdk .autoconfigure .spi .AutoConfigurationCustomizerProvider ;
3439import io .opentelemetry .sdk .autoconfigure .spi .ConfigProperties ;
4146import java .util .Map ;
4247import java .util .Objects ;
4348import java .util .Optional ;
49+ import java .util .logging .Level ;
50+ import java .util .logging .Logger ;
4451import javax .annotation .Nonnull ;
45- import javax .annotation .Nullable ;
4652import org .apache .beam .sdk .annotations .Internal ;
4753import org .apache .beam .sdk .extensions .opentelemetry .gcp .auth .GoogleAuthException .Reason ;
4854
6268 * @see AutoConfigurationCustomizerProvider
6369 * @see GoogleCredentials
6470 */
65- @ AutoService (AutoConfigurationCustomizerProvider .class )
6671@ Internal
72+ @ AutoService (AutoConfigurationCustomizerProvider .class )
6773public class GcpAuthAutoConfigurationCustomizerProvider
6874 implements AutoConfigurationCustomizerProvider {
6975
76+ private static final Logger LOG =
77+ Logger .getLogger (GcpAuthAutoConfigurationCustomizerProvider .class .getName ());
78+ private static final String SIGNAL_TARGET_WARNING_FIX_SUGGESTION =
79+ String .format (
80+ "You may safely ignore this warning if it is intentional, otherwise please configure the '%s' by exporting valid values to environment variable: %s or by setting valid values in system property: %s." ,
81+ ConfigurableOption .GOOGLE_OTEL_AUTH_TARGET_SIGNALS .getUserReadableName (),
82+ ConfigurableOption .GOOGLE_OTEL_AUTH_TARGET_SIGNALS .getEnvironmentVariable (),
83+ ConfigurableOption .GOOGLE_OTEL_AUTH_TARGET_SIGNALS .getSystemProperty ());
84+
7085 static final String QUOTA_USER_PROJECT_HEADER = "x-goog-user-project" ;
7186 static final String GCP_USER_PROJECT_ID_KEY = "gcp.project_id" ;
7287
7388 static final String SIGNAL_TYPE_TRACES = "traces" ;
7489 static final String SIGNAL_TYPE_METRICS = "metrics" ;
7590 static final String SIGNAL_TYPE_ALL = "all" ;
76-
77- private @ Nullable GoogleCredentials credentials ;
91+ static final String SIGNAL_TYPE_NONE = "none" ;
7892
7993 /**
8094 * Customizes the provided {@link AutoConfigurationCustomizer} such that authenticated exports to
8195 * GCP Telemetry API are possible from the configured OTLP exporter.
8296 *
83- * <p>This method performs the following:
97+ * <p>This method attempts to retrieve Google Application Default Credentials (ADC) and performs
98+ * the following:
8499 *
85100 * <ul>
86101 * <li>Verifies whether the configured OTLP endpoint (base or signal specific) is a known GCP
@@ -99,147 +114,139 @@ public class GcpAuthAutoConfigurationCustomizerProvider
99114 */
100115 @ Override
101116 public void customize (@ Nonnull AutoConfigurationCustomizer autoConfiguration ) {
117+ java .util .function .Supplier <GoogleCredentials > credentialsSupplier =
118+ new java .util .function .Supplier <GoogleCredentials >() {
119+ private @ javax .annotation .Nullable GoogleCredentials credentials ;
120+
121+ @ Override
122+ public synchronized GoogleCredentials get () {
123+ if (credentials == null ) {
124+ try {
125+ credentials = GoogleCredentials .getApplicationDefault ();
126+ } catch (IOException e ) {
127+ throw new GoogleAuthException (Reason .FAILED_ADC_RETRIEVAL , e );
128+ }
129+ }
130+ return credentials ;
131+ }
132+ };
102133 autoConfiguration
103- .addSpanExporterCustomizer (this ::customizeSpanExporter )
104- .addMetricExporterCustomizer (this ::customizeMetricExporter )
105- .addResourceCustomizer (this ::customizeResource );
134+ .addSpanExporterCustomizer (
135+ (spanExporter , configProperties ) ->
136+ customizeSpanExporter (spanExporter , credentialsSupplier , configProperties ))
137+ .addMetricExporterCustomizer (
138+ (metricExporter , configProperties ) ->
139+ customizeMetricExporter (metricExporter , credentialsSupplier , configProperties ))
140+ .addResourceCustomizer (
141+ (resource , configProperties ) ->
142+ customizeResource (resource , credentialsSupplier , configProperties ));
106143 }
107144
108145 @ Override
109146 public int order () {
110147 return Integer .MAX_VALUE - 1 ;
111148 }
112149
113- private synchronized GoogleCredentials getCredentials () {
114- if (credentials == null ) {
115- try {
116- credentials = GoogleCredentials .getApplicationDefault ();
117- } catch (IOException e ) {
118- throw new GoogleAuthException (Reason .FAILED_ADC_RETRIEVAL , e );
119- }
120- }
121- return credentials ;
122- }
123-
124- private SpanExporter customizeSpanExporter (
125- SpanExporter exporter , ConfigProperties configProperties ) {
150+ private static SpanExporter customizeSpanExporter (
151+ SpanExporter exporter ,
152+ java .util .function .Supplier <GoogleCredentials > credentialsSupplier ,
153+ ConfigProperties configProperties ) {
126154 if (isSignalTargeted (SIGNAL_TYPE_TRACES , configProperties )) {
127- return addAuthorizationHeaders (exporter , configProperties );
155+ return addAuthorizationHeaders (exporter , credentialsSupplier .get (), configProperties );
156+ } else {
157+ String [] params = {
158+ SIGNAL_TYPE_TRACES , SIGNAL_TYPE_NONE , SIGNAL_TARGET_WARNING_FIX_SUGGESTION
159+ };
160+ LOG .log (
161+ Level .WARNING ,
162+ "GCP Authentication Extension is not configured for signal type: {0} or is configured with signal type: {1}. {2}" ,
163+ params );
128164 }
129165 return exporter ;
130166 }
131167
132- private MetricExporter customizeMetricExporter (
133- MetricExporter exporter , ConfigProperties configProperties ) {
168+ private static MetricExporter customizeMetricExporter (
169+ MetricExporter exporter ,
170+ java .util .function .Supplier <GoogleCredentials > credentialsSupplier ,
171+ ConfigProperties configProperties ) {
134172 if (isSignalTargeted (SIGNAL_TYPE_METRICS , configProperties )) {
135- return addAuthorizationHeaders (exporter , configProperties );
173+ return addAuthorizationHeaders (exporter , credentialsSupplier .get (), configProperties );
174+ } else {
175+ String [] params = {
176+ SIGNAL_TYPE_METRICS , SIGNAL_TYPE_NONE , SIGNAL_TARGET_WARNING_FIX_SUGGESTION
177+ };
178+ LOG .log (
179+ Level .WARNING ,
180+ "GCP Authentication Extension is not configured for signal type: {0} or is configured with signal type: {1}. {2}" ,
181+ params );
136182 }
137183 return exporter ;
138184 }
139185
140186 // Checks if the auth extension is configured to target the passed signal for authentication.
141187 private static boolean isSignalTargeted (String checkSignal , ConfigProperties configProperties ) {
142- String endpoint = configProperties .getString ("otel.exporter.otlp." + checkSignal + ".endpoint" );
143- if (endpoint == null ) {
144- endpoint = configProperties .getString ("otel.exporter.otlp.endpoint" );
145- }
146- if (endpoint == null ) {
147- return false ;
148- }
149-
150- try {
151- java .net .URI uri = new java .net .URI (endpoint );
152- String host = uri .getHost ();
153- String scheme = uri .getScheme ();
154- if (host == null
155- || scheme == null
156- || !scheme .equalsIgnoreCase ("https" )
157- || (!host .equalsIgnoreCase ("telemetry.googleapis.com" )
158- && !host .equalsIgnoreCase ("telemetry.mtls.googleapis.com" ))) {
159- return false ;
160- }
161- } catch (java .net .URISyntaxException e ) {
162- return false ;
163- }
164-
165188 String userSpecifiedTargetedSignals =
166189 ConfigurableOption .GOOGLE_OTEL_AUTH_TARGET_SIGNALS .getConfiguredValueWithFallback (
167190 configProperties , () -> SIGNAL_TYPE_ALL );
168- return stream (userSpecifiedTargetedSignals .split ("," ))
169- .map (String ::trim )
170- .anyMatch (
171- targetedSignal ->
172- targetedSignal .equals (checkSignal ) || targetedSignal .equals (SIGNAL_TYPE_ALL ));
173- }
174-
175- private boolean isAnySignalTargeted (ConfigProperties configProperties ) {
176- return isSignalTargeted (SIGNAL_TYPE_TRACES , configProperties )
177- || isSignalTargeted (SIGNAL_TYPE_METRICS , configProperties );
191+ List <String > targetedSignals =
192+ stream (userSpecifiedTargetedSignals .split ("," )).map (String ::trim ).collect (toList ());
193+ if (targetedSignals .contains (SIGNAL_TYPE_NONE )) {
194+ return false ;
195+ }
196+ return targetedSignals .contains (checkSignal ) || targetedSignals .contains (SIGNAL_TYPE_ALL );
178197 }
179198
180199 // Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and
181200 // OtlpHttpSpanExporter.
182- private SpanExporter addAuthorizationHeaders (
183- SpanExporter exporter , ConfigProperties configProperties ) {
201+ private static SpanExporter addAuthorizationHeaders (
202+ SpanExporter exporter , GoogleCredentials credentials , ConfigProperties configProperties ) {
184203 if (exporter instanceof OtlpHttpSpanExporter ) {
185- SpanExporter result =
204+ OtlpHttpSpanExporterBuilder builder =
186205 ((OtlpHttpSpanExporter ) exporter )
187206 .toBuilder ()
188- .setHeaders (() -> getRequiredHeaderMap (configProperties ))
189- .build ();
190- exporter .shutdown ();
191- return result ;
207+ .setHeaders (() -> getRequiredHeaderMap (credentials , configProperties ));
208+ return builder .build ();
192209 } else if (exporter instanceof OtlpGrpcSpanExporter ) {
193- SpanExporter result =
210+ OtlpGrpcSpanExporterBuilder builder =
194211 ((OtlpGrpcSpanExporter ) exporter )
195212 .toBuilder ()
196- .setHeaders (() -> getRequiredHeaderMap (configProperties ))
197- .build ();
198- exporter .shutdown ();
199- return result ;
213+ .setHeaders (() -> getRequiredHeaderMap (credentials , configProperties ));
214+ return builder .build ();
200215 }
201216 return exporter ;
202217 }
203218
204219 // Adds authorization headers to the calls made by the OtlpGrpcMetricExporter and
205220 // OtlpHttpMetricExporter.
206- private MetricExporter addAuthorizationHeaders (
207- MetricExporter exporter , ConfigProperties configProperties ) {
221+ private static MetricExporter addAuthorizationHeaders (
222+ MetricExporter exporter , GoogleCredentials credentials , ConfigProperties configProperties ) {
208223 if (exporter instanceof OtlpHttpMetricExporter ) {
209- MetricExporter result =
224+ OtlpHttpMetricExporterBuilder builder =
210225 ((OtlpHttpMetricExporter ) exporter )
211226 .toBuilder ()
212- .setHeaders (() -> getRequiredHeaderMap (configProperties ))
213- .build ();
214- exporter .shutdown ();
215- return result ;
227+ .setHeaders (() -> getRequiredHeaderMap (credentials , configProperties ));
228+ return builder .build ();
216229 } else if (exporter instanceof OtlpGrpcMetricExporter ) {
217- MetricExporter result =
230+ OtlpGrpcMetricExporterBuilder builder =
218231 ((OtlpGrpcMetricExporter ) exporter )
219232 .toBuilder ()
220- .setHeaders (() -> getRequiredHeaderMap (configProperties ))
221- .build ();
222- exporter .shutdown ();
223- return result ;
233+ .setHeaders (() -> getRequiredHeaderMap (credentials , configProperties ));
234+ return builder .build ();
224235 }
225236 return exporter ;
226237 }
227238
228- private Map <String , String > getRequiredHeaderMap (ConfigProperties configProperties ) {
229- GoogleCredentials creds = getCredentials ();
239+ private static Map <String , String > getRequiredHeaderMap (
240+ GoogleCredentials credentials , ConfigProperties configProperties ) {
230241 Map <String , List <String >> gcpHeaders ;
231242 try {
232243 // this also refreshes the credentials, if required
233- gcpHeaders = creds .getRequestMetadata ();
244+ gcpHeaders = credentials .getRequestMetadata ();
234245 } catch (IOException e ) {
235246 throw new GoogleAuthException (Reason .FAILED_ADC_REFRESH , e );
236247 }
237- if (gcpHeaders == null ) {
238- return Map .of ();
239- }
240248 Map <String , String > flattenedHeaders =
241249 gcpHeaders .entrySet ().stream ()
242- .filter (entry -> entry .getKey () != null && entry .getValue () != null )
243250 .collect (
244251 toMap (
245252 Map .Entry ::getKey ,
@@ -262,16 +269,19 @@ private Map<String, String> getRequiredHeaderMap(ConfigProperties configProperti
262269 }
263270
264271 // Updates the current resource with the attributes required for ingesting OTLP data on GCP.
265- private Resource customizeResource (Resource resource , ConfigProperties configProperties ) {
266- if (!isAnySignalTargeted (configProperties )) {
272+ private static Resource customizeResource (
273+ Resource resource ,
274+ java .util .function .Supplier <GoogleCredentials > credentialsSupplier ,
275+ ConfigProperties configProperties ) {
276+ if (!isSignalTargeted (SIGNAL_TYPE_TRACES , configProperties )
277+ && !isSignalTargeted (SIGNAL_TYPE_METRICS , configProperties )) {
267278 return resource ;
268279 }
269-
270280 String gcpProjectId ;
271281 try {
272282 gcpProjectId = ConfigurableOption .GOOGLE_CLOUD_PROJECT .getConfiguredValue (configProperties );
273283 } catch (ConfigurationException e ) {
274- gcpProjectId = getCredentials ().getProjectId ();
284+ gcpProjectId = credentialsSupplier . get ().getProjectId ();
275285 if (gcpProjectId == null || gcpProjectId .isEmpty ()) {
276286 throw e ;
277287 }
0 commit comments