英文:
Golang - Copy Exec output to Log
问题
我想要将一个进程的输出及时地重定向到log
中。如果我等待进程完成,我可以这样做:
cmd := exec.Command("yes", "Go is awesome") // 永远打印"Go is awesome"
out, err := cmd.CombinedOutput()
log.Printf("%s", out)
然而,如果进程需要很长时间或者没有完成,这种方法就不太有用了。我知道可以实时地写入标准输出,像这样:
cmd := exec.Command("yes")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
然而,这对我来说并不是很有帮助,因为我正在编写一个不会写入终端的服务。我正在寻找一种可以做到以下效果的方法:
cmd := exec.Command("yes")
cmd.Stdout = log.Stdout
cmd.Stderr = log.Stdout
cmd.Run()
log
包并没有直接提供对其写入器的访问,所以这是不可能的。我肯定不是唯一遇到这个问题的人,通常是如何解决的呢?
英文:
I would like to redirect the output of a process to log
in a timely manner. I can do it if I wait for the process to finish like this:
cmd := exec.Command("yes", "Go is awesome") // Prints "Go is awesome", forever
out, err := cmd.CombinedOutput()
log.Printf("%s", out)
However, if the process takes a long time or doesn't finish this is less useful. I know I can write to stdout in real time like this:
cmd := exec.Command("yes")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
This doesn't really help me though, because I am writing a service that isn't writing to a terminal. I'm looking for something that will let me do something like:
cmd := exec.Command("yes")
cmd.Stdout = log.Stdout
cmd.Stderr = log.Stdout
cmd.Run()
log
doesn't give direct access to its writer so this is not possible. Surely I'm not the only with this problem, how is this typically done?
答案1
得分: 34
你应该在这里使用一个管道,例如:
stdout, err := cmd.StdoutPipe()
if err != nil {
return 0, err
}
// 在设置好管道后启动命令
if err := cmd.Start(); err != nil {
return 0, err
}
// 逐行读取命令的标准输出
in := bufio.NewScanner(stdout)
for in.Scan() {
log.Printf(in.Text()) // 将每一行写入日志,或者其他你需要的操作
}
if err := in.Err(); err != nil {
log.Printf("错误: %s", err)
}
我只处理了Stdout
,但是也可以同时处理Stderr
,例如通过使用goroutine。
英文:
You should use a pipe here, for example:
stdout, err := cmd.StdoutPipe()
if err != nil {
return 0, err
}
// start the command after having set up the pipe
if err := cmd.Start(); err != nil {
return 0, err
}
// read command's stdout line by line
in := bufio.NewScanner(stdout)
for in.Scan() {
log.Printf(in.Text()) // write each line to your log, or anything you need
}
if err := in.Err(); err != nil {
log.Printf("error: %s", err)
}
I have only handled Stdout
here, but it is possible to handle Stderr
at the same time, for example by using a goroutine.
答案2
得分: 5
exec.Command
和log.Logger
都基于io.Writer
。你无法访问第二个的那些,但你也不需要,因为要么你没有改变它,而是使用os.Stderr
,要么你改变了它,并且在创建日志记录器时有io.Writer
可用。
所以你只需要通过使用正确的io.Writer
来调整你的第二个示例...
编辑
经过一些思考,我认为你可以通过将log.Logger
嵌入到一个实现io.Writer
接口的结构体中来实现。
示例:
type LogWriter struct {
logger *log.Logger
}
func NewLogWriter(l *log.Logger) *LogWriter {
lw := &LogWriter{}
lw.logger = l
return lw
}
func (lw LogWriter) Write(p []byte) (n int, err error) {
lw.logger.Println(p)
return len(p), nil
}
然后将其传递给你的exec.Command
输出...
cmd := exec.Command(…)
cmd.Stdout = NewLogWriter(log.New(…))
你可以将其用于标准输出或错误输出,或为每个输出创建新的对象。
编辑2
由于我将在我的下一个项目中使用这个技巧,我将其放在了GitHub上的一个包中。欢迎提供反馈等。
英文:
Both exec.Command
and log.Logger
are based on an io.Writer
. You don't have access to those of the second, but you don't need to because either you didn't changed it, and you're using os.Stderr
or you changed it and you have the io.Writer
at hand when creating the logger.
So you just have to adapt you second example by using the right io.Writer
…
Edit
After some though on it, I think you may be good to go by just embedding a log.Logger
into a struct that will implement the io.Writer
interface…
Example:
type LogWriter struct {
logger *log.Logger
}
func NewLogWriter(l *log.Logger) *LogWriter {
lw := &LogWriter{}
lw.logger = l
return lw
}
func (lw LogWriter) Write (p []byte) (n int, err error) {
lw.logger.Println(p)
return len(p), nil
}
And then pass it to your exec.Command
output…
cmd := exec.Command(…)
cmd.Stdout = NewLogWriter(log.New(…))
You can use it for both standard or error output, or create new object for each one.
Edit2
As I'm going to use this trick for one of my next projects, I put it into a package on github. Feel free to give feedback, etc.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论