(UI + SpendLogs) - Store SpendLogs in UTC Timezone, Fix filtering logs by start/end time (#8190)

* fix request_id field

* spend logs store time in UTC

* fix ui_view_spend_logs

* UI make time filter queries in UTC

* fix time filters

* fix TimeCellProps

* ui use UTC for filtering time
This commit is contained in:
Ishaan Jaff 2025-02-01 17:26:18 -08:00 • committed by GitHub
parent c0f3100934
commit 8ba60bf13c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 71 additions and 18 deletions

View file

@ -1,7 +1,7 @@
#### SPEND MANAGEMENT #####
import collections
import os
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from typing import TYPE_CHECKING, Any, List, Optional
import fastapi
@ -1688,13 +1688,18 @@ async def ui_view_spend_logs( # noqa: PLR0915
)
try:
# Convert the date strings to datetime objects
start_date_obj = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
end_date_obj = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
start_date_obj = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S").replace(
tzinfo=timezone.utc
)
end_date_obj = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S").replace(
tzinfo=timezone.utc
)
# Convert to ISO format strings for Prisma
start_date_iso = start_date_obj.isoformat() + "Z" # Add Z to indicate UTC
end_date_iso = end_date_obj.isoformat() + "Z" # Add Z to indicate UTC
start_date_iso = start_date_obj.isoformat() # Already in UTC, no need to add Z
end_date_iso = end_date_obj.isoformat() # Already in UTC, no need to add Z
# Build where conditions
where_conditions: dict[str, Any] = {

View file

@ -1,6 +1,8 @@
import json
import secrets
from datetime import datetime
from datetime import datetime as dt
from datetime import timezone
from typing import Optional, cast
from pydantic import BaseModel
@ -153,9 +155,9 @@ def get_logging_payload( # noqa: PLR0915
call_type=call_type or "",
api_key=str(api_key),
cache_hit=str(cache_hit),
startTime=start_time,
endTime=end_time,
completionStartTime=completion_start_time,
startTime=_ensure_datetime_utc(start_time),
endTime=_ensure_datetime_utc(end_time),
completionStartTime=_ensure_datetime_utc(completion_start_time),
model=kwargs.get("model", "") or "",
user=kwargs.get("litellm_params", {})
.get("metadata", {})
@ -195,6 +197,12 @@ def get_logging_payload( # noqa: PLR0915
raise e
def _ensure_datetime_utc(timestamp: datetime) -> datetime:
"""Helper to ensure datetime is in UTC"""
timestamp = timestamp.astimezone(timezone.utc)
return timestamp
async def get_spend_by_team_and_customer(
start_date: dt,
end_date: dt,

View file

@ -5,6 +5,7 @@ import React from "react";
import { CountryCell } from "./country_cell";
import { getProviderLogoAndName } from "../provider_info_helpers";
import { Tooltip } from "antd";
import { TimeCell } from "./time_cell";
export type LogEntry = {
request_id: string;
@ -53,17 +54,17 @@ export const columns: ColumnDef<LogEntry>[] = [
{
header: "Time",
accessorKey: "startTime",
cell: (info: any) => (
<span>{moment(info.getValue()).format("MMM DD HH:mm:ss")}</span>
),
cell: (info: any) => <TimeCell utcTime={info.getValue()} />,
},
{
header: "Request ID",
accessorKey: "request_id",
cell: (info: any) => (
<span className="font-mono text-xs max-w-[100px] truncate block">
{String(info.getValue() || "")}
</span>
<Tooltip title={String(info.getValue() || "")}>
<span className="font-mono text-xs max-w-[100px] truncate block">
{String(info.getValue() || "")}
</span>
</Tooltip>
),
},
{

View file

@ -105,10 +105,11 @@ export default function SpendLogsTable({
};
}
const formattedStartTime = moment(startTime).format("YYYY-MM-DD HH:mm:ss");
// Convert times to UTC before formatting
const formattedStartTime = moment(startTime).utc().format("YYYY-MM-DD HH:mm:ss");
const formattedEndTime = isCustomDate
? moment(endTime).format("YYYY-MM-DD HH:mm:ss")
: moment().format("YYYY-MM-DD HH:mm:ss");
? moment(endTime).utc().format("YYYY-MM-DD HH:mm:ss")
: moment().utc().format("YYYY-MM-DD HH:mm:ss");
return await uiSpendLogsCall(
accessToken,
@ -176,7 +177,7 @@ export default function SpendLogsTable({
<h1 className="text-xl font-semibold">Request Logs</h1>
</div>
<div className="bg-white rounded-lg shadow overflow-hidden">
<div className="bg-white rounded-lg shadow">
<div className="border-b px-6 py-4">
<div className="flex flex-col md:flex-row items-start md:items-center justify-between space-y-4 md:space-y-0">
<div className="flex flex-wrap items-center gap-3">

View file

@ -0,0 +1,38 @@
import * as React from "react";
interface TimeCellProps {
utcTime: string;
}
const getLocalTime = (utcTime: string): string => {
try {
const date = new Date(utcTime);
return date.toLocaleString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: true
}).replace(',', '');
} catch (e) {
return "Error converting time";
}
};
export const TimeCell: React.FC<TimeCellProps> = ({ utcTime }) => {
return (
<span style={{
fontFamily: 'monospace',
width: '180px',
display: 'inline-block'
}}>
{getLocalTime(utcTime)}
</span>
);
};
export const getTimeZone = (): string => {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
};