2828import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .BIGTABLE_PROJECT_ID_KEY ;
2929import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .CLIENT_UID_KEY ;
3030import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .CLUSTER_ID_KEY ;
31+ import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .GRPC_METRICS ;
3132import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .INSTANCE_ID_KEY ;
33+ import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .INTERNAL_METRICS ;
3234import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .METER_NAME ;
3335import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .TABLE_ID_KEY ;
3436import static com .google .cloud .bigtable .data .v2 .stub .metrics .BuiltinMetricsConstants .ZONE_ID_KEY ;
7476import java .util .List ;
7577import java .util .Map ;
7678import java .util .Objects ;
79+ import java .util .Optional ;
7780import java .util .Set ;
7881import java .util .UUID ;
7982import java .util .logging .Level ;
@@ -164,16 +167,12 @@ static List<TimeSeries> convertToApplicationResourceTimeSeries(
164167 "convert application metrics is called when the supported resource is not detected" );
165168 List <TimeSeries > allTimeSeries = new ArrayList <>();
166169 for (MetricData metricData : collection ) {
167- if (!metricData .getInstrumentationScopeInfo ().getName ().equals (METER_NAME )) {
168- // Filter out metric data for instruments that are not part of the bigtable builtin metrics
169- continue ;
170- }
171170 metricData .getData ().getPoints ().stream ()
172171 .map (
173172 pointData ->
174- convertPointToApplicationResourceTimeSeries (
175- metricData , pointData , applicationResource ) )
176- .forEach (allTimeSeries ::add );
173+ createInternalMetricsTimeSeries ( metricData , pointData , applicationResource ))
174+ . filter ( Optional :: isPresent )
175+ .forEach (ts -> ts . ifPresent ( allTimeSeries ::add ) );
177176 }
178177 return allTimeSeries ;
179178 }
@@ -290,19 +289,28 @@ private static TimeSeries convertPointToBigtableTimeSeries(
290289 return builder .build ();
291290 }
292291
293- private static TimeSeries convertPointToApplicationResourceTimeSeries (
292+ private static Optional < TimeSeries > createInternalMetricsTimeSeries (
294293 MetricData metricData , PointData pointData , MonitoredResource applicationResource ) {
295294 TimeSeries .Builder builder =
296295 TimeSeries .newBuilder ()
297296 .setMetricKind (convertMetricKind (metricData ))
298297 .setValueType (convertValueType (metricData .getType ()))
299298 .setResource (applicationResource );
300299
301- Metric .Builder metricBuilder = Metric .newBuilder ().setType (metricData .getName ());
302-
303- Attributes attributes = pointData .getAttributes ();
304- for (AttributeKey <?> key : attributes .asMap ().keySet ()) {
305- metricBuilder .putLabels (key .getKey (), String .valueOf (attributes .get (key )));
300+ final Metric .Builder metricBuilder ;
301+ // TODO: clean this up
302+ // Internal metrics are based on views that include the metric prefix
303+ // gRPC metrics dont have views and are dot encoded
304+ // To unify these:
305+ // - the useless views should be removed
306+ // - internal metrics should use relative metric names w/o the prefix
307+ if (INTERNAL_METRICS .contains (metricData .getName ())) {
308+ metricBuilder = newApplicationMetricBuilder (metricData .getName (), pointData .getAttributes ());
309+ } else if (GRPC_METRICS .containsKey (metricData .getName ())) {
310+ metricBuilder = newGrpcMetricBuilder (metricData .getName (), pointData .getAttributes ());
311+ } else {
312+ logger .fine ("Skipping unexpected internal metric: " + metricData .getName ());
313+ return Optional .empty ();
306314 }
307315
308316 builder .setMetric (metricBuilder .build ());
@@ -314,7 +322,42 @@ private static TimeSeries convertPointToApplicationResourceTimeSeries(
314322 .build ();
315323
316324 builder .addPoints (createPoint (metricData .getType (), pointData , timeInterval ));
317- return builder .build ();
325+ return Optional .of (builder .build ());
326+ }
327+
328+ private static Metric .Builder newApplicationMetricBuilder (
329+ String metricName , Attributes attributes ) {
330+ // TODO: unify handling of metric prefixes
331+ Metric .Builder metricBuilder = Metric .newBuilder ().setType (metricName );
332+ for (Map .Entry <AttributeKey <?>, Object > e : attributes .asMap ().entrySet ()) {
333+ metricBuilder .putLabels (e .getKey ().getKey (), String .valueOf (e .getValue ()));
334+ }
335+ return metricBuilder ;
336+ }
337+
338+ private static Metric .Builder newGrpcMetricBuilder (String grpcMetricName , Attributes attributes ) {
339+ Set <String > allowedAttrs = GRPC_METRICS .get (grpcMetricName );
340+
341+ Metric .Builder metricBuilder =
342+ Metric .newBuilder ()
343+ .setType ("bigtable.googleapis.com/internal/client/" + grpcMetricName .replace ('.' , '/' ));
344+ for (Map .Entry <AttributeKey <?>, Object > e : attributes .asMap ().entrySet ()) {
345+ String attrKey = e .getKey ().getKey ();
346+ Object attrValue = e .getValue ();
347+
348+ // gRPC metrics are experimental and can change attribute names, to avoid incompatibility with
349+ // the predefined
350+ // metric schemas in stackdriver, filter out unknown keys
351+ if (!allowedAttrs .contains (attrKey )) {
352+ continue ;
353+ }
354+ // translate grpc key format to be compatible with cloud monitoring:
355+ // grpc.xds_client.server_failure -> grpc_xds_client_server_failure
356+ String normalizedKey = attrKey .replace ('.' , '_' );
357+ metricBuilder .putLabels (normalizedKey , String .valueOf (attrValue ));
358+ }
359+
360+ return metricBuilder ;
318361 }
319362
320363 private static MetricKind convertMetricKind (MetricData metricData ) {
0 commit comments