Golang – 将执行结果复制到日志中

huangapple go评论93阅读模式
英文:

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.Commandlog.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.

huangapple
  • 本文由 发表于 2014年8月8日 03:48:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/25190971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定