@@ -152,28 +152,71 @@ class ChatRequest(BaseModel):
152152 messages : List [ChatMessage ]
153153
154154
155- def _resolve_feature_counts (
155+ def _parse_feature_info (
156156 features : Union [List [str ], "feast.FeatureService" ],
157157) -> tuple :
158- """Return (feature_count, feature_view_count) from the resolved features.
158+ """Return ``(feature_view_names, feature_count)`` from resolved features.
159159
160160 ``features`` is either a list of ``"feature_view:feature"`` strings or
161161 a ``FeatureService`` with ``feature_view_projections``.
162+
163+ Returns:
164+ (fv_names, feat_count) where fv_names is a list of unique feature
165+ view name strings and feat_count is the total number of features.
162166 """
163167 from feast .feature_service import FeatureService
168+ from feast .utils import _parse_feature_ref
164169
165170 if isinstance (features , FeatureService ):
166171 projections = features .feature_view_projections
167- fv_count = len ( projections )
172+ fv_names = [ p . name for p in projections ]
168173 feat_count = sum (len (p .features ) for p in projections )
169174 elif isinstance (features , list ):
170175 feat_count = len (features )
171- fv_names = {ref .split (":" )[0 ].split ("@" )[0 ] for ref in features if ":" in ref }
172- fv_count = len (fv_names )
176+ fv_names = list ({_parse_feature_ref (ref )[0 ] for ref in features if ":" in ref })
173177 else :
178+ fv_names = []
174179 feat_count = 0
175- fv_count = 0
176- return str (feat_count ), str (fv_count )
180+ return fv_names , feat_count
181+
182+
183+ def _resolve_feature_counts (
184+ features : Union [List [str ], "feast.FeatureService" ],
185+ ) -> tuple :
186+ """Return ``(feature_count_str, feature_view_count_str)`` for Prometheus labels."""
187+ fv_names , feat_count = _parse_feature_info (features )
188+ return str (feat_count ), str (len (fv_names ))
189+
190+
191+ def _emit_online_audit (
192+ request : GetOnlineFeaturesRequest ,
193+ features : Union [List [str ], "feast.FeatureService" ],
194+ entity_count : int ,
195+ status : str ,
196+ latency_ms : float ,
197+ ):
198+ """Best-effort audit log emission for online feature requests."""
199+ try :
200+ from feast .permissions .security_manager import get_security_manager
201+
202+ requestor_id = "anonymous"
203+ sm = get_security_manager ()
204+ if sm and sm .current_user :
205+ requestor_id = sm .current_user .username or "anonymous"
206+
207+ fv_names , feat_count = _parse_feature_info (features )
208+
209+ feast_metrics .emit_online_audit_log (
210+ requestor_id = requestor_id ,
211+ entity_keys = list (request .entities .keys ()),
212+ entity_count = entity_count ,
213+ feature_views = fv_names ,
214+ feature_count = feat_count ,
215+ status = status ,
216+ latency_ms = latency_ms ,
217+ )
218+ except Exception :
219+ logger .warning ("Failed to emit online audit log" , exc_info = True )
177220
178221
179222async def _get_features (
@@ -390,11 +433,22 @@ async def get_online_features(request: GetOnlineFeaturesRequest) -> Any:
390433 include_feature_view_version_metadata = request .include_feature_view_version_metadata ,
391434 )
392435
393- if store ._get_provider ().async_supported .online .read :
394- response = await store .get_online_features_async (** read_params ) # type: ignore
395- else :
396- response = await run_in_threadpool (
397- lambda : store .get_online_features (** read_params ) # type: ignore
436+ audit_start_ms = time .monotonic () * 1000
437+ audit_status = "success"
438+ try :
439+ if store ._get_provider ().async_supported .online .read :
440+ response = await store .get_online_features_async (** read_params ) # type: ignore
441+ else :
442+ response = await run_in_threadpool (
443+ lambda : store .get_online_features (** read_params ) # type: ignore
444+ )
445+ except Exception :
446+ audit_status = "error"
447+ raise
448+ finally :
449+ audit_latency_ms = time .monotonic () * 1000 - audit_start_ms
450+ _emit_online_audit (
451+ request , features , entity_count , audit_status , audit_latency_ms
398452 )
399453
400454 response_dict = await run_in_threadpool (
0 commit comments