为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
潇湘沐 2023-04-24 15:55:56
我正在尝试实现一个满足 io.Writer 要求的代理,因此我可以将其插入记录器。这个想法是它会像平常一样打印输出,但也会保留一份数据副本供以后读取。以下代码中的 ProxyIO 结构应该执行此操作,而且只要我直接调用其 Write() 方法,它确实会执行此操作。但是,当我将它插入 log.Logger 实例时,输出是意外的。(这是精简代码,我想使用的原始实现是使用映射和循环指针,而不是[][]byte示例代码中使用的 buf。我还删除了所有锁定。)package mainimport (    "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: test11: test22: test33: test44: test55: test6但是,它将始终打印:0: test11: test22: test33: test64: test65: test6我的地图实现的行为是相同的。我还尝试使用双向链表作为container/list存储,它总是一样的。所以我一定在这里遗漏了一些重要的东西。为什么我在缓冲区中看到了三次最后的日志输出,而不是最后三行日志输出?
查看完整描述

1 回答

?
catspeake

TA贡献1111条经验 获得超0个赞

如果您查看源代码,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}
查看完整回答
反对 回复 2023-04-24
  • 1 回答
  • 0 关注
  • 144 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信