feat: add debug log control and optimize error response and cors log
- add DebugLog global variable controlled by BYTEDANCE_TTS_DEBUG env - enable debug logs for cors, tts upstream, response parsing etc when debug mode is on - update tts error response to use standardized json error format - add dashboard and metrics path to cache-control exempt list
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/volcano-tts/tts-api/common"
|
||||||
"github.com/volcano-tts/tts-api/dto"
|
"github.com/volcano-tts/tts-api/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +41,9 @@ func ParseStream(body io.Reader, started time.Time) (*ParsedStream, error) {
|
|||||||
}
|
}
|
||||||
var resp dto.V3TTSResponse
|
var resp dto.V3TTSResponse
|
||||||
if err := json.Unmarshal(line, &resp); err != nil {
|
if err := json.Unmarshal(line, &resp); err != nil {
|
||||||
log.Printf("volcano: 解析响应行失败: %v, line=%q", err, truncateForLog(line, 200))
|
if common.DebugLog {
|
||||||
|
log.Printf("volcano: 解析响应行失败: %v, line=%q", err, truncateForLog(line, 200))
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +59,9 @@ func ParseStream(body io.Reader, started time.Time) (*ParsedStream, error) {
|
|||||||
if resp.Usage != nil {
|
if resp.Usage != nil {
|
||||||
out.TextWords = resp.Usage.TextWords
|
out.TextWords = resp.Usage.TextWords
|
||||||
out.HasUsage = true
|
out.HasUsage = true
|
||||||
log.Printf("TTS 合成结束, usage: text_words=%d", out.TextWords)
|
if common.DebugLog {
|
||||||
|
log.Printf("TTS 合成结束, usage: text_words=%d", out.TextWords)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
}
|
}
|
||||||
@@ -66,9 +71,13 @@ func ParseStream(body io.Reader, started time.Time) (*ParsedStream, error) {
|
|||||||
// 事件分发:显式匹配已知事件,绝不把未知事件当作音频。
|
// 事件分发:显式匹配已知事件,绝不把未知事件当作音频。
|
||||||
switch resp.Event {
|
switch resp.Event {
|
||||||
case "TTSSentenceStart":
|
case "TTSSentenceStart":
|
||||||
log.Printf("Sentence start: sequence=%d, sentence=%s", resp.Sequence, resp.Sentence)
|
if common.DebugLog {
|
||||||
|
log.Printf("Sentence start: sequence=%d, sentence=%s", resp.Sequence, resp.Sentence)
|
||||||
|
}
|
||||||
case "TTSSentenceEnd":
|
case "TTSSentenceEnd":
|
||||||
log.Printf("Sentence end: sequence=%d", resp.Sequence)
|
if common.DebugLog {
|
||||||
|
log.Printf("Sentence end: sequence=%d", resp.Sequence)
|
||||||
|
}
|
||||||
case "TTSSubtitle":
|
case "TTSSubtitle":
|
||||||
if resp.Data != "" {
|
if resp.Data != "" {
|
||||||
out.Subtitles = append(out.Subtitles, dto.SubtitleEntry{
|
out.Subtitles = append(out.Subtitles, dto.SubtitleEntry{
|
||||||
@@ -98,7 +107,9 @@ func ParseStream(body io.Reader, started time.Time) (*ParsedStream, error) {
|
|||||||
case "":
|
case "":
|
||||||
// 传输帧,跳过
|
// 传输帧,跳过
|
||||||
default:
|
default:
|
||||||
log.Printf("volcano: 忽略未识别事件 event=%q sequence=%d", resp.Event, resp.Sequence)
|
if common.DebugLog {
|
||||||
|
log.Printf("volcano: 忽略未识别事件 event=%q sequence=%d", resp.Event, resp.Sequence)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/volcano-tts/tts-api/common"
|
||||||
"github.com/volcano-tts/tts-api/dto"
|
"github.com/volcano-tts/tts-api/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,8 +80,10 @@ func Synthesis(
|
|||||||
"X-Control-Require-Usage-Tokens-Return": "*",
|
"X-Control-Require-Usage-Tokens-Return": "*",
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("TTS upstream: resource_id=%s speaker=%s model=%q format=%s sample_rate=%d speech_rate=%d additions=%q",
|
if common.DebugLog {
|
||||||
opts.ResourceID, opts.Speaker, opts.Model, opts.Format, opts.SampleRate, opts.SpeechRate, extractAdditionsForLog(body))
|
log.Printf("TTS upstream: resource_id=%s speaker=%s model=%q format=%s sample_rate=%d speech_rate=%d additions=%q",
|
||||||
|
opts.ResourceID, opts.Speaker, opts.Model, opts.Format, opts.SampleRate, opts.SpeechRate, extractAdditionsForLog(body))
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.PostStream(ctx, "https://openspeech.bytedance.com/api/v3/tts/unidirectional", headers, body)
|
resp, err := client.PostStream(ctx, "https://openspeech.bytedance.com/api/v3/tts/unidirectional", headers, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -129,6 +132,9 @@ func Synthesis(
|
|||||||
}
|
}
|
||||||
mtr.UpstreamFinished(opts.Speaker, opts.Model, opts.Format, "ok", duration, parsed.FirstChunk, parsed.Chunks, len(finalData), 0)
|
mtr.UpstreamFinished(opts.Speaker, opts.Model, opts.Format, "ok", duration, parsed.FirstChunk, parsed.Chunks, len(finalData), 0)
|
||||||
|
|
||||||
|
log.Printf("TTS 合成成功 - 音色=%s 格式=%s 文本=%d字 音频=%d字节 分片=%d 耗时=%v",
|
||||||
|
opts.Speaker, clientFormat, len(text), len(finalData), parsed.Chunks, duration)
|
||||||
|
|
||||||
return &dto.SynthesisResult{
|
return &dto.SynthesisResult{
|
||||||
AudioData: finalData,
|
AudioData: finalData,
|
||||||
Format: finalFormat,
|
Format: finalFormat,
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package common
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
// DebugLog 控制非必要日志输出;由 setting 包在启动时通过 BYTEDANCE_TTS_DEBUG 环境变量设置。
|
||||||
|
var DebugLog bool
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultPort = "8080"
|
DefaultPort = "8080"
|
||||||
DefaultTimeout = 30 * time.Second
|
DefaultTimeout = 30 * time.Second
|
||||||
|
|||||||
+1
-1
@@ -162,7 +162,7 @@ func OpenaiTTSHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
metrics.RequestDuration.Observe(duration.Seconds(), telemetry.Labels{"status": finalLabels["status"], "format": clientFormat})
|
metrics.RequestDuration.Observe(duration.Seconds(), telemetry.Labels{"status": finalLabels["status"], "format": clientFormat})
|
||||||
log.Printf("警告: TTS 合成失败 - 路径=%s 客户端=%s 文本长度=%d 耗时=%v 错误=%v",
|
log.Printf("警告: TTS 合成失败 - 路径=%s 客户端=%s 文本长度=%d 耗时=%v 错误=%v",
|
||||||
r.URL.Path, middleware.GetClientIP(r), len(req.Input), duration, err)
|
r.URL.Path, middleware.GetClientIP(r), len(req.Input), duration, err)
|
||||||
http.Error(w, "TTS synthesis failed", http.StatusInternalServerError)
|
middleware.SendJSONError(w, http.StatusInternalServerError, "TTS synthesis failed.", "server_error", "synthesis_failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/volcano-tts/tts-api/common"
|
||||||
"github.com/volcano-tts/tts-api/setting"
|
"github.com/volcano-tts/tts-api/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,8 +71,10 @@ func CORS(next http.Handler) http.Handler {
|
|||||||
if !matched {
|
if !matched {
|
||||||
// Origin 不在白名单:拒绝请求(预检和非预检均拒绝),
|
// Origin 不在白名单:拒绝请求(预检和非预检均拒绝),
|
||||||
// 防止不匹配的请求穿透到后端浪费 TTS 资源
|
// 防止不匹配的请求穿透到后端浪费 TTS 资源
|
||||||
log.Printf("CORS拦截: 来源=%q 路径=%s 方法=%s 客户端=%s",
|
if common.DebugLog {
|
||||||
origin, r.URL.Path, r.Method, GetClientIP(r))
|
log.Printf("CORS拦截: 来源=%q 路径=%s 方法=%s 客户端=%s",
|
||||||
|
origin, r.URL.Path, r.Method, GetClientIP(r))
|
||||||
|
}
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func SecurityHeaders(next http.Handler) http.Handler {
|
|||||||
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
||||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||||
|
|
||||||
if strings.HasPrefix(r.URL.Path, "/v1/") || r.URL.Path == "/health" {
|
if strings.HasPrefix(r.URL.Path, "/v1/") || r.URL.Path == "/health" || r.URL.Path == "/dashboard" || r.URL.Path == "/metrics" {
|
||||||
w.Header().Set("Cache-Control", "no-store")
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,11 @@ func InitTTSConfig() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common.DebugLog = getEnvBool("BYTEDANCE_TTS_DEBUG", false)
|
||||||
|
if common.DebugLog {
|
||||||
|
log.Println("调试日志已启用 BYTEDANCE_TTS_DEBUG")
|
||||||
|
}
|
||||||
|
|
||||||
TTSOptions = volcano.Options{
|
TTSOptions = volcano.Options{
|
||||||
APIKey: apiKey,
|
APIKey: apiKey,
|
||||||
ResourceID: resourceId,
|
ResourceID: resourceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user