Expose usage record metadata fields

This commit is contained in:
RWDai
2026-05-16 15:55:38 +08:00
parent df7b2824a6
commit 97cd92a1a2
3 changed files with 98 additions and 4 deletions

View File

@@ -5,7 +5,7 @@ use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
use crate::handlers::admin::shared::query_param_value;
use crate::GatewayError;
use aether_admin::observability::usage::{
admin_usage_bad_request_response, admin_usage_data_unavailable_response,
admin_usage_bad_request_response, admin_usage_client_family, admin_usage_data_unavailable_response,
admin_usage_has_fallback, admin_usage_is_failed, admin_usage_matches_search,
admin_usage_matches_username, admin_usage_parse_ids, admin_usage_parse_limit,
admin_usage_parse_offset, admin_usage_provider_key_name, admin_usage_record_json,
@@ -263,6 +263,19 @@ fn admin_usage_matches_attempt_status(
}
}
fn admin_usage_matches_client_family(
item: &StoredRequestUsageAudit,
client_family: Option<&str>,
) -> bool {
let Some(client_family) = client_family
.map(str::trim)
.filter(|value| !value.is_empty())
else {
return true;
};
admin_usage_client_family(item).is_some_and(|value| value.eq_ignore_ascii_case(client_family))
}
#[allow(clippy::too_many_arguments)]
fn build_admin_usage_records_response_with_attempt_flags(
items: &[StoredRequestUsageAudit],
@@ -598,6 +611,7 @@ pub(super) async fn maybe_build_local_admin_usage_summary_response(
admin_usage_attempt_status_filter(query_param_value(query, "status").as_deref());
let search = query_param_value(query, "search");
let username_filter = query_param_value(query, "username");
let client_family_filter = query_param_value(query, "client_family");
let limit = match admin_usage_parse_limit(query) {
Ok(value) => value,
Err(detail) => return Ok(Some(admin_usage_bad_request_response(detail))),
@@ -632,7 +646,10 @@ pub(super) async fn maybe_build_local_admin_usage_summary_response(
let active_username_filter = username_filter
.as_deref()
.filter(|value| !value.trim().is_empty());
let (usage, total) = if let Some(attempt_status) = attempt_status_filter {
let active_client_family_filter = client_family_filter
.as_deref()
.filter(|value| !value.trim().is_empty());
let (usage, total) = if attempt_status_filter.is_some() || active_client_family_filter.is_some() {
let mut usage = state.list_usage_audits(&base_query).await?;
let user_ids: Vec<String> = usage
.iter()
@@ -662,12 +679,12 @@ pub(super) async fn maybe_build_local_admin_usage_summary_response(
active_username_filter,
&users_by_id,
state.has_auth_user_data_reader(),
) && admin_usage_matches_attempt_status(
) && attempt_status_filter.is_none_or(|attempt_status| admin_usage_matches_attempt_status(
item,
attempt_status,
&attempt_flags_by_usage_id,
request_candidate_reader_available,
)
)) && admin_usage_matches_client_family(item, active_client_family_filter)
});
sort_usage_newest_first(&mut usage);
let total = usage.len();

View File

@@ -321,6 +321,35 @@ fn users_me_usage_upstream_is_stream(item: &StoredRequestUsageAudit) -> bool {
.unwrap_or(item.is_stream)
}
fn users_me_usage_metadata_string<'a>(
item: &'a StoredRequestUsageAudit,
key: &str,
) -> Option<&'a str> {
item.request_metadata
.as_ref()
.and_then(serde_json::Value::as_object)
.and_then(|metadata| metadata.get(key))
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
}
fn users_me_usage_client_family(item: &StoredRequestUsageAudit) -> Option<&str> {
item.request_metadata
.as_ref()
.and_then(serde_json::Value::as_object)
.and_then(|metadata| {
metadata
.get("client_session_affinity")
.and_then(serde_json::Value::as_object)
.and_then(|affinity| affinity.get("client_family"))
.and_then(serde_json::Value::as_str)
.or_else(|| metadata.get("client_family").and_then(serde_json::Value::as_str))
})
.map(str::trim)
.filter(|value| !value.is_empty())
}
fn build_users_me_usage_record_payload(
item: &StoredRequestUsageAudit,
include_actual_cost: bool,
@@ -353,6 +382,11 @@ fn build_users_me_usage_record_payload(
"upstream_is_stream": upstream_is_stream,
"client_requested_stream": client_is_stream,
"client_is_stream": client_is_stream,
"client_family": users_me_usage_client_family(item),
"client_ip": users_me_usage_metadata_string(item, "client_ip"),
"user_agent": users_me_usage_metadata_string(item, "user_agent"),
"request_path": users_me_usage_metadata_string(item, "request_path"),
"request_path_and_query": users_me_usage_metadata_string(item, "request_path_and_query"),
"status": item.status,
"has_fallback": item.has_fallback(),
"created_at": unix_secs_to_rfc3339(item.created_at_unix_ms),

View File

@@ -998,6 +998,35 @@ fn admin_usage_upstream_is_stream(item: &StoredRequestUsageAudit) -> bool {
.unwrap_or(item.is_stream)
}
fn admin_usage_metadata_string<'a>(
item: &'a StoredRequestUsageAudit,
key: &str,
) -> Option<&'a str> {
item.request_metadata
.as_ref()
.and_then(Value::as_object)
.and_then(|metadata| metadata.get(key))
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
}
pub fn admin_usage_client_family(item: &StoredRequestUsageAudit) -> Option<&str> {
item.request_metadata
.as_ref()
.and_then(Value::as_object)
.and_then(|metadata| {
metadata
.get("client_session_affinity")
.and_then(Value::as_object)
.and_then(|affinity| affinity.get("client_family"))
.and_then(Value::as_str)
.or_else(|| metadata.get("client_family").and_then(Value::as_str))
})
.map(str::trim)
.filter(|value| !value.is_empty())
}
fn admin_usage_active_request_json(
item: &StoredRequestUsageAudit,
api_key_name: Option<String>,
@@ -1030,6 +1059,11 @@ fn admin_usage_active_request_json(
"upstream_is_stream": upstream_is_stream,
"client_requested_stream": client_is_stream,
"client_is_stream": client_is_stream,
"client_family": admin_usage_client_family(item),
"client_ip": admin_usage_metadata_string(item, "client_ip"),
"user_agent": admin_usage_metadata_string(item, "user_agent"),
"request_path": admin_usage_metadata_string(item, "request_path"),
"request_path_and_query": admin_usage_metadata_string(item, "request_path_and_query"),
"has_fallback": admin_usage_has_fallback(item),
});
if let Some(api_format) = item.api_format.as_ref() {
@@ -1131,6 +1165,15 @@ pub fn admin_usage_record_json(
json!(client_is_stream),
);
object.insert("client_is_stream".to_string(), json!(client_is_stream));
maybe_insert_string_field(object, "client_family", admin_usage_client_family(item));
maybe_insert_string_field(object, "client_ip", admin_usage_metadata_string(item, "client_ip"));
maybe_insert_string_field(object, "user_agent", admin_usage_metadata_string(item, "user_agent"));
maybe_insert_string_field(object, "request_path", admin_usage_metadata_string(item, "request_path"));
maybe_insert_string_field(
object,
"request_path_and_query",
admin_usage_metadata_string(item, "request_path_and_query"),
);
payload
}