func (d *dumper) full(dir direction, kind, method, qualifier string, msg interface{}, latency time.Duration) {
	var hdr string
	if qualifier != "" {
		hdr = qualifier + " " + method + " " + dir.arrow() + " "
	} else {
		hdr = method + " " + dir.arrow() + " "
	}
	switch dir {
	case request:
		raw, _ := yaml.Marshal(msg)
		str := strings.TrimRight(string(raw), "\n")
		if strings.LastIndexByte(str, '\n') > 0 {
			d.line(dir, latency, hdr+"(%s) REQUEST", kind)
			d.block(dir, latency, hdr+"    ", str)
		} else {
			d.line(dir, latency, hdr+"(%s) REQUEST %s", kind, str)
		}
	case reply:
		if err, ok := msg.(error); ok {
			d.warn(dir, latency, hdr+"(%s) FAILED", kind)
			d.warn(dir, latency, hdr+"    %v", err)
		} else {
			raw, _ := yaml.Marshal(msg)
			str := strings.TrimRight(string(raw), "\n")
			if strings.LastIndexByte(str, '\n') > 0 {
				d.line(dir, latency, hdr+"(%s) REPLY", kind)
				d.block(dir, latency, hdr+"    ", str)
			} else {
				d.line(dir, latency, hdr+"(%s) REPLY %s", kind, str)
			}
		}
	}
}
full does a full dump of the given message.
package dump
import (
	"fmt"
	"os"
	"sigs.k8s.io/yaml"
	"strings"
	"sync"
	"time"
	logger "github.com/intel/cri-resource-manager/pkg/log"
)
const (
	stampLayout = "2006-Jan-02 15:04:05.000"
	stampLen = len(stampLayout)
)
type dumper struct {
	sync.RWMutex                   
	rules        ruleset           
	details      map[string]level  
	disabled     bool              
	debug        bool              
	path         string            
	file         *os.File          
	methods      []string          
	q            chan *dumpreq
}
type dumpreq struct {
	dir       direction
	kind      string
	method    string
	qualifier string
	msg       interface{}
	latency   time.Duration
	sync      chan struct{}
}
type direction int
const (
	request = iota
	reply
	nop
)
var dump = newDumper()
var log = logger.NewLogger("dump")
var message = logger.NewLogger("message")
func Train(methods []string) {
	dump.Lock()
	defer dump.Unlock()
	dump.train(methods)
}
func RequestMessage(kind, name, qualifier string, req interface{}, sync bool) {
	if !dump.disabled {
		var ch chan struct{}
		if sync {
			ch = make(chan struct{})
		}
		dump.q <- &dumpreq{
			dir:       request,
			kind:      kind,
			method:    name,
			qualifier: qualifier,
			msg:       req,
			sync:      ch,
		}
		if ch != nil {
			_ = <-ch
		}
	}
}
func ReplyMessage(kind, name, qualifier string, rpl interface{}, latency time.Duration, sync bool) {
	if !dump.disabled {
		var ch chan struct{}
		if sync {
			ch = make(chan struct{})
		}
		dump.q <- &dumpreq{
			dir:       reply,
			kind:      kind,
			method:    name,
			qualifier: qualifier,
			msg:       rpl,
			latency:   latency,
			sync:      ch,
		}
		if ch != nil {
			_ = <-ch
		}
	}
}
func Sync() {
	if !dump.disabled {
		dump.sync()
	}
}
func newDumper() *dumper {
	d := &dumper{q: make(chan *dumpreq, 16)}
	d.run()
	return d
}
func (d *dumper) run() {
	go func() {
		for req := range d.q {
			if req.dir != nop {
				method := methodName(req.method)
				d.RLock()
				detail, ok := d.details[method]
				if !ok {
					detail = d.rules.detailOf(method)
				}
				d.RUnlock()
				switch detail {
				case Name:
					d.name(req.dir, req.kind, method, req.qualifier, req.msg, req.latency)
				case Full:
					d.full(req.dir, req.kind, method, req.qualifier, req.msg, req.latency)
				}
			}
			if req.sync != nil {
				close(req.sync)
			}
		}
	}()
}
func (d *dumper) sync() {
	ch := make(chan struct{})
	dump.q <- &dumpreq{dir: nop, sync: ch}
	_ = <-ch
}
func (d *dumper) configure(o *options) {
	d.Lock()
	defer d.Unlock()
	d.debug = o.Debug
	d.rules = o.rules.duplicate()
	if d.path != o.File || d.disabled != o.Disabled {
		if d.file != nil {
			log.Info("closing old message dump file %q...", d.path)
			d.file.Close()
			d.file = nil
		}
		d.disabled = o.Disabled
		if d.disabled {
			return
		}
		d.path = o.File
		if d.path != "" {
			var err error
			log.Info("opening new message dump file %q...", d.path)
			d.file, err = os.Create(d.path)
			if err != nil {
				log.Error("failed to open file %q: %v", d.path, err)
			}
		}
	}
	d.train(nil)
}
func (d *dumper) train(names []string) {
	if names != nil {
		d.methods = make([]string, len(names), len(names))
	} else {
		names = d.methods
	}
	d.details = make(map[string]level)
	for idx, name := range names {
		method := methodName(name)
		detail := d.rules.detailOf(method)
		log.Info("%s: %v", method, detail)
		d.methods[idx] = method
		d.details[method] = detail
	}
}
func (d *dumper) name(dir direction, kind, method, qualifier string, msg interface{}, latency time.Duration) {
	var hdr string
	switch dir {
	case request:
		return
	case reply:
		if qualifier != "" {
			hdr = qualifier + " " + method + " " + dir.arrow() + " "
		} else {
			hdr = method + " " + dir.arrow() + " "
		}
		if err, ok := msg.(error); ok {
			d.warn(dir, latency, hdr+"(%s) FAILED: %v", kind, err)
		} else {
			d.line(dir, latency, hdr+"(%s) REQUEST", kind)
		}
	}
}