feat: 为 Python worker 输出添加行前缀,并启用无缓冲模式

- 新增 prefixWriter,对每行输出添加 [python:N] 前缀,便于与 Go 日志区分
- 注入 PYTHONUNBUFFERED=1,确保 print() 实时输出不被管道缓冲
This commit is contained in:
2026-04-14 14:38:24 +08:00
parent 39517dc5fc
commit b4e77ce9a8
2 changed files with 62 additions and 8 deletions

52
prefix_writer.go Normal file
View File

@@ -0,0 +1,52 @@
package gobridge
import (
"bytes"
"io"
"sync"
)
// prefixWriter 对每一行输出添加固定前缀,线程安全。
type prefixWriter struct {
mu sync.Mutex
w io.Writer
prefix []byte
buf []byte
}
func newPrefixWriter(w io.Writer, prefix string) *prefixWriter {
return &prefixWriter{w: w, prefix: []byte(prefix)}
}
func (p *prefixWriter) Write(b []byte) (int, error) {
p.mu.Lock()
defer p.mu.Unlock()
n := len(b)
p.buf = append(p.buf, b...)
for {
idx := bytes.IndexByte(p.buf, '\n')
if idx < 0 {
break
}
line := append(p.prefix, p.buf[:idx+1]...)
if _, err := p.w.Write(line); err != nil {
return n, err
}
p.buf = p.buf[idx+1:]
}
return n, nil
}
// flush 将缓冲区中尚未以换行结尾的内容输出(进程退出时调用)。
func (p *prefixWriter) flush() {
p.mu.Lock()
defer p.mu.Unlock()
if len(p.buf) > 0 {
line := append(p.prefix, p.buf...)
line = append(line, '\n')
p.w.Write(line) //nolint
p.buf = nil
}
}

View File

@@ -60,18 +60,20 @@ func (w *worker) start() error {
fmt.Sprintf("GOBRIDGE_WORKER_ID=%d", w.id),
fmt.Sprintf("GOBRIDGE_WORKER_COUNT=%d", w.cfg.workers),
"GOBRIDGE_DEATH_FD=3", // ExtraFiles[0] → fd 3
"PYTHONUNBUFFERED=1", // Python stdout/stderr 不缓冲print() 立即输出
)
cmd.ExtraFiles = []*os.File{pr} // fd 3 in child
if w.cfg.stdout != nil {
cmd.Stdout = w.cfg.stdout
} else {
cmd.Stdout = os.Stdout
prefix := fmt.Sprintf("[python:%d] ", w.id)
stdout := w.cfg.stdout
if stdout == nil {
stdout = os.Stdout
}
if w.cfg.stderr != nil {
cmd.Stderr = w.cfg.stderr
} else {
cmd.Stderr = os.Stderr
stderr := w.cfg.stderr
if stderr == nil {
stderr = os.Stderr
}
cmd.Stdout = newPrefixWriter(stdout, prefix)
cmd.Stderr = newPrefixWriter(stderr, prefix)
if err := cmd.Start(); err != nil {
pr.Close()