fix(ops): 优化错误日志过滤和查询逻辑
后端改动: - 添加 resolved 参数默认值处理(向后兼容,默认显示未解决错误) - 新增 status_codes_other 查询参数支持 - 移除 service 层的高级设置过滤逻辑,简化错误日志查询流程 前端改动: - 完善错误日志相关组件的国际化支持 - 优化 Ops 监控面板和设置对话框的用户体验
This commit is contained in:
@@ -110,6 +110,12 @@ func (h *OpsHandler) GetErrorLogs(c *gin.Context) {
|
||||
filter.Source = source
|
||||
}
|
||||
filter.View = parseOpsViewParam(c)
|
||||
|
||||
// Legacy endpoint default: unresolved only (backward-compatible).
|
||||
{
|
||||
b := false
|
||||
filter.Resolved = &b
|
||||
}
|
||||
if v := strings.TrimSpace(c.Query("resolved")); v != "" {
|
||||
switch strings.ToLower(v) {
|
||||
case "1", "true", "yes":
|
||||
@@ -143,6 +149,17 @@ func (h *OpsHandler) GetErrorLogs(c *gin.Context) {
|
||||
}
|
||||
filter.StatusCodes = out
|
||||
}
|
||||
if v := strings.TrimSpace(c.Query("status_codes_other")); v != "" {
|
||||
switch strings.ToLower(v) {
|
||||
case "1", "true", "yes":
|
||||
filter.StatusCodesOther = true
|
||||
case "0", "false", "no":
|
||||
filter.StatusCodesOther = false
|
||||
default:
|
||||
response.BadRequest(c, "Invalid status_codes_other")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err := h.opsService.GetErrorLogs(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
|
||||
@@ -132,7 +132,6 @@ func (r *opsRepository) ListErrorLogs(ctx context.Context, filter *service.OpsEr
|
||||
pageSize = 500
|
||||
}
|
||||
|
||||
// buildOpsErrorLogsWhere may mutate filter (default resolved filter).
|
||||
where, args := buildOpsErrorLogsWhere(filter)
|
||||
countSQL := "SELECT COUNT(*) FROM ops_error_logs e " + where
|
||||
|
||||
@@ -933,15 +932,11 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
|
||||
}
|
||||
// ops_error_logs stores client-visible error requests (status>=400),
|
||||
// but we also persist "recovered" upstream errors (status<400) for upstream health visibility.
|
||||
// By default, keep list endpoints scoped to unresolved records if the caller didn't specify.
|
||||
// If Resolved is not specified, do not filter by resolved state (backward-compatible).
|
||||
resolvedFilter := (*bool)(nil)
|
||||
if filter != nil {
|
||||
resolvedFilter = filter.Resolved
|
||||
}
|
||||
if resolvedFilter == nil {
|
||||
f := false
|
||||
resolvedFilter = &f
|
||||
}
|
||||
// Keep list endpoints scoped to client errors unless explicitly filtering upstream phase.
|
||||
if phaseFilter != "upstream" {
|
||||
clauses = append(clauses, "COALESCE(status_code, 0) >= 400")
|
||||
@@ -1007,6 +1002,11 @@ func buildOpsErrorLogsWhere(filter *service.OpsErrorLogFilter) (string, []any) {
|
||||
if len(filter.StatusCodes) > 0 {
|
||||
args = append(args, pq.Array(filter.StatusCodes))
|
||||
clauses = append(clauses, "COALESCE(upstream_status_code, status_code, 0) = ANY($"+itoa(len(args))+")")
|
||||
} else if filter.StatusCodesOther {
|
||||
// "Other" means: status codes not in the common list.
|
||||
known := []int{400, 401, 403, 404, 409, 422, 429, 500, 502, 503, 504, 529}
|
||||
args = append(args, pq.Array(known))
|
||||
clauses = append(clauses, "NOT (COALESCE(upstream_status_code, status_code, 0) = ANY($"+itoa(len(args))+"))")
|
||||
}
|
||||
if q := strings.TrimSpace(filter.Query); q != "" {
|
||||
like := "%" + q + "%"
|
||||
|
||||
@@ -86,12 +86,13 @@ type OpsErrorLogFilter struct {
|
||||
GroupID *int64
|
||||
AccountID *int64
|
||||
|
||||
StatusCodes []int
|
||||
Phase string
|
||||
Owner string
|
||||
Source string
|
||||
Resolved *bool
|
||||
Query string
|
||||
StatusCodes []int
|
||||
StatusCodesOther bool
|
||||
Phase string
|
||||
Owner string
|
||||
Source string
|
||||
Resolved *bool
|
||||
Query string
|
||||
|
||||
// View controls error categorization for list endpoints.
|
||||
// - errors: show actionable errors (exclude business-limited / 429 / 529)
|
||||
|
||||
@@ -261,64 +261,9 @@ func (s *OpsService) GetErrorLogs(ctx context.Context, filter *OpsErrorLogFilter
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply error filtering based on settings (for historical data)
|
||||
result = s.filterErrorLogsBySettings(ctx, result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// filterErrorLogsBySettings filters error logs based on advanced settings.
|
||||
// This ensures that historical errors are also filtered when viewing the dashboard.
|
||||
func (s *OpsService) filterErrorLogsBySettings(ctx context.Context, result *OpsErrorLogList) *OpsErrorLogList {
|
||||
if result == nil || len(result.Errors) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
settings, err := s.GetOpsAdvancedSettings(ctx)
|
||||
if err != nil || settings == nil {
|
||||
// If we can't get settings, return unfiltered (fail open)
|
||||
return result
|
||||
}
|
||||
|
||||
filtered := make([]*OpsErrorLog, 0, len(result.Errors))
|
||||
for _, errLog := range result.Errors {
|
||||
if shouldFilterErrorLog(settings, errLog) {
|
||||
continue // Skip this error
|
||||
}
|
||||
filtered = append(filtered, errLog)
|
||||
}
|
||||
|
||||
// Update total count to reflect filtered results
|
||||
result.Errors = filtered
|
||||
result.Total = len(filtered)
|
||||
return result
|
||||
}
|
||||
|
||||
// shouldFilterErrorLog determines if an error log should be filtered based on settings.
|
||||
func shouldFilterErrorLog(settings *OpsAdvancedSettings, errLog *OpsErrorLog) bool {
|
||||
if settings == nil || errLog == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
msgLower := strings.ToLower(errLog.Message)
|
||||
|
||||
// Check if count_tokens errors should be ignored
|
||||
if settings.IgnoreCountTokensErrors && strings.Contains(errLog.RequestPath, "/count_tokens") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if context canceled errors should be ignored
|
||||
if settings.IgnoreContextCanceled && strings.Contains(msgLower, "context canceled") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if "no available accounts" errors should be ignored
|
||||
if settings.IgnoreNoAvailableAccounts && strings.Contains(msgLower, "no available accounts") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *OpsService) GetErrorLogByID(ctx context.Context, id int64) (*OpsErrorLogDetail, error) {
|
||||
if err := s.RequireMonitoringEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user