本文最后由 森林生灵 于 2020/07/04 01:58:54 编辑
golang 的 io.MultiWriter() 函数可以将多个实现 io.Writer 接口实例包装为一个,实现多实例统一写入。但在开发自定义日志库时使用此函数遇到了这样一个问提,传入的多个接口实例总会有几个不会生效,而且好似是否生效还跟入传参位置有关。参考了网络上的案例,测试可以正常工作。
func test() {
file, err := os.Create("tmp.txt")
if err != nil {
panic(err)
}
defer file.Close()
writer := io.MultiWriter(file, os.Stdout)
writer.Write([]byte("Hello"))
}有问题的部分代码如下:
// 控制台打印彩色日志
type Console struct {}
func (c *Console) Write(p []byte) (n int, err error) {
prefix := ""
msg := string(p)
length := len("[INFO]")
if len(msg) >= length {
prefix = msg[:length]
switch prefix {
case "[INFO]":
prefix = StringBlue(prefix)
case "[WARN]":
prefix = StringYellow(prefix)
case "[ERRO]":
prefix = StringRed(prefix)
case "[HTTP]":
prefix = StringGreen(prefix)
}
}
if prefix != "" {
// 控制台打印彩色字符前缀
return fmt.Fprintln(os.Stdout, prefix, msg[length:])
} else {
// 没有前缀直接输出原始消息
return fmt.Fprintln(os.Stdout, msg)
}
}
// 发送严重错误通知
type Logger struct {}
func (l *Logger) Write(p []byte) (n int, err error) {
// TODO://
file, err := os.Create("tmp.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.Write(p)
// 由于尚未完成的该部分功能,为了测试 io.MultiWriter() 直接返回默认值
return 0, nil
}
// 测试 io.MultiWriter()
func test() {
writer := io.MultiWriter(os.Stderr, &console.Console{}, &reporter.Reporter{})
writer.Write([]byte("[INFO]Test Msg"))
writer.Write([]byte("Raw Msg"))
}表面看似两段代码没有什么错误,而且还把接口方法返回值的 error 硬性定义为 nil,但运行起来却大相径庭,经过一番查找发现异常原因在于 io.MultiWriter() 它不仅仅是判断返回值的 error ,还判断返回值的长度是否和传入 []byte 的长度一致。
func (t *multiWriter) Write(p []byte) (n int, err error) {
for _, w := range t.writers {
n, err = w.Write(p)
if err != nil {
return
}
// 处理后的切片长度要与原传入切片的长度一样,否则直接跳出循环,不在执行之后的 io.Write
if n != len(p) {
err = ErrShortWrite
return
}
}
return len(p), nil
}再回头看一下实现的那两个实例,很明显 &console.Console{} 在有符合的 prefix 时会对原始数据进行了修改,返回 fmt.Fprintln() 中的长度固然比原来的长;同时又由于 &reporter.Reporter{} 中强制返回的长度为 0,也会触发错误而终止循环。
心得,在本案例中参考了网传案例,先入为主,并未输出 io.MultiWriter() 的返回值,不曾考虑在 error 为 nil 时亦会导致程序中途跳出,故开发时一定要输出调用函数的返回值,即使上层函数 error 为 nil,也要输出一下,可(我)以(还)避(是)免(太)走(菜)弯(了)路 .....
本文标题:注意 io.MultiWriter 的返回值
版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
相关文章
上一篇:golang 模板语法