diff --git a/adapter/volcano/synthesis.go b/adapter/volcano/synthesis.go index 1df6ffc..4eece0f 100644 --- a/adapter/volcano/synthesis.go +++ b/adapter/volcano/synthesis.go @@ -84,7 +84,7 @@ func Synthesis( if common.DebugLog { log.Printf("TTS upstream: resource_id=%s speaker=%s model=%q format=%s sample_rate=%d speech_rate=%d additions=%q", - opts.ResourceID, telemetry.MaskSpeaker(opts.Speaker), opts.Model, opts.Format, opts.SampleRate, opts.SpeechRate, extractAdditionsForLog(body)) + telemetry.MaskResourceID(opts.ResourceID), telemetry.MaskSpeaker(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) diff --git a/controller/tts.go b/controller/tts.go index 4b103f3..d0c6e05 100644 --- a/controller/tts.go +++ b/controller/tts.go @@ -197,7 +197,7 @@ func OpenaiTTSHandler(w http.ResponseWriter, r *http.Request) { opts.Model = v.Model } log.Printf("[tts] voice=%s 命中 (speaker=%s resource=%s model=%s) - 客户端=%s", - req.Voice, telemetry.MaskSpeaker(v.Speaker), v.ResourceID, v.Model, middleware.GetClientIP(r)) + req.Voice, telemetry.MaskSpeaker(v.Speaker), telemetry.MaskResourceID(v.ResourceID), v.Model, middleware.GetClientIP(r)) } ctx, cancel := context.WithTimeout(r.Context(), setting.TTSTimeout) diff --git a/main.go b/main.go index 5612b46..7c2aad0 100644 --- a/main.go +++ b/main.go @@ -78,7 +78,7 @@ func main() { log.Printf("[main][WARN] TTS 运行时配置加载失败 (setup mode, 需先 /setup): %v", err) } else { log.Printf("[main] TTS 运行时配置已加载(api_key=***, speaker=%s, resource=%s, format=%s)", - setting.TTSOptions.Speaker, setting.TTSOptions.ResourceID, setting.TTSOptions.Format) + telemetry.MaskSpeaker(setting.TTSOptions.Speaker), telemetry.MaskResourceID(setting.TTSOptions.ResourceID), setting.TTSOptions.Format) } } diff --git a/setting/config.go b/setting/config.go index 189326a..3ca4a2d 100644 --- a/setting/config.go +++ b/setting/config.go @@ -449,7 +449,7 @@ func LogStartupSummary() { } checks := []ttsCheck{ {"BYTEDANCE_TTS_API_KEY", maskAPIKey(TTSOptions.APIKey), TTSOptions.APIKey != ""}, - {"BYTEDANCE_TTS_RESOURCE_ID", TTSOptions.ResourceID, TTSOptions.ResourceID != ""}, + {"BYTEDANCE_TTS_RESOURCE_ID", telemetry.MaskResourceID(TTSOptions.ResourceID), TTSOptions.ResourceID != ""}, // speaker 是火山复刻音色 ID(用户付费资产),日志里打码,避免明文落盘 {"BYTEDANCE_TTS_SPEAKER", telemetry.MaskSpeaker(TTSOptions.Speaker), TTSOptions.Speaker != ""}, } diff --git a/telemetry/labels.go b/telemetry/labels.go index 7dfaa29..a45604a 100644 --- a/telemetry/labels.go +++ b/telemetry/labels.go @@ -41,14 +41,29 @@ func SpeakerLabel(s string) string { // - 其它 → 前 4 + **** + 后 4 (保留前缀便于肉眼区分 "S_xx 开头" vs "BV001_...") // 例子: "S_G8tEKnaJ1" → "S_G8****naJ1" func MaskSpeaker(s string) string { + return maskWithAffix(s, "(未设置)") +} + +// MaskResourceID 把火山 TTS 资源 ID 部分打码用于日志输出。 +// 资源 ID 同样属于用户付费/敏感资产(指向 V3 复刻项目),与 speaker 走同一规则。 +// - 空 → "(未设置)" +// - 长度 ≤ 4 → 全打码 +// - 其它 → 前 4 + **** + 后 4 +// 例子: "volc.megatts.icl" → "volc****.icl"; "seed-icl-2.0" → "seed****2.0" +func MaskResourceID(s string) string { + return maskWithAffix(s, "(未设置)") +} + +// maskWithAffix 共用的"前 4 + **** + 后 4"打码逻辑,空串返回 emptyLabel。 +func maskWithAffix(s, emptyLabel string) string { if s == "" { - return "(未设置)" + return emptyLabel } if len(s) <= 4 { return strings.Repeat("*", len(s)) } // 找前 4 字符中第一个非 [A-Za-z0-9_] 字符做截断,避免截到奇怪位置 - // (虽然火山 ID 实际都是 S_xxx 字母数字组合,这里保险) + // (虽然火山 ID 实际都是字母数字组合,这里保险) prefix := s[:4] suffix := s[len(s)-4:] return prefix + "****" + suffix