1+ # -*- coding: utf-8 -*-
2+ # Copyright 2025 Google LLC
3+ #
4+ # Licensed under the Apache License, Version 2.0 (the "License");
5+ # you may not use this file except in compliance with the License.
6+ # You may obtain a copy of the License at
7+ #
8+ # http://www.apache.org/licenses/LICENSE-2.0
9+ #
10+ # Unless required by applicable law or agreed to in writing, software
11+ # distributed under the License is distributed on an "AS IS" BASIS,
12+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ # See the License for the specific language governing permissions and
14+ # limitations under the License.
15+
16+ """
17+ This module provides the MetricsGfeTracer class for recording GFE (Google Front End) metrics.
18+ It utilizes OpenTelemetry for metrics instrumentation and records latency and missing header counts
19+ for GFE requests.
20+ """
21+
22+ try :
23+ from opentelemetry .metrics import Counter , Histogram , get_meter_provider
24+
25+ HAS_OPENTELEMETRY_INSTALLED = True
26+ except ImportError : # pragma: NO COVER
27+ HAS_OPENTELEMETRY_INSTALLED = False
28+ from typing import List
29+ import re
30+ from google .cloud .spanner_v1 .metrics .constants import (
31+ BUILT_IN_METRICS_METER_NAME ,
32+ SPANNER_SERVICE_NAME ,
33+ METRIC_NAME_GFE_LATENCY ,
34+ METRIC_NAME_GFE_MISSING_HEADER_COUNT ,
35+ )
36+ from google .cloud .spanner_v1 import __version__
37+
38+
39+ class MetricsGfeTracer :
40+ """
41+ A tracer class for recording Google Front End (GFE) metrics using OpenTelemetry.
42+
43+ This class provides methods to record latency and missing header counts for GFE requests.
44+ It uses OpenTelemetry's Histogram and Counter instruments for metrics recording.
45+ """
46+
47+ _instrument_gfe_latency : "Histogram"
48+ _instrument_gfe_missing_header_count : "Counter"
49+ enabled = False
50+
51+ def __init__ (self ) -> None :
52+ """
53+ Initializes the MetricsGfeTracer instance and creates the necessary metric instruments.
54+ """
55+ self ._create_gfe_metric_instruments ()
56+
57+ def record_gfe_metrics (self , metadata : List ):
58+ """
59+ Records GFE metrics based on the provided metadata.
60+
61+ If the tracer is enabled, it checks for GFE-related headers in the metadata and records
62+ latency or missing header counts accordingly.
63+
64+ Args:
65+ metadata (List): A list of metadata headers to be checked for GFE information.
66+ """
67+ if not self .enabled :
68+ return
69+
70+ gfe_headers = [
71+ header .value
72+ for header in metadata
73+ if header .key == "server-timing" and header .value .startswith ("gfe" )
74+ ]
75+
76+ if len (gfe_headers ) == 0 :
77+ self .record_gfe_missing_header_count ()
78+ return
79+
80+ for gfe_header in gfe_headers :
81+ match = re .search (r"dur=(\d+)" , gfe_header )
82+ if match :
83+ duration = int (match .group (1 ))
84+ self .record_gfe_latency (duration )
85+
86+ def record_gfe_latency (self , latency : int ) -> None :
87+ """
88+ Records the GFE latency using the Histogram instrument.
89+
90+ Args:
91+ latency (int): The latency duration to be recorded.
92+ """
93+ if not self .enabled or not HAS_OPENTELEMETRY_INSTALLED :
94+ return
95+ self ._instrument_gfe_latency .record (amount = latency )
96+
97+ def record_gfe_missing_header_count (self ) -> None :
98+ """
99+ Increments the counter for missing GFE headers.
100+ """
101+ if not self .enabled or not HAS_OPENTELEMETRY_INSTALLED :
102+ return
103+ self ._instrument_gfe_missing_header_count .add (amount = 1 )
104+
105+ def _create_gfe_metric_instruments (self ) -> None :
106+ """
107+ Creates the OpenTelemetry metric instruments for recording GFE metrics.
108+ """
109+ if not HAS_OPENTELEMETRY_INSTALLED :
110+ return
111+ meter_provider = get_meter_provider ()
112+ meter = meter_provider .get_meter (
113+ name = BUILT_IN_METRICS_METER_NAME , version = __version__
114+ )
115+ self ._instrument_gfe_latency = meter .create_histogram (
116+ name = f"{ SPANNER_SERVICE_NAME } /{ METRIC_NAME_GFE_LATENCY } " ,
117+ unit = "ms" ,
118+ description = "GFE Latency." ,
119+ )
120+ self ._instrument_gfe_missing_header_count = meter .create_counter (
121+ name = f"{ SPANNER_SERVICE_NAME } /{ METRIC_NAME_GFE_MISSING_HEADER_COUNT } " ,
122+ unit = "1" ,
123+ description = "GFE missing header count." ,
124+ )
0 commit comments