
spew是一个更友好的变量打印工具,github.com/davecgh/go-spew,它能打印指针指向的内容,避免直接打印的时候打印出来的是指针地址的问题,同时能够检测结构体中存在的环,解决了json序列化打印的时候不能打印环的问题,它提供了很多接口,可以打印到文件、json、和标准输出,逻辑都差不多,我们详细分析下打印到标准输出逻辑的实现。
func Dump(a ...interface{}) {
fdump(&Config, os.Stdout, a...)
}源码位于:github.com/davecgh/go-spew@v1.1.1/spew/dump.go,它会依次打印变长参数列表里面的每一个变量,如果遇到nil变量,会打印出 (interface {}) <nil> <nil>
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
w.Write(spaceBytes)
w.Write(nilAngleBytes)
w.Write(newlineBytes)
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes) interfaceBytes = []byte("(interface {})")
spaceBytes = []byte(" ")
nilAngleBytes = []byte("<nil>")
newlineBytes = []byte("\n")如果遇到非nil变量,会调用 d.dump函数其中d定义如下
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}函数内部根据值的类型进行打印
func (d *dumpState) dump(v reflect.Value) {
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
if kind == reflect.Ptr {
d.indent()
d.dumpPtr(v)
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
valueLen, valueCap := 0, 0
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
valueLen, valueCap = v.Len(), v.Cap()
case reflect.Map, reflect.String:
valueLen = v.Len()
}
if valueLen != 0
d.w.Write(lenEqualsBytes)
printInt(d.w, int64(valueLen), 10)
if !d.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(d.cs, d.w, v); handled {
switch kind {
case reflect.Invalid:
case reflect.Array:
d.w.Write(openBraceNewlineBytes)
case reflect.Struct:
d.w.Write(openBraceNewlineBytes)
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer())invalidAngleBytes = []byte("<invalid>")
输出指针类型的时候,会根据熟悉所在层级,根据深度填空格
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))如果是数组,slice或者chan会输出len和cap等信息
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")如果有方法列表需要打印方法列表。其中输出指针类型的详细如下: 在输出的过程中会判断是否有环,如果有环只打印一遍,再次走到,打印
circularBytes = []byte("<already shown>")func (d *dumpState) dumpPtr(v reflect.Value) {
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
pointerChain = append(pointerChain, addr)
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
d.w.Write(openParenBytes)
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
打印方法的逻辑位于:github.com/davecgh/go-spew@v1.1.1/spew/common.go会对interface进行特殊处理
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
if !v.CanInterface() {
if UnsafeDisabled {
v = unsafeReflectValue(v)
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)github.com/davecgh/go-spew@v1.1.1/spew/bypass.go
func unsafeReflectValue(v reflect.Value) reflect.Value {
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v本质上是利用反射reflect.Value获取值信息,然后打印出值信息,附加一些类型的属性信息,解决了直接打印指针问题和json序列化环的问题。