Compare commits
2
Commits
b93ede29e0
...
bcbd796fa5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcbd796fa5 | ||
|
|
8592843bdf |
@@ -244,11 +244,49 @@ curl.exe -v -X POST "https://your-app.zeabur.app/v1/audio/speech" -H "Content-Ty
|
||||
另外 PowerShell 里 `{"foo":"bar"}` 不加单引号会被当成脚本块解析,body 被吃掉所有引号。**要么用单引号包 JSON**,要么把 body 写到文件用 `--data-binary "@file.json"`。
|
||||
### 6. 查看日志
|
||||
|
||||
服务启动后会输出详细日志,包括:
|
||||
- 服务启动信息
|
||||
- 配置状态
|
||||
- 请求统计信息
|
||||
- 错误详情
|
||||
服务启动后输出到 stdout/stderr。常见日志关键字:
|
||||
|
||||
**中间件层拒绝**(有专门日志):
|
||||
|
||||
```
|
||||
CORS拦截: 来源="https://..." 路径=/v1/audio/speech 方法=POST 客户端=...
|
||||
警告: 已超过IP速率限制,拒绝请求 - 客户端IP: 1.2.3.4
|
||||
警告: 已达到最大并发请求数限制,拒绝请求 - 客户端IP: 1.2.3.4
|
||||
```
|
||||
|
||||
**Controller 层拒绝**(每条都带具体原因和客户端 IP):
|
||||
|
||||
```
|
||||
警告: 错误的方法 - 方法=GET 期望=POST 路径=/v1/audio/speech 客户端=...
|
||||
警告: API Key 鉴权失败 - 路径=/v1/audio/speech 客户端=... 远端=...
|
||||
警告: TTS配置未就绪,拒绝请求 - 错误=缺少必需的环境变量: [BYTEDANCE_TTS_API_KEY] 路径=...
|
||||
警告: 请求体过大 - 路径=... 限制=1048576字节
|
||||
警告: 读取请求体失败 - 路径=... 错误=...
|
||||
警告: JSON 解析失败 - 路径=... 错误=... body前200字节="..."
|
||||
警告: Model 名过长 - 路径=... 长度=80 限制=64
|
||||
警告: Model 名含非法字符 - 路径=... model前50字节="..."
|
||||
警告: input 字段为空 - 路径=...
|
||||
警告: input 文本过长 - 路径=... 长度=6000 限制=5000
|
||||
警告: TTS 合成失败 - 路径=... 文本长度=50 耗时=114ms 错误=...
|
||||
```
|
||||
|
||||
**请求结束通用日志**(每个请求都有,由 Logger 中间件输出):
|
||||
|
||||
```
|
||||
POST /v1/audio/speech 1.2.3.4:56789 200 245ms
|
||||
POST /v1/audio/speech 1.2.3.4:56789 400 1ms
|
||||
```
|
||||
|
||||
**调试技巧**:
|
||||
|
||||
- 排查请求被拒:在 Zeabur 实时日志里搜 `警告:` 或 `CORS拦截:`
|
||||
- 排查 CORS:搜 `CORS拦截:` 看具体被拒的 origin
|
||||
- 排查 4xx/5xx:找对应路径的 `POST /v1/audio/speech ... 4xx` 行,再往上翻看 `警告:` 行
|
||||
- 排查 55000000 等上游错误:搜 `TTS service error` 或 `TTS 合成失败`,看火山返回的 code/message
|
||||
|
||||
**静默路径(不会产生日志)**:
|
||||
|
||||
- CORS 预检成功的 `OPTIONS ... 204`:完全不打日志(设计如此,避免高频预检刷屏)。CORS 拒绝的 OPTIONS 仍会输出 `CORS拦截:` 日志
|
||||
|
||||
## 部署建议
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,18 +23,32 @@ func InitController() {
|
||||
volcanoClient = volcano.NewHTTPClient()
|
||||
}
|
||||
|
||||
// truncateForLog 用于在日志中安全地展示请求内容(截断避免日志爆炸、控制不可打印字符)
|
||||
func truncateForLog(b []byte, max int) string {
|
||||
if len(b) > max {
|
||||
return string(b[:max]) + fmt.Sprintf("...(truncated, total %d bytes)", len(b))
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func OpenaiTTSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
log.Printf("警告: 错误的方法 - 方法=%s 期望=POST 路径=%s 客户端=%s",
|
||||
r.Method, r.URL.Path, middleware.GetClientIP(r))
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if !middleware.ValidateAPIKey(r) {
|
||||
log.Printf("警告: API Key 鉴权失败 - 路径=%s 客户端=%s 远端=%s",
|
||||
r.URL.Path, middleware.GetClientIP(r), r.RemoteAddr)
|
||||
middleware.SendJSONError(w, http.StatusUnauthorized, "Invalid API key provided.", "invalid_request_error", "invalid_api_key")
|
||||
return
|
||||
}
|
||||
|
||||
if setting.TTSConfigErr != nil {
|
||||
log.Printf("警告: TTS配置未就绪,拒绝请求 - 错误=%v 路径=%s 客户端=%s",
|
||||
setting.TTSConfigErr, r.URL.Path, middleware.GetClientIP(r))
|
||||
middleware.SendJSONError(w, http.StatusServiceUnavailable, "TTS service configuration error. Please check environment variables and restart the service.", "configuration_error", "service_unavailable")
|
||||
return
|
||||
}
|
||||
@@ -42,36 +57,49 @@ func OpenaiTTSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "request body too large") {
|
||||
log.Printf("警告: 请求体过大 - 路径=%s 客户端=%s 限制=%d字节",
|
||||
r.URL.Path, middleware.GetClientIP(r), common.MaxRequestBodySize)
|
||||
http.Error(w, "Request body too large", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
log.Printf("警告: 读取请求体失败 - 路径=%s 客户端=%s 错误=%v",
|
||||
r.URL.Path, middleware.GetClientIP(r), err)
|
||||
http.Error(w, "Failed to read request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.OpenAITTSRequest
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
log.Printf("警告: JSON 解析失败 - 路径=%s 客户端=%s 错误=%v body前200字节=%q",
|
||||
r.URL.Path, middleware.GetClientIP(r), err, truncateForLog(body, 200))
|
||||
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Model != "" {
|
||||
if len(req.Model) > common.MaxModelNameLength {
|
||||
log.Printf("警告: Model 名过长 - 路径=%s 客户端=%s 长度=%d 限制=%d",
|
||||
r.URL.Path, middleware.GetClientIP(r), len(req.Model), common.MaxModelNameLength)
|
||||
http.Error(w, fmt.Sprintf("Model name too long (max %d characters)", common.MaxModelNameLength), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if strings.ContainsAny(req.Model, "\x00\n\r\t") {
|
||||
log.Printf("警告: Model 名含非法字符 - 路径=%s 客户端=%s model前50字节=%q",
|
||||
r.URL.Path, middleware.GetClientIP(r), truncateForLog([]byte(req.Model), 50))
|
||||
http.Error(w, "Model name contains invalid characters", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.Input == "" {
|
||||
log.Printf("警告: input 字段为空 - 路径=%s 客户端=%s", r.URL.Path, middleware.GetClientIP(r))
|
||||
http.Error(w, "Input text is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Input) > common.MaxTextLength {
|
||||
log.Printf("警告: input 文本过长 - 路径=%s 客户端=%s 长度=%d 限制=%d",
|
||||
r.URL.Path, middleware.GetClientIP(r), len(req.Input), common.MaxTextLength)
|
||||
http.Error(w, fmt.Sprintf("Input text too long (max %d characters)", common.MaxTextLength), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -93,6 +121,8 @@ func OpenaiTTSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err != nil {
|
||||
service.GlobalStats.AddRequest(false, duration, err.Error())
|
||||
log.Printf("警告: TTS 合成失败 - 路径=%s 客户端=%s 文本长度=%d 耗时=%v 错误=%v",
|
||||
r.URL.Path, middleware.GetClientIP(r), len(req.Input), duration, err)
|
||||
http.Error(w, "TTS synthesis failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user