55Metrics tracking for OGR API calls.
66
77This module provides a lightweight metrics tracking system to count
8- API calls per service type and namespace. Metrics are stored in-memory
8+ API calls per instance URL and namespace. Metrics are stored in-memory
99and are NOT thread-safe or process-synchronized.
1010
1111The track_ogr_request decorator can be applied to GitProject methods
12- to automatically track API calls.
12+ to automatically track API calls. The instance URL uniquely identifies
13+ the service instance (e.g., github.com, gitlab.com, gitlab.example.com).
1314"""
1415
16+ from __future__ import annotations
17+
1518import functools
1619import logging
1720from collections import defaultdict
18- from typing import Any , Callable , TypeVar , cast
21+ from typing import TYPE_CHECKING , Any , Callable , TypeVar , cast
22+
23+ if TYPE_CHECKING :
24+ from ogr .abstract import GitProject
1925
2026logger = logging .getLogger (__name__ )
2127
2430
2531class RequestMetricsTracker :
2632 """
27- Tracker for counting API calls per namespace and service type .
33+ Tracker for counting API calls per instance URL and namespace .
2834
2935 Note: This class is NOT thread-safe and does NOT synchronize across processes.
3036 In a multi-worker environment, each worker maintains its own metrics, and some
@@ -38,25 +44,25 @@ def __init__(self):
3844
3945 def record_request (
4046 self ,
41- service_type : str ,
47+ instance_url : str ,
4248 namespace : str ,
4349 ) -> None :
4450 """
45- Record an API request for a given service type and namespace.
51+ Record an API request for a given instance URL and namespace.
4652
4753 Args:
48- service_type : The service type (e.g., "github", "gitlab", "pagure ")
54+ instance_url : The service instance URL (e.g., "https:// github.com ", "https:// gitlab.com ")
4955 namespace: The namespace (e.g., "packit", "rpms")
5056 """
51- key = (service_type , namespace )
57+ key = (instance_url , namespace )
5258 self ._counts [key ] += 1
5359
5460 def get_all_counts (self ) -> dict [tuple [str , str ], int ]:
5561 """
5662 Get all request counts.
5763
5864 Returns:
59- Dictionary mapping (service_type , namespace) tuples to counts.
65+ Dictionary mapping (instance_url , namespace) tuples to counts.
6066 """
6167 return {key : count for key , count in self ._counts .items () if count > 0 }
6268
@@ -78,60 +84,58 @@ def get_metrics_tracker() -> RequestMetricsTracker:
7884 return _metrics_tracker
7985
8086
81- def record_ogr_request (service_type : str , namespace : str ) -> None :
87+ def record_ogr_request (instance_url : str , namespace : str ) -> None :
8288 """
8389 Record an ogr API request.
8490
8591 This is a convenience function that uses the global metrics tracker.
8692
8793 Args:
88- service_type : The service type (e.g., "github", "gitlab", "pagure ")
94+ instance_url : The service instance URL (e.g., "https:// github.com ", "https:// gitlab.com ")
8995 namespace: The namespace (e.g., "packit-service", "rpms")
9096 """
91- _metrics_tracker .record_request (service_type , namespace )
97+ _metrics_tracker .record_request (instance_url , namespace )
9298
9399
94- def track_ogr_request (service_type : str ) -> Callable [[ F ], F ] :
100+ def track_ogr_request (func : F ) -> F :
95101 """
96102 Decorator to track ogr API method calls.
97103
98104 The decorated method must be called on a GitProject instance
99- (which has `namespace` and `service` attributes).
105+ (which has `namespace`, `repo`, and `service` attributes).
100106
101107 For Pagure projects, the namespace is combined with the repo name
102108 (e.g., "rpms/python-requests") to provide more granular metrics.
103109
104- Args:
105- service_type: The service type (e.g., "github", "gitlab", "pagure")
110+ The instance URL is extracted from the service to distinguish between
111+ different instances (e.g., multiple GitLab or Forgejo instances).
106112
107113 Example:
108- @track_ogr_request("github")
114+ @track_ogr_request
109115 def get_issues(self):
110116 ...
111117 """
112118
113- def decorator (func : F ) -> F :
114- @functools .wraps (func )
115- def wrapper (self : object , * args : Any , ** kwargs : Any ) -> Any :
116- namespace = getattr (self , "namespace" , None )
117- if namespace :
118- try :
119- # For Pagure, append the repo name to the namespace
120- # to get more granular metrics (e.g., "rpms/python-requests")
121- if service_type == "pagure" :
122- repo = getattr (self , "repo" , None )
123- if repo :
124- namespace = f"{ namespace } /{ repo } "
119+ @functools .wraps (func )
120+ def wrapper (self : GitProject , * args : Any , ** kwargs : Any ) -> Any :
121+ try :
122+ from ogr .services .pagure import PagureService
123+
124+ namespace = self .namespace
125+ instance_url = self .service .instance_url
125126
126- record_ogr_request (service_type , namespace )
127- except Exception as e :
128- logger .debug (f"Failed to record metrics: { e } " )
127+ # For Pagure, append the repo name to the namespace
128+ # to get more granular metrics (e.g., "rpms/python-requests")
129+ if isinstance (self .service , PagureService ) and self .repo :
130+ namespace = f"{ namespace } /{ self .repo } "
129131
130- return func (self , * args , ** kwargs )
132+ record_ogr_request (instance_url , namespace )
133+ except Exception as e :
134+ logger .debug (f"Failed to record metrics: { e } " )
131135
132- return cast ( F , wrapper )
136+ return func ( self , * args , ** kwargs )
133137
134- return decorator
138+ return cast ( F , wrapper )
135139
136140
137141__all__ = [
0 commit comments