Refactor log management and usage analytics

This commit is contained in:
Evan Yang
2026-03-13 15:55:16 +08:00
parent e3bca39815
commit cb22968ff3
4 changed files with 42 additions and 26 deletions
View File
+13 -11
View File
@@ -3,36 +3,38 @@ Logging configuration for Routstr.
CRITICAL LOG MESSAGES FOR USAGE STATISTICS:
===========================================
The following log messages are parsed by the usage tracking system (routstr/core/admin.py).
The following log messages are parsed by the usage tracking system
(routstr/core/usage_analytics_store.py and routstr/core/log_manager.py).
DO NOT modify or remove these messages without updating the usage tracking logic:
1. "Received proxy request" (INFO) - routstr/proxy.py
- Used to count total incoming requests
- Includes model information in context
2. "Payment adjustment completed for streaming" (INFO) - routstr/upstream/base.py
"Payment adjustment completed for non-streaming" (INFO) - routstr/upstream/base.py
2. "Calculated token-based cost" (INFO) - routstr/auth.py
- Used to track successful completions and revenue
- The 'cost_data.total_msats' field is extracted for revenue calculation
- Must include 'cost_data' in extra dict
- The 'token_cost' and 'model' fields are extracted for dashboard metrics
3. "Payment processed successfully" (INFO) - routstr/auth.py
3. "Max cost payment finalized" (INFO) - routstr/auth.py
- Used as the successful completion fallback when token usage is unavailable
- The 'charged_amount' and 'model' fields are extracted for dashboard metrics
4. "Payment processed successfully" (INFO) - routstr/auth.py
- Used to count successful payment processing events
- Tracks payment-related metrics
4. "Upstream request failed, revert payment" (WARNING) - routstr/proxy.py
5. "Upstream request failed, revert payment" (WARNING) - routstr/proxy.py
- Used to track failed requests and refunds
- The 'max_cost_for_model' field is extracted for refund calculation
- Must include 'max_cost_for_model' in extra dict
5. Any ERROR level logs with "upstream" in the message
6. Any ERROR level logs with "upstream" in the message
- Used to count upstream provider errors
- Helps identify service reliability issues
If you need to modify these messages, ensure you also update the parsing logic in:
- routstr/core/admin.py:_aggregate_metrics_by_time()
- routstr/core/admin.py:_get_summary_stats()
- routstr/core/admin.py:get_revenue_by_model()
- routstr/core/usage_analytics_store.py
- routstr/core/log_manager.py
"""
import logging.config
+28 -12
View File
@@ -21,7 +21,7 @@ class UsageAnalyticsStore:
bytes.
"""
SCHEMA_VERSION = "2"
SCHEMA_VERSION = "3"
def __init__(self, logs_dir: Path, db_path: Path | None = None):
self.logs_dir = logs_dir
@@ -473,10 +473,7 @@ class UsageAnalyticsStore:
elif level == "WARNING":
bucket["warnings"] += 1
completed = (
"completed for streaming" in message
or "completed for non-streaming" in message
)
completed, revenue_msats = self._extract_success_metrics(entry, message)
if completed:
bucket["total_requests"] += 1
bucket["successful_chat_completions"] += 1
@@ -484,13 +481,9 @@ class UsageAnalyticsStore:
model_bucket["requests"] += 1
model_bucket["successful"] += 1
cost_data = entry.get("cost_data")
if isinstance(cost_data, dict):
actual_cost = cost_data.get("total_msats", 0)
if isinstance(actual_cost, (int, float)) and actual_cost > 0:
cost_float = float(actual_cost)
bucket["revenue_msats"] += cost_float
model_bucket["revenue_msats"] += cost_float
if revenue_msats > 0:
bucket["revenue_msats"] += revenue_msats
model_bucket["revenue_msats"] += revenue_msats
failed = (
"upstream request failed" in message
@@ -988,6 +981,29 @@ class UsageAnalyticsStore:
return None
return f"{timestamp[:16]}:00"
def _extract_success_metrics(
self, entry: dict[str, Any], message: str
) -> tuple[bool, float]:
# These auth logs are emitted once per successful settlement across providers
# and avoid duplicate counting from provider-specific completion logs.
logger_name = str(entry.get("name", ""))
if not logger_name.startswith("routstr.auth"):
return False, 0.0
if "calculated token-based cost" in message:
token_cost = entry.get("token_cost", 0)
if isinstance(token_cost, (int, float)) and token_cost > 0:
return True, float(token_cost)
return True, 0.0
if "max cost payment finalized" in message:
charged_amount = entry.get("charged_amount", 0)
if isinstance(charged_amount, (int, float)) and charged_amount > 0:
return True, float(charged_amount)
return True, 0.0
return False, 0.0
def _new_minute_stats(self) -> dict[str, float]:
return {
"total_entries": 0.0,
+1 -3
View File
@@ -66,9 +66,7 @@ function normalizeBaseUrl(url: string): string {
}
export function CheatSheet(): JSX.Element {
const [baseUrl, setBaseUrl] = useState(() =>
typeof window === 'undefined' ? '' : ConfigurationService.getLocalBaseUrl()
);
const [baseUrl, setBaseUrl] = useState('');
const [apiKeyInput, setApiKeyInput] = useState('');
const [walletInfo, setWalletInfo] = useState<WalletSnapshot | null>(null);
const [refundReceipt, setRefundReceipt] = useState<RefundReceipt | null>(