2828import com .google .common .cache .Cache ;
2929import com .google .common .cache .CacheBuilder ;
3030import com .google .spanner .admin .database .v1 .DatabaseName ;
31- import io .grpc .CallOptions ;
32- import io .grpc .Channel ;
33- import io .grpc .ClientCall ;
34- import io .grpc .ClientInterceptor ;
31+ import io .grpc .*;
3532import io .grpc .ForwardingClientCall .SimpleForwardingClientCall ;
3633import io .grpc .ForwardingClientCallListener .SimpleForwardingClientCallListener ;
37- import io .grpc .Metadata ;
38- import io .grpc .MethodDescriptor ;
3934import io .grpc .alts .AltsContextUtil ;
4035import io .opencensus .stats .MeasureMap ;
4136import io .opencensus .stats .Stats ;
@@ -91,6 +86,8 @@ class HeaderInterceptor implements ClientInterceptor {
9186 private static final Logger LOGGER = Logger .getLogger (HeaderInterceptor .class .getName ());
9287 private static final Level LEVEL = Level .INFO ;
9388 private final SpannerRpcMetrics spannerRpcMetrics ;
89+ private Float gfeLatency ;
90+ private Float afeLatency ;
9491
9592 HeaderInterceptor (SpannerRpcMetrics spannerRpcMetrics ) {
9693 this .spannerRpcMetrics = spannerRpcMetrics ;
@@ -113,24 +110,46 @@ public void start(Listener<RespT> responseListener, Metadata headers) {
113110 TagContext tagContext = getTagContext (key , method .getFullMethodName (), databaseName );
114111 Attributes attributes =
115112 getMetricAttributes (key , method .getFullMethodName (), databaseName );
116- Map <String , String > builtInMetricsAttributes =
117- getBuiltInMetricAttributes (key , databaseName );
118- builtInMetricsAttributes .put (BuiltInMetricsConstant .REQUEST_ID_KEY .getKey (), requestId );
119- addBuiltInMetricAttributes (compositeTracer , builtInMetricsAttributes );
120- if (span != null ) {
121- span .setAttribute (XGoogSpannerRequestId .REQUEST_ID , requestId );
122- }
113+
123114 super .start (
124115 new SimpleForwardingClientCallListener <RespT >(responseListener ) {
125116 @ Override
126117 public void onHeaders (Metadata metadata ) {
127- // Check if the call uses DirectPath by inspecting the ALTS context.
128- boolean isDirectPathUsed = AltsContextUtil .check (getAttributes ());
129- addDirectPathUsedAttribute (compositeTracer , isDirectPathUsed );
130- processHeader (
131- metadata , tagContext , attributes , span , compositeTracer , isDirectPathUsed );
118+ String serverTiming = metadata .get (SERVER_TIMING_HEADER_KEY );
119+ try {
120+ // Get gfe and afe Latency value
121+ Map <String , Float > serverTimingMetrics = parseServerTimingHeader (serverTiming );
122+ gfeLatency = serverTimingMetrics .get (GFE_TIMING_HEADER );
123+ afeLatency = serverTimingMetrics .get (AFE_TIMING_HEADER );
124+ } catch (NumberFormatException e ) {
125+ LOGGER .log (LEVEL , "Invalid server-timing object in header: {}" , serverTiming );
126+ }
127+
132128 super .onHeaders (metadata );
133129 }
130+
131+ @ Override
132+ public void onClose (Status status , Metadata trailers ) {
133+ // Record Built-in Metrics
134+ boolean isDirectPathUsed = AltsContextUtil .check (getAttributes ());
135+ boolean isAfeEnabled = GapicSpannerRpc .isEnableAFEServerTiming ();
136+ recordSpan (span , requestId );
137+ recordCustomMetrics (tagContext , attributes , isDirectPathUsed );
138+ Map <String , String > builtInMetricsAttributes = new HashMap <>();
139+ try {
140+ builtInMetricsAttributes = getBuiltInMetricAttributes (key , databaseName );
141+ } catch (ExecutionException e ) {
142+ LOGGER .log (
143+ LEVEL , "Unable to get built-in metric attributes {}" , e .getMessage ());
144+ }
145+ recordBuiltInMetrics (
146+ compositeTracer ,
147+ builtInMetricsAttributes ,
148+ requestId ,
149+ isDirectPathUsed ,
150+ isAfeEnabled );
151+ super .onClose (status , trailers );
152+ }
134153 },
135154 headers );
136155 } catch (ExecutionException executionException ) {
@@ -141,29 +160,12 @@ public void onHeaders(Metadata metadata) {
141160 };
142161 }
143162
144- private void processHeader (
145- Metadata metadata ,
146- TagContext tagContext ,
147- Attributes attributes ,
148- Span span ,
149- CompositeTracer compositeTracer ,
150- boolean isDirectPathUsed ) {
163+ private void recordCustomMetrics (
164+ TagContext tagContext , Attributes attributes , Boolean isDirectPathUsed ) {
165+ // Record OpenCensus and Custom OpenTelemetry Metrics
151166 MeasureMap measureMap = STATS_RECORDER .newMeasureMap ();
152- String serverTiming = metadata .get (SERVER_TIMING_HEADER_KEY );
153- try {
154- // Previous implementation parsed the GFE latency directly using:
155- // long latency = Long.parseLong(serverTiming.substring("gfet4t7; dur=".length()));
156- // This approach assumed the serverTiming header contained exactly one metric "gfet4t7".
157- // If additional metrics were introduced in the header, older versions of the library
158- // would fail to parse it correctly. To make the parsing more robust, the logic has been
159- // updated to handle multiple metrics gracefully.
160-
161- Map <String , Float > serverTimingMetrics = parseServerTimingHeader (serverTiming );
162- Float gfeLatency = serverTimingMetrics .get (GFE_TIMING_HEADER );
163- boolean isAfeEnabled = GapicSpannerRpc .isEnableAFEServerTiming ();
164- Float afeLatency = isAfeEnabled ? serverTimingMetrics .get (AFE_TIMING_HEADER ) : null ;
165167
166- // Record OpenCensus and Custom OpenTelemetry Metrics
168+ if (! isDirectPathUsed ) {
167169 if (gfeLatency != null ) {
168170 long gfeVal = gfeLatency .longValue ();
169171 measureMap .put (SPANNER_GFE_LATENCY , gfeVal );
@@ -174,39 +176,35 @@ private void processHeader(
174176 measureMap .put (SPANNER_GFE_HEADER_MISSING_COUNT , 1L );
175177 spannerRpcMetrics .recordGfeHeaderMissingCount (1L , attributes );
176178 }
177- measureMap .record (tagContext );
179+ }
180+ measureMap .record (tagContext );
181+ }
178182
179- // Record Built-in Metrics
180- if (compositeTracer != null ) {
181- // GFE Latency Metrics
182- if (!isDirectPathUsed ) {
183- if (gfeLatency != null ) {
184- compositeTracer .recordGFELatency (gfeLatency );
185- } else {
186- compositeTracer .recordGfeHeaderMissingCount (1L );
187- }
188- }
189- // AFE Tracing
190- if (isAfeEnabled ) {
191- if (afeLatency != null ) {
192- compositeTracer .recordAFELatency (afeLatency );
193- } else {
194- compositeTracer .recordAfeHeaderMissingCount (1L );
195- }
196- }
183+ private void recordSpan (Span span , String requestId ) {
184+ if (span != null ) {
185+ if (gfeLatency != null ) {
186+ span .setAttribute ("gfe_latency" , gfeLatency .toString ());
197187 }
198-
199- // Record Span Attributes
200- if (span != null ) {
201- if (gfeLatency != null ) {
202- span .setAttribute ("gfe_latency" , gfeLatency .toString ());
203- }
204- if (afeLatency != null ) {
205- span .setAttribute ("afe_latency" , afeLatency .toString ());
206- }
188+ if (afeLatency != null ) {
189+ span .setAttribute ("afe_latency" , afeLatency .toString ());
207190 }
208- } catch (NumberFormatException e ) {
209- LOGGER .log (LEVEL , "Invalid server-timing object in header: {}" , serverTiming );
191+ span .setAttribute (XGoogSpannerRequestId .REQUEST_ID , requestId );
192+ }
193+ }
194+
195+ private void recordBuiltInMetrics (
196+ CompositeTracer compositeTracer ,
197+ Map <String , String > builtInMetricsAttributes ,
198+ String requestId ,
199+ Boolean isDirectPathUsed ,
200+ Boolean isAfeEnabled ) {
201+ if (compositeTracer != null ) {
202+ builtInMetricsAttributes .put (BuiltInMetricsConstant .REQUEST_ID_KEY .getKey (), requestId );
203+ builtInMetricsAttributes .put (
204+ BuiltInMetricsConstant .DIRECT_PATH_USED_KEY .getKey (), Boolean .toString (isDirectPathUsed ));
205+ compositeTracer .addAttributes (builtInMetricsAttributes );
206+ compositeTracer .recordServerTimingHeaderMetrics (
207+ gfeLatency , afeLatency , isDirectPathUsed , isAfeEnabled );
210208 }
211209 }
212210
@@ -309,19 +307,4 @@ private Map<String, String> getBuiltInMetricAttributes(String key, DatabaseName
309307 return attributes ;
310308 });
311309 }
312-
313- private void addBuiltInMetricAttributes (
314- CompositeTracer compositeTracer , Map <String , String > builtInMetricsAttributes ) {
315- if (compositeTracer != null ) {
316- compositeTracer .addAttributes (builtInMetricsAttributes );
317- }
318- }
319-
320- private void addDirectPathUsedAttribute (
321- CompositeTracer compositeTracer , Boolean isDirectPathUsed ) {
322- if (compositeTracer != null ) {
323- compositeTracer .addAttributes (
324- BuiltInMetricsConstant .DIRECT_PATH_USED_KEY .getKey (), Boolean .toString (isDirectPathUsed ));
325- }
326- }
327310}
0 commit comments