From e5857161ffde222e691002cf5df1589792a7b579 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Sun, 11 Jan 2026 15:31:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(ops):=20=E5=A2=9E=E5=BC=BA=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E8=AF=A6=E6=83=85=E5=BC=B9=E7=AA=97=E4=B8=8EAPI?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **前端改动**: 1. OpsErrorDetailModal.vue: - 新增上游错误详情展示功能 - 支持查看上游错误的请求头、响应体等调试信息 - 改进错误信息格式化与可读性 2. ops.ts API: - 新增getUpstreamErrors接口调用上游错误查询API **后端配置**: - config.go/config.yaml/deploy/config.example.yaml: - 更新配置支持上游错误事件记录开关 - 添加相关配置项文档说明 --- backend/internal/config/config.go | 2 +- config.yaml | 2 +- deploy/config.example.yaml | 2 +- frontend/src/api/admin/ops.ts | 6 ++ .../ops/components/OpsErrorDetailModal.vue | 97 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 579e498a..25c6cb65 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -635,7 +635,7 @@ func setDefaults() { // Gateway viper.SetDefault("gateway.response_header_timeout", 600) // 600秒(10分钟)等待上游响应头,LLM高负载时可能排队较久 - viper.SetDefault("gateway.log_upstream_error_body", false) + viper.SetDefault("gateway.log_upstream_error_body", true) viper.SetDefault("gateway.log_upstream_error_body_max_bytes", 2048) viper.SetDefault("gateway.inject_beta_for_apikey", false) viper.SetDefault("gateway.failover_on_400", false) diff --git a/config.yaml b/config.yaml index 106de2c3..13e7977c 100644 --- a/config.yaml +++ b/config.yaml @@ -159,7 +159,7 @@ gateway: max_line_size: 41943040 # Log upstream error response body summary (safe/truncated; does not log request content) # 记录上游错误响应体摘要(安全/截断;不记录请求内容) - log_upstream_error_body: false + log_upstream_error_body: true # Max bytes to log from upstream error body # 记录上游错误响应体的最大字节数 log_upstream_error_body_max_bytes: 2048 diff --git a/deploy/config.example.yaml b/deploy/config.example.yaml index 87ff3148..7ca26968 100644 --- a/deploy/config.example.yaml +++ b/deploy/config.example.yaml @@ -159,7 +159,7 @@ gateway: max_line_size: 41943040 # Log upstream error response body summary (safe/truncated; does not log request content) # 记录上游错误响应体摘要(安全/截断;不记录请求内容) - log_upstream_error_body: false + log_upstream_error_body: true # Max bytes to log from upstream error body # 记录上游错误响应体的最大字节数 log_upstream_error_body_max_bytes: 2048 diff --git a/frontend/src/api/admin/ops.ts b/frontend/src/api/admin/ops.ts index 42b9e70d..3c39a32b 100644 --- a/frontend/src/api/admin/ops.ts +++ b/frontend/src/api/admin/ops.ts @@ -704,6 +704,12 @@ export interface OpsErrorDetail extends OpsErrorLog { error_body: string user_agent: string + // Upstream context (optional; enriched by gateway services) + upstream_status_code?: number | null + upstream_error_message?: string + upstream_error_detail?: string + upstream_errors?: string + auth_latency_ms?: number | null routing_latency_ms?: number | null upstream_latency_ms?: number | null diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue index f8166040..0726bacd 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue @@ -177,6 +177,81 @@ + +
+

+ {{ t('admin.ops.errorDetails.upstreamErrors') }} +

+ +
+
+
status
+
+ {{ detail.upstream_status_code != null ? detail.upstream_status_code : '—' }} +
+
+
+
message
+
+ {{ detail.upstream_error_message || '—' }} +
+
+
+ +
+
detail
+
{{ prettyJSON(detail.upstream_error_detail) }}
+
+ +
+
upstream_errors
+ +
+
+
+
+ #{{ idx + 1 }} {{ ev.kind }} +
+
+ {{ ev.at_unix_ms ? formatDateTime(new Date(ev.at_unix_ms)) : '' }} +
+
+ +
+
account_id: {{ ev.account_id ?? '—' }}
+
status: {{ ev.upstream_status_code ?? '—' }}
+
+ request_id: {{ ev.upstream_request_id || '—' }} +
+
+ +
+ {{ ev.message }} +
+ +
{{ prettyJSON(ev.detail) }}
+
+
+ +
{{ prettyJSON(detail.upstream_errors) }}
+
+
+
@@ -259,6 +334,28 @@ const title = computed(() => { const emptyText = computed(() => 'No error selected.') +type UpstreamErrorEvent = { + at_unix_ms?: number + platform?: string + account_id?: number + upstream_status_code?: number + upstream_request_id?: string + kind?: string + message?: string + detail?: string +} + +const upstreamErrors = computed(() => { + const raw = detail.value?.upstream_errors + if (!raw) return [] + try { + const parsed = JSON.parse(raw) + return Array.isArray(parsed) ? (parsed as UpstreamErrorEvent[]) : [] + } catch { + return [] + } +}) + function close() { emit('update:show', false) }