golang proxy io.Writer ведет себя по-разному при использовании в журнале. - PullRequest
0 голосов
/ 13 октября 2018

Я пытаюсь реализовать прокси, который удовлетворяет 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)

Я ожидаю следующий вывод из этогоcode:

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как хранилище, оно всегда одно и то же. Поэтому я, должно быть, здесь упускаю что-то существенное.

Почему я вижу, как последний журнал выводил три раза в буфере вместо последних трех строк вывода журнала?

1 Ответ

0 голосов
/ 13 октября 2018

Если вы посмотрите на исходный код Logger.Print, то увидите, что он вызывает logger.Output.Вы заметите, как он устанавливает значение строки в l.buf, а затем вызывает Write

Если вы прочитаете этот ответ , вы увидите, что, хотя все проходит мимоЗначение

, когда вы передаете фрагмент функции, из этого заголовка будет сделана копия, включая указатель, который будет указывать на тот же массив резервных копий.

Поэтому, когда вы делаете:

l.Print("test4")
l.Print("test5")
l.Print("test6")

Logger эффективно повторно использует один и тот же фрагмент, и вы append получаете ссылку на этот же фрагмент три раза, поэтому естественно, что при печати он использует самое последнее значение, установленное три раза.

Чтобы исправить это, вы можете скопировать []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
}

Обновленная игровая площадка: https://play.golang.org/p/DIWC1Xa6w0R

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...