docker logs 二进制数据格式介绍以及解析

docker logs 二进制数据格式介绍及解析

为什么需要了解docker logs二进制数据格式

docker 以特定格式的二进制数据接口,提供docker logs。从该接口中获取的日志数据包括,输出到标准设备的日志和输出到标准错误输出设备的日志。如果我们需要按照我们想要的方式,来获取我们需要的日志信息,就必须要了解docker logs 二进制数据格式, 才能正确的反序列化docker格式的二进制数据。

docker logs 二进制数据格式是怎样的

先上一张docker logs二进制格式图片
在这里插入图片描述
如图所示,日志头部信息,占8个字节。第一个字节表示设备类型(标准输出设备or标准错误输出)。5~8字节(共4字节)表示本条日志长度。中间有3个字节为保留字段。长度字段后紧跟是本条日志内容。然后是第二条日志头部… 这样依次循环,直到最后一条日志内容结尾(读到错误类型是io.EOF即读完了)。

解析函数

官方在"github.com/docker/docker/pkg/stdcopy" 包中提供了函数:
StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
该函数能将日志文件分流为输出到标准输出和标准错误输出两类日志,功能非常强大。
但是,在某些场景下,将日志分类输出,反而带了麻烦。比如要在一个页面(界面)按时间顺序同时输出所有日志(包含标准输出/标准错误输出)。在这种场景,就比较尴尬。因为,前面将日志分类分流出来,现在又要合并到一块。并且还要按照日志产生的时间顺序排序,然后统一输出。这在大量日志的情况下,这将是一场灾难!
既然,已经了解了docker logs二进制数据格式,不如自己新造一个更好用的解析函数。废话不多说,直接上代码:


// StdCopyMix is a modified version of io.Copy and it is different from StdCopy.
// StdCopyMix is used for deserializing binary docker logs

// As it reads from `src`, StdCopyMix will write to `dstout` only.
//
// StdCopyMix will read until it hits EOF on `src`. It will then return a nil error.
// In other words: if `err` is non nil, it indicates a real underlying error.
//
// `written` will hold the total number of bytes written to `dstout` only.
func StdCopyMix(dstout io.Writer, src io.Reader) (written int64, err error) {
	var (
		buf       = make([]byte, startingBufLen)
		bufLen    = len(buf)
		nr, nw    int
		er, ew    error
		frameSize int
	)

	for {
		// Make sure we have at least a full header
		for nr < stdWriterPrefixLen {
			var nr2 int
			nr2, er = src.Read(buf[nr:])
			nr += nr2
			if er == io.EOF {
				if nr < stdWriterPrefixLen {
					return written, nil
				}
				break
			}
			if er != nil {
				return 0, er
			}
		}

		stream := StdType(buf[stdWriterFdIndex])

		// Retrieve the size of the frame
		frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4]))

		// Check if the buffer is big enough to read the frame.
		// Extend it if necessary.
		if frameSize+stdWriterPrefixLen > bufLen {
			buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...)
			bufLen = len(buf)
		}

		// While the amount of bytes read is less than the size of the frame + header, we keep reading
		for nr < frameSize+stdWriterPrefixLen {
			var nr2 int
			nr2, er = src.Read(buf[nr:])
			nr += nr2
			if er == io.EOF {
				if nr < frameSize+stdWriterPrefixLen {
					return written, nil
				}
				break
			}
			if er != nil {
				return 0, er
			}
		}

		// we might have an error from the source mixed up in our multiplexed
		// stream. if we do, return it.
		if stream == Systemerr {
			return written, fmt.Errorf("error from daemon in stream: %s", string(buf[stdWriterPrefixLen:frameSize+stdWriterPrefixLen]))
		}

		// Write the retrieved frame (without header)
		nw, ew = dstout.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
		if ew != nil {
			return 0, ew
		}

		// If the frame has not been fully written: error
		if nw != frameSize {
			return 0, io.ErrShortWrite
		}
		written += int64(nw)

		// Move the rest of the buffer to the beginning
		copy(buf, buf[frameSize+stdWriterPrefixLen:])
		// Move the index
		nr -= frameSize + stdWriterPrefixLen
	}
}

这段代码,经过测试,工作正常。
如果你喜欢本文,欢迎转载。转载请注明出处!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值