feat: 日志增强
This commit is contained in:
@@ -80,11 +80,13 @@ type RequestLog struct {
|
|||||||
IsSuccess bool `gorm:"not null" json:"is_success"`
|
IsSuccess bool `gorm:"not null" json:"is_success"`
|
||||||
SourceIP string `gorm:"type:varchar(45)" json:"source_ip"`
|
SourceIP string `gorm:"type:varchar(45)" json:"source_ip"`
|
||||||
StatusCode int `gorm:"not null" json:"status_code"`
|
StatusCode int `gorm:"not null" json:"status_code"`
|
||||||
RequestPath string `gorm:"type:varchar(1024)" json:"request_path"`
|
RequestPath string `gorm:"type:varchar(500)" json:"request_path"`
|
||||||
Duration int64 `gorm:"not null" json:"duration_ms"`
|
Duration int64 `gorm:"not null" json:"duration_ms"`
|
||||||
ErrorMessage string `gorm:"type:text" json:"error_message"`
|
ErrorMessage string `gorm:"type:text" json:"error_message"`
|
||||||
UserAgent string `gorm:"type:varchar(512)" json:"user_agent"`
|
UserAgent string `gorm:"type:varchar(512)" json:"user_agent"`
|
||||||
Retries int `gorm:"not null" json:"retries"`
|
Retries int `gorm:"not null" json:"retries"`
|
||||||
|
UpstreamAddr string `gorm:"type:varchar(500)" json:"upstream_addr"`
|
||||||
|
IsStream bool `gorm:"not null" json:"is_stream"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatCard 用于仪表盘的单个统计卡片数据
|
// StatCard 用于仪表盘的单个统计卡片数据
|
||||||
|
@@ -116,11 +116,11 @@ func (ps *ProxyServer) executeRequestWithRetry(
|
|||||||
logrus.Debugf("Max retries exceeded for group %s after %d attempts. Parsed Error: %s", group.Name, retryCount, logMessage)
|
logrus.Debugf("Max retries exceeded for group %s after %d attempts. Parsed Error: %s", group.Name, retryCount, logMessage)
|
||||||
|
|
||||||
keyID, _ := strconv.ParseUint(lastError.KeyID, 10, 64)
|
keyID, _ := strconv.ParseUint(lastError.KeyID, 10, 64)
|
||||||
ps.logRequest(c, group, uint(keyID), startTime, lastError.StatusCode, retryCount, errors.New(logMessage))
|
ps.logRequest(c, group, uint(keyID), startTime, lastError.StatusCode, retryCount, errors.New(logMessage), isStream, lastError.UpstreamAddr)
|
||||||
} else {
|
} else {
|
||||||
response.Error(c, app_errors.ErrMaxRetriesExceeded)
|
response.Error(c, app_errors.ErrMaxRetriesExceeded)
|
||||||
logrus.Debugf("Max retries exceeded for group %s after %d attempts.", group.Name, retryCount)
|
logrus.Debugf("Max retries exceeded for group %s after %d attempts.", group.Name, retryCount)
|
||||||
ps.logRequest(c, group, 0, startTime, http.StatusServiceUnavailable, retryCount, app_errors.ErrMaxRetriesExceeded)
|
ps.logRequest(c, group, 0, startTime, http.StatusServiceUnavailable, retryCount, app_errors.ErrMaxRetriesExceeded, isStream, "")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ func (ps *ProxyServer) executeRequestWithRetry(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Failed to select a key for group %s on attempt %d: %v", group.Name, retryCount+1, err)
|
logrus.Errorf("Failed to select a key for group %s on attempt %d: %v", group.Name, retryCount+1, err)
|
||||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrNoKeysAvailable, err.Error()))
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrNoKeysAvailable, err.Error()))
|
||||||
ps.logRequest(c, group, 0, startTime, http.StatusServiceUnavailable, retryCount, err)
|
ps.logRequest(c, group, 0, startTime, http.StatusServiceUnavailable, retryCount, err, isStream, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ func (ps *ProxyServer) executeRequestWithRetry(
|
|||||||
if err != nil || (resp != nil && resp.StatusCode >= 400) {
|
if err != nil || (resp != nil && resp.StatusCode >= 400) {
|
||||||
if err != nil && app_errors.IsIgnorableError(err) {
|
if err != nil && app_errors.IsIgnorableError(err) {
|
||||||
logrus.Debugf("Client-side ignorable error for key %s, aborting retries: %v", utils.MaskAPIKey(apiKey.KeyValue), err)
|
logrus.Debugf("Client-side ignorable error for key %s, aborting retries: %v", utils.MaskAPIKey(apiKey.KeyValue), err)
|
||||||
ps.logRequest(c, group, apiKey.ID, startTime, 499, retryCount+1, err)
|
ps.logRequest(c, group, apiKey.ID, startTime, 499, retryCount+1, err, isStream, upstreamURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +212,7 @@ func (ps *ProxyServer) executeRequestWithRetry(
|
|||||||
ParsedErrorMessage: parsedError,
|
ParsedErrorMessage: parsedError,
|
||||||
KeyID: fmt.Sprintf("%d", apiKey.ID),
|
KeyID: fmt.Sprintf("%d", apiKey.ID),
|
||||||
Attempt: retryCount + 1,
|
Attempt: retryCount + 1,
|
||||||
|
UpstreamAddr: upstreamURL,
|
||||||
})
|
})
|
||||||
ps.executeRequestWithRetry(c, channelHandler, group, bodyBytes, isStream, startTime, retryCount+1, newRetryErrors)
|
ps.executeRequestWithRetry(c, channelHandler, group, bodyBytes, isStream, startTime, retryCount+1, newRetryErrors)
|
||||||
return
|
return
|
||||||
@@ -219,7 +220,7 @@ func (ps *ProxyServer) executeRequestWithRetry(
|
|||||||
|
|
||||||
ps.keyProvider.UpdateStatus(apiKey, group, true)
|
ps.keyProvider.UpdateStatus(apiKey, group, true)
|
||||||
logrus.Debugf("Request for group %s succeeded on attempt %d with key %s", group.Name, retryCount+1, utils.MaskAPIKey(apiKey.KeyValue))
|
logrus.Debugf("Request for group %s succeeded on attempt %d with key %s", group.Name, retryCount+1, utils.MaskAPIKey(apiKey.KeyValue))
|
||||||
ps.logRequest(c, group, apiKey.ID, startTime, resp.StatusCode, retryCount+1, nil)
|
ps.logRequest(c, group, apiKey.ID, startTime, resp.StatusCode, retryCount+1, nil, isStream, upstreamURL)
|
||||||
|
|
||||||
for key, values := range resp.Header {
|
for key, values := range resp.Header {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
@@ -244,6 +245,8 @@ func (ps *ProxyServer) logRequest(
|
|||||||
statusCode int,
|
statusCode int,
|
||||||
retries int,
|
retries int,
|
||||||
finalError error,
|
finalError error,
|
||||||
|
isStream bool,
|
||||||
|
upstreamAddr string,
|
||||||
) {
|
) {
|
||||||
if ps.requestLogService == nil {
|
if ps.requestLogService == nil {
|
||||||
return
|
return
|
||||||
@@ -252,15 +255,17 @@ func (ps *ProxyServer) logRequest(
|
|||||||
duration := time.Since(startTime).Milliseconds()
|
duration := time.Since(startTime).Milliseconds()
|
||||||
|
|
||||||
logEntry := &models.RequestLog{
|
logEntry := &models.RequestLog{
|
||||||
GroupID: group.ID,
|
GroupID: group.ID,
|
||||||
KeyID: keyID,
|
KeyID: keyID,
|
||||||
IsSuccess: finalError == nil && statusCode < 400,
|
IsSuccess: finalError == nil && statusCode < 400,
|
||||||
SourceIP: c.ClientIP(),
|
SourceIP: c.ClientIP(),
|
||||||
StatusCode: statusCode,
|
StatusCode: statusCode,
|
||||||
RequestPath: c.Request.URL.String(),
|
RequestPath: utils.TruncateString(c.Request.URL.String(), 500),
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
UserAgent: c.Request.UserAgent(),
|
UserAgent: c.Request.UserAgent(),
|
||||||
Retries: retries,
|
Retries: retries,
|
||||||
|
IsStream: isStream,
|
||||||
|
UpstreamAddr: utils.TruncateString(upstreamAddr, 500),
|
||||||
}
|
}
|
||||||
|
|
||||||
if finalError != nil {
|
if finalError != nil {
|
||||||
|
@@ -85,4 +85,5 @@ type RetryError struct {
|
|||||||
ParsedErrorMessage string `json:"-"`
|
ParsedErrorMessage string `json:"-"`
|
||||||
KeyID string `json:"key_id"`
|
KeyID string `json:"key_id"`
|
||||||
Attempt int `json:"attempt"`
|
Attempt int `json:"attempt"`
|
||||||
|
UpstreamAddr string `json:"-"`
|
||||||
}
|
}
|
||||||
|
@@ -10,3 +10,11 @@ func MaskAPIKey(key string) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s****%s", key[:4], key[length-4:])
|
return fmt.Sprintf("%s****%s", key[:4], key[length-4:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TruncateString shortens a string to a maximum length.
|
||||||
|
func TruncateString(s string, maxLength int) string {
|
||||||
|
if len(s) > maxLength {
|
||||||
|
return s[:maxLength]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
@@ -112,8 +112,20 @@ const createColumns = () => [
|
|||||||
{ default: () => (row.is_success ? "成功" : "失败") }
|
{ default: () => (row.is_success ? "成功" : "失败") }
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "类型",
|
||||||
|
key: "is_stream",
|
||||||
|
width: 80,
|
||||||
|
render: (row: LogRow) =>
|
||||||
|
h(
|
||||||
|
NTag,
|
||||||
|
{ type: row.is_stream ? "info" : "default", size: "small", round: true },
|
||||||
|
{ default: () => (row.is_stream ? "流式" : "非流") }
|
||||||
|
),
|
||||||
|
},
|
||||||
{ title: "状态码", key: "status_code", width: 80 },
|
{ title: "状态码", key: "status_code", width: 80 },
|
||||||
{ title: "耗时(ms)", key: "duration_ms", width: 100 },
|
{ title: "耗时(ms)", key: "duration_ms", width: 100 },
|
||||||
|
{ title: "重试", key: "retries", width: 60 },
|
||||||
{ title: "分组", key: "group_name", width: 120 },
|
{ title: "分组", key: "group_name", width: 120 },
|
||||||
{
|
{
|
||||||
title: "Key",
|
title: "Key",
|
||||||
@@ -142,6 +154,12 @@ const createColumns = () => [
|
|||||||
render: (row: LogRow) =>
|
render: (row: LogRow) =>
|
||||||
h(NEllipsis, { style: "max-width: 200px" }, { default: () => row.request_path }),
|
h(NEllipsis, { style: "max-width: 200px" }, { default: () => row.request_path }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "上游地址",
|
||||||
|
key: "upstream_addr",
|
||||||
|
render: (row: LogRow) =>
|
||||||
|
h(NEllipsis, { style: "max-width: 200px" }, { default: () => row.upstream_addr }),
|
||||||
|
},
|
||||||
{ title: "源IP", key: "source_ip", width: 130 },
|
{ title: "源IP", key: "source_ip", width: 130 },
|
||||||
{
|
{
|
||||||
title: "错误信息",
|
title: "错误信息",
|
||||||
@@ -149,6 +167,12 @@ const createColumns = () => [
|
|||||||
render: (row: LogRow) =>
|
render: (row: LogRow) =>
|
||||||
h(NEllipsis, { style: "max-width: 250px" }, { default: () => row.error_message || "-" }),
|
h(NEllipsis, { style: "max-width: 250px" }, { default: () => row.error_message || "-" }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "User Agent",
|
||||||
|
key: "user_agent",
|
||||||
|
render: (row: LogRow) =>
|
||||||
|
h(NEllipsis, { style: "max-width: 200px" }, { default: () => row.user_agent }),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const columns = createColumns();
|
const columns = createColumns();
|
||||||
|
@@ -105,6 +105,8 @@ export interface RequestLog {
|
|||||||
retries: number;
|
retries: number;
|
||||||
group_name?: string;
|
group_name?: string;
|
||||||
key_value?: string;
|
key_value?: string;
|
||||||
|
upstream_addr: string;
|
||||||
|
is_stream: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Pagination {
|
export interface Pagination {
|
||||||
|
Reference in New Issue
Block a user