func (span *Span) Duration() int64 {
	cd, sd, _ := span.Durations()
	switch {
	case cd != 0:
		return cd
	case sd != 0:
		return sd
	}
	return 0
}
Duration of server or client request
package dtrace
import (
	"hash/fnv"
	"time"
)
type Span struct {
	System       string
	TraceID      string
	ProfileID    string
	ParentSpanID string
	ID           string `json:"spanid"`
	Annotations  SpanAnnotations
	Timeline     SpanTimeline
	Revision     int
	Prefix       string
	TraceHashSum uint64
	RawString    string
}
type SpanTimestamp struct {
	Value time.Time
}
func (st *SpanTimestamp) UnmarshalJSON(bytes []byte) (err error) {
	if bytes[0] == '"' {
		bytes = bytes[1:]
	}
	if bytes[len(bytes)-1] == '"' {
		bytes = bytes[0 : len(bytes)-1]
	}
	if bytes[len(bytes)-1] != 'Z' {
		bytes = append(bytes, 'Z')
	}
	st.Value, err = time.Parse(time.RFC3339Nano, string(bytes))
	return err
}
type SpanAnnotations map[string]interface{}
type SpanTimeline map[string]SpanTimestamp
type Trace struct {
	ID        string
	Roots     SpanMap
	Children  map[string]SpanMap
	Spans     SpanMap
	Timestamp time.Time
	ProfileID string
	Root      *Span
	Completed bool
}
type Chain struct {
	Path     string `json:"path"`
	Prefix   string `json:"prefix"`
	Level    int    `json:"level"`
	Duration int64  `json:"duration"`
}
type TraceMap map[string]*Trace
func (traceMap TraceMap) getOrCreate(traceid string) *Trace {
	trace, exists := traceMap[traceid]
	if !exists {
		trace = &Trace{
			ID:       traceid,
			Roots:    make(SpanMap),
			Children: make(map[string]SpanMap),
			Spans:    make(SpanMap),
		}
		traceMap[traceid] = trace
	}
	return trace
}
type SpanMap map[string]*Span
func (spanMap SpanMap) getOrCreate(spanid string) *Span {
	span, exists := spanMap[spanid]
	if !exists {
		span = &Span{
			ID:          spanid,
			Timeline:    make(SpanTimeline),
			Annotations: make(SpanAnnotations),
		}
		spanMap[spanid] = span
	}
	return span
}
func (trace *Trace) getChildren(spanID string) []string {
	var spanIDs []string
	if children, exists := trace.Children[spanID]; exists {
		spanIDs = make([]string, 0, len(children))
		for id := range children {
			spanIDs = append(spanIDs, id)
		}
	}
	return spanIDs
}
func (span *Span) revision() int {
	return int(span.Annotations.get(float64(0), "revision").(float64))
}
func (span *Span) isClient() bool {
	if _, exists := span.Timeline["cs"]; exists {
		return true
	}
	if _, exists := span.Timeline["cr"]; exists {
		return true
	}
	return false
}
func (span *Span) CalculateTraceHashSum() {
	h := fnv.New64()
	h.Write([]byte(span.TraceID))
	hashSum := h.Sum64()
	span.TraceHashSum = hashSum
}
func (span *Span) IsRoot() bool {
	_, exists := span.Annotations["root"]
	return exists
}
func (span *Span) getTimestamp(name string) *time.Time {
	if timestamp, exists := span.Timeline[name]; exists {
		return &timestamp.Value
	}
	return nil
}
func (trace *Trace) patchTargetID(span *Span) {
	if _, exists := span.Annotations["targetid"]; !exists {
		span.Annotations["targetid"] = span.Annotations.get("unknown", "targethost", "host")
	}
	if targetid, _ := span.Annotations["targetid"]; targetid != "unknown" {
		return
	}
	if len(span.ParentSpanID) == 0 {
		return
	}
	parentSpan, exists := trace.Spans[span.ParentSpanID]
	if !exists {
		return
	}
	if _, exists := parentSpan.Annotations["wrapper"]; !exists {
		return
	}
	if targetid, exists := parentSpan.Annotations["targetid"]; exists {
		span.Annotations["targetid"] = targetid
	}
}
func (annotations SpanAnnotations) get(defaultValue interface{}, keys ...string) interface{} {
	for _, key := range keys {
		if value, exists := annotations[key]; exists {
			return value
		}
	}
	return defaultValue
}