@@ -49,7 +49,12 @@ class PrometheusExporter(MetricsExporter):
4949
5050 """
5151
52- def __init__ (self , port : int = 9090 , use_prometheus_client : bool = True ):
52+ def __init__ (
53+ self ,
54+ port : int = 9090 ,
55+ use_prometheus_client : bool = True ,
56+ host : str = "127.0.0.1" ,
57+ ):
5358 """Initialize Prometheus exporter.
5459
5560 Parameters
@@ -58,9 +63,12 @@ def __init__(self, port: int = 9090, use_prometheus_client: bool = True):
5863 HTTP server port
5964 use_prometheus_client : bool
6065 Whether to use prometheus_client library
66+ host : str
67+ Host address to bind to (default: 127.0.0.1 for localhost only)
6168
6269 """
6370 self .port = port
71+ self .host = host
6472 self .use_prometheus_client = use_prometheus_client
6573 self ._registered_functions : Dict [str , Callable ] = {}
6674 self ._lock = threading .Lock ()
@@ -183,54 +191,130 @@ def _generate_text_metrics(self) -> str:
183191
184192 """
185193 lines = []
194+
195+ # Emit HELP/TYPE headers once at the top for each metric
186196 lines .append ("# HELP cachier_cache_hits_total Total cache hits" )
187197 lines .append ("# TYPE cachier_cache_hits_total counter" )
198+
199+ with self ._lock :
200+ for func_name , func in self ._registered_functions .items ():
201+ if not hasattr (func , "metrics" ) or func .metrics is None :
202+ continue
203+ stats = func .metrics .get_stats ()
204+ lines .append (
205+ f'cachier_cache_hits_total{{function="{ func_name } "}} { stats .hits } '
206+ )
207+
208+ # Misses
209+ lines .append ("" )
210+ lines .append ("# HELP cachier_cache_misses_total Total cache misses" )
211+ lines .append ("# TYPE cachier_cache_misses_total counter" )
212+
213+ with self ._lock :
214+ for func_name , func in self ._registered_functions .items ():
215+ if not hasattr (func , "metrics" ) or func .metrics is None :
216+ continue
217+ stats = func .metrics .get_stats ()
218+ lines .append (
219+ f'cachier_cache_misses_total{{function="{ func_name } "}} { stats .misses } '
220+ )
188221
222+ # Hit rate
223+ lines .append ("" )
224+ lines .append ("# HELP cachier_cache_hit_rate Cache hit rate percentage" )
225+ lines .append ("# TYPE cachier_cache_hit_rate gauge" )
226+
189227 with self ._lock :
190228 for func_name , func in self ._registered_functions .items ():
191- if not hasattr (func , "metrics" ):
229+ if not hasattr (func , "metrics" ) or func . metrics is None :
192230 continue
231+ stats = func .metrics .get_stats ()
232+ lines .append (
233+ f'cachier_cache_hit_rate{{function="{ func_name } "}} { stats .hit_rate :.2f} '
234+ )
235+
236+ # Average latency
237+ lines .append ("" )
238+ lines .append ("# HELP cachier_avg_latency_ms Average cache operation latency in milliseconds" )
239+ lines .append ("# TYPE cachier_avg_latency_ms gauge" )
240+
241+ with self ._lock :
242+ for func_name , func in self ._registered_functions .items ():
243+ if not hasattr (func , "metrics" ) or func .metrics is None :
244+ continue
245+ stats = func .metrics .get_stats ()
246+ lines .append (
247+ f'cachier_avg_latency_ms{{function="{ func_name } "}} { stats .avg_latency_ms :.4f} '
248+ )
193249
250+ # Stale hits
251+ lines .append ("" )
252+ lines .append ("# HELP cachier_stale_hits_total Total stale cache hits" )
253+ lines .append ("# TYPE cachier_stale_hits_total counter" )
254+
255+ with self ._lock :
256+ for func_name , func in self ._registered_functions .items ():
257+ if not hasattr (func , "metrics" ) or func .metrics is None :
258+ continue
194259 stats = func .metrics .get_stats ()
260+ lines .append (
261+ f'cachier_stale_hits_total{{function="{ func_name } "}} { stats .stale_hits } '
262+ )
195263
196- # Hits
264+ # Recalculations
265+ lines .append ("" )
266+ lines .append ("# HELP cachier_recalculations_total Total cache recalculations" )
267+ lines .append ("# TYPE cachier_recalculations_total counter" )
268+
269+ with self ._lock :
270+ for func_name , func in self ._registered_functions .items ():
271+ if not hasattr (func , "metrics" ) or func .metrics is None :
272+ continue
273+ stats = func .metrics .get_stats ()
197274 lines .append (
198- f'cachier_cache_hits_total{{function="{ func_name } "}} '
199- f"{ stats .hits } "
275+ f'cachier_recalculations_total{{function="{ func_name } "}} { stats .recalculations } '
200276 )
201277
202- # Misses
203- if not lines or "misses" not in lines [- 1 ]:
204- lines .append (
205- "# HELP cachier_cache_misses_total Total cache misses"
206- )
207- lines .append ("# TYPE cachier_cache_misses_total counter" )
278+ # Entry count
279+ lines .append ("" )
280+ lines .append ("# HELP cachier_entry_count Current cache entries" )
281+ lines .append ("# TYPE cachier_entry_count gauge" )
282+
283+ with self ._lock :
284+ for func_name , func in self ._registered_functions .items ():
285+ if not hasattr (func , "metrics" ) or func .metrics is None :
286+ continue
287+ stats = func .metrics .get_stats ()
208288 lines .append (
209- f'cachier_cache_misses_total{{function="{ func_name } "}} '
210- f"{ stats .misses } "
289+ f'cachier_entry_count{{function="{ func_name } "}} { stats .entry_count } '
211290 )
212291
213- # Hit rate
214- if not lines or "hit_rate" not in lines [- 1 ]:
215- lines .append (
216- "# HELP cachier_cache_hit_rate Cache "
217- "hit rate percentage"
218- )
219- lines .append ("# TYPE cachier_cache_hit_rate gauge" )
292+ # Cache size
293+ lines .append ("" )
294+ lines .append ("# HELP cachier_cache_size_bytes Total cache size in bytes" )
295+ lines .append ("# TYPE cachier_cache_size_bytes gauge" )
296+
297+ with self ._lock :
298+ for func_name , func in self ._registered_functions .items ():
299+ if not hasattr (func , "metrics" ) or func .metrics is None :
300+ continue
301+ stats = func .metrics .get_stats ()
220302 lines .append (
221- f'cachier_cache_hit_rate{{function="{ func_name } "}} '
222- f"{ stats .hit_rate :.2f} "
303+ f'cachier_cache_size_bytes{{function="{ func_name } "}} { stats .total_size_bytes } '
223304 )
224305
225- # Entry count
226- if not lines or "entry_count" not in lines [- 1 ]:
227- lines .append (
228- "# HELP cachier_entry_count Current cache entries"
229- )
230- lines .append ("# TYPE cachier_entry_count gauge" )
306+ # Size limit rejections
307+ lines .append ("" )
308+ lines .append ("# HELP cachier_size_limit_rejections_total Entries rejected due to size limit" )
309+ lines .append ("# TYPE cachier_size_limit_rejections_total counter" )
310+
311+ with self ._lock :
312+ for func_name , func in self ._registered_functions .items ():
313+ if not hasattr (func , "metrics" ) or func .metrics is None :
314+ continue
315+ stats = func .metrics .get_stats ()
231316 lines .append (
232- f'cachier_entry_count{{function="{ func_name } "}} '
233- f"{ stats .entry_count } "
317+ f'cachier_size_limit_rejections_total{{function="{ func_name } "}} { stats .size_limit_rejections } '
234318 )
235319
236320 return "\n " .join (lines ) + "\n "
@@ -277,7 +361,7 @@ def do_GET(self):
277361 def log_message (self , fmt , * args ):
278362 """Suppress log messages."""
279363
280- self ._server = HTTPServer (("" , self .port ), MetricsHandler )
364+ self ._server = HTTPServer ((self . host , self .port ), MetricsHandler )
281365
282366 def run_server ():
283367 self ._server .serve_forever ()
0 commit comments