dsfdsfdsfdsf1223
2016-07-28 14:30
浏览 201
已采纳

io.MultiWriter与golang的传递值

I'd like to create a situation where everything set to a particular log.Logger is also appended to a particular variable's array of strings.

The variable's type implements the io.Writer interface so it should be easy to add that via io.MultiWriter to log.New(), but I seem to have run into an intractable problem: the io.Writer interface is fixed and it's impossible for the variable to reference itself given golang's pass-by-value.

Maybe it will make more sense with an example:

package main

import "fmt"
import "io"
import "log"
import "os"
import "strings"

var Log *log.Logger

type Job_Result struct {
    Job_ID int64
    // other stuff
    Log_Lines []string
}

// satisfies io.Writer interface
func (jr Job_Result) Write (p []byte) (n int, err error) {
    s := strings.TrimRight(string(p),"
 ")
    jr.Log_Lines= append(jr.Log_Lines,s)
    return len(s), nil  
}

func (jr Job_Result) Dump() {
    fmt.Println("
Here is a dump of the job result log lines:")
    for n, s := range jr.Log_Lines{
        fmt.Printf("\tline %d: %s
",n,s)
    }
}

func main() {

    // make a Job_Result

    var jr Job_Result
    jr.Job_ID = 123
    jr.Log_Lines = make([]string,0)

    // create an io.MultiWriter that points to both stdout 
    // and that Job_Result var

    var writers io.Writer
    writers = io.MultiWriter(os.Stdout,jr)

    Log = log.New(writers,
       "",
       log.Ldate|log.Ltime|log.Lshortfile)

    // send some stuff to the log

    Log.Println("program starting")
    Log.Println("something happened")
    Log.Printf("last thing that happened, should be %drd line
",3)

    jr.Dump()   

}

This is the output, which is not surprising:

2016/07/28 07:20:07 testjob.go:43: program starting
2016/07/28 07:20:07 testjob.go:44: something happened
2016/07/28 07:20:07 testjob.go:45: last thing that happened, should be 3rd line

Here is a dump of the job result log lines:

I understand the problem - Write() is getting a copy of the Job_Result variable, so it's dutifully appending and then the copy vanishes as it's local. I should pass it a pointer to the Job_Result...but I'm not the one calling Write(), it's done by the Logger, and I can't change that.

I thought this was a simple solution to capturing log output into an array (and there is other subscribe/unsubscribe stuff I didn't show), but it all comes down to this problematic io.Write() interface.

Pilot error? Bad design? Something I'm not grokking? Thanks for any advice.

  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 邀请回答

1条回答 默认 最新

  • dongshao8471 2016-07-28 14:36
    最佳回答

    redefine the write function (is now pointer receiver)

    // satisfies io.Writer interface
    func (jr *Job_Result) Write (p []byte) (n int, err error) {
        s := strings.TrimRight(string(p),"
     ")
        jr.Log_Lines= append(jr.Log_Lines,s)
        return len(s), nil  
    }
    

    initialize

    jr := new(Job_Result) // makes a pointer.
    

    rest stays as is. This way, *Job_Result still implements io.Writer, but doesn't lose state.

    The go tutorial already said, when a method modifies the receiver, you should probably use a pointer receiver, or the changes may be lost. Working with a pointer instead of the actual object has little downside, when you want to make sure, there is exactly one object. (And yes, it technically isn't an object).

    评论
    解决 无用
    打赏 举报