猿问

golang 代理 io.Writer 在 log.Logger 中使用时表现不同

我正在尝试实现一个满足 io.Writer 要求的代理,因此我可以将其插入记录器。这个想法是它会像平常一样打印输出,但也会保留一份数据副本供以后读取。

以下代码中的 ProxyIO 结构应该执行此操作,而且只要我直接调用其 Write() 方法,它确实会执行此操作。但是,当我将它插入 log.Logger 实例时,输出是意外的。

(这是精简代码,我想使用的原始实现是使用映射和循环指针,而不是[][]byte示例代码中使用的 buf。我还删除了所有锁定。)

package main


import (

    "fmt"

    "io"

    "io/ioutil"

    "log"

)


type ProxyIO struct {

    out io.Writer // the io we are proxying

    buf [][]byte

}


func newProxyIO(out io.Writer) *ProxyIO {

    return &ProxyIO{

        out: out,

        buf: [][]byte{},

    }

}


func (r *ProxyIO) Write(s []byte) (int, error) {

    r.out.Write(s)

    r.buf = append(r.buf, s)

    return len(s), nil

}


func main() {

    p := newProxyIO(ioutil.Discard)

    p.Write([]byte("test1\n"))

    p.Write([]byte("test2\n"))

    p.Write([]byte("test3\n"))

    l := log.New(p, "", 0)

    l.Print("test4")

    l.Print("test5")

    l.Print("test6")

    for i, e := range p.buf {

        fmt.Printf("%d: %s", i, e)

    }

}

(这里是操场上的代码https://play.golang.org/p/UoOq4Nd-rmI)


我希望这段代码有以下输出:


0: test1

1: test2

2: test3

3: test4

4: test5

5: test6

但是,它将始终打印:


0: test1

1: test2

2: test3

3: test6

4: test6

5: test6

我的地图实现的行为是相同的。我还尝试使用双向链表作为container/list存储,它总是一样的。所以我一定在这里遗漏了一些重要的东西。


为什么我在缓冲区中看到了三次最后的日志输出,而不是最后三行日志输出?


潇湘沐
浏览 145回答 1
1回答

catspeake

如果您查看源代码,Logger.Print您会看到它调用logger.Output. 您会注意到它如何将字符串的值设置为l.buf然后调用Write您会发现即使所有内容都是按值传递的当您将切片传递给函数时,将从该标头制作一个副本,包括指针,该指针将指向相同的后备数组。所以当你这样做时:l.Print("test4") l.Print("test5") l.Print("test6")Logger 有效地重复使用同一个切片,并且您正在append对同一个切片引用 3 次,因此在打印时自然会使用 3 次最新的值集。[]byte要解决此问题,您可以像这样在使用之前复制:func (r *ProxyIO) Write(s []byte) (int, error) {     c := make([]byte, len(s))         copy(c, s)     r.out.Write(c)     r.buf = append(r.buf, c)         return len(c), nil}
随时随地看视频慕课网APP

相关分类

Go
我要回答