diff --git a/common/metrics/errorclassify.go b/common/metrics/errorclassify.go new file mode 100644 index 0000000..d5f896a --- /dev/null +++ b/common/metrics/errorclassify.go @@ -0,0 +1,53 @@ +package metrics + +import ( + "strconv" + "strings" +) + +// ClassifyError 根据状态码和错误消息分类错误 +func ClassifyError(statusCode int, errMsg string) (string, string) { + // 超时类 + if strings.Contains(errMsg, "context deadline exceeded") { + return "timeout", "context_deadline_exceeded" + } + if strings.Contains(errMsg, "context canceled") { + return "timeout", "context_canceled" + } + + // 配额/计费类 + if statusCode == 402 { + return "billing", "insufficient_quota" + } + if statusCode == 403 && strings.Contains(strings.ToLower(errMsg), "quota") { + return "billing", "quota_exceeded" + } + + // 认证类 + if statusCode == 401 { + return "auth", "401" + } + if statusCode == 403 && !strings.Contains(strings.ToLower(errMsg), "quota") { + return "auth", "403" + } + + // 速率限制 + if statusCode == 429 { + return "rate_limit", "429" + } + + // 上游错误(5xx) + if statusCode >= 500 { + return "upstream", strconv.Itoa(statusCode) + } + if statusCode >= 400 { + return "other", strconv.Itoa(statusCode) + } + + // 无状态码的未知错误 + if statusCode == 0 { + return "other", "unknown" + } + + return "other", strconv.Itoa(statusCode) +} diff --git a/common/metrics/errorclassify_test.go b/common/metrics/errorclassify_test.go new file mode 100644 index 0000000..0c525d1 --- /dev/null +++ b/common/metrics/errorclassify_test.go @@ -0,0 +1,85 @@ +package metrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestClassifyError_RateLimitEmptyMsg(t *testing.T) { + errorType, errorCode := ClassifyError(429, "") + assert.Equal(t, "rate_limit", errorType) + assert.Equal(t, "429", errorCode) +} + +func TestClassifyError_UpstreamHTTP502(t *testing.T) { + errorType, errorCode := ClassifyError(502, "") + assert.Equal(t, "upstream", errorType) + assert.Equal(t, "502", errorCode) +} + +func TestClassifyError_UpstreamHTTP500(t *testing.T) { + errorType, errorCode := ClassifyError(500, "") + assert.Equal(t, "upstream", errorType) + assert.Equal(t, "500", errorCode) +} + +func TestClassifyError_Timeout(t *testing.T) { + errorType, errorCode := ClassifyError(0, "context deadline exceeded") + assert.Equal(t, "timeout", errorType) + assert.Equal(t, "context_deadline_exceeded", errorCode) +} + +func TestClassifyError_TimeoutCanceled(t *testing.T) { + errorType, errorCode := ClassifyError(0, "context canceled") + assert.Equal(t, "timeout", errorType) + assert.Equal(t, "context_canceled", errorCode) +} + +func TestClassifyError_AuthInvalidToken(t *testing.T) { + errorType, errorCode := ClassifyError(401, "invalid api key") + assert.Equal(t, "auth", errorType) + assert.Equal(t, "401", errorCode) +} + +func TestClassifyError_AuthForbidden(t *testing.T) { + errorType, errorCode := ClassifyError(403, "") + assert.Equal(t, "auth", errorType) + assert.Equal(t, "403", errorCode) +} + +func TestClassifyError_RateLimit(t *testing.T) { + errorType, errorCode := ClassifyError(429, "rate limit exceeded") + assert.Equal(t, "rate_limit", errorType) + assert.Equal(t, "429", errorCode) +} + +func TestClassifyError_BillingInsufficientQuota(t *testing.T) { + errorType, errorCode := ClassifyError(402, "insufficient quota") + assert.Equal(t, "billing", errorType) + assert.Equal(t, "insufficient_quota", errorCode) +} + +func TestClassifyError_BillingQuotaExceeded(t *testing.T) { + errorType, errorCode := ClassifyError(403, "quota exceeded") + assert.Equal(t, "billing", errorType) + assert.Equal(t, "quota_exceeded", errorCode) +} + +func TestClassifyError_Other(t *testing.T) { + errorType, errorCode := ClassifyError(0, "something unexpected") + assert.Equal(t, "other", errorType) + assert.Equal(t, "unknown", errorCode) +} + +func TestClassifyError_BadRequest(t *testing.T) { + errorType, errorCode := ClassifyError(400, "") + assert.Equal(t, "other", errorType) + assert.Equal(t, "400", errorCode) +} + +func TestClassifyError_EmptyBoth(t *testing.T) { + errorType, errorCode := ClassifyError(0, "") + assert.Equal(t, "other", errorType) + assert.Equal(t, "unknown", errorCode) +}