Beispiel #1
0
func (a *App) d3timelineInner(t *appdash.Trace, depth int) ([]timelineItem, error) {
	var items []timelineItem

	var events []appdash.Event
	if err := appdash.UnmarshalEvents(t.Span.Annotations, &events); err != nil {
		return nil, err
	}

	var u *url.URL
	if t.ID.Parent == 0 {
		var err error
		u, err = a.URLToTrace(t.ID.Trace)
		if err != nil {
			return nil, err
		}
	} else {
		var err error
		u, err = a.URLToTraceSpan(t.ID.Trace, t.ID.Span)
		if err != nil {
			return nil, err
		}
	}

	item := timelineItem{
		Label:     t.Span.Name(),
		FullLabel: t.Span.Name(),
		Data:      t.Annotations.StringMap(),
		SpanID:    t.Span.ID.Span.String(),
		URL:       u.String(),
	}

	if !item.Valid() {
		return nil, ErrTimelineItemValidation
	}

	if t.Span.ID.Parent != 0 {
		item.ParentSpanID = t.Span.ID.Parent.String()
	}
	if depth <= 1 {
		item.Visible = true
	}
	for _, e := range events {
		if e, ok := e.(appdash.TimespanEvent); ok {
			start := e.Start().UnixNano() / int64(time.Millisecond)
			end := e.End().UnixNano() / int64(time.Millisecond)
			ts := timelineItemTimespan{
				Start: start,
				End:   end,
			}
			if t.Span.ID.Parent == 0 {
				ts.Label = e.Schema()
				item.Times = append(item.Times, &ts)
			} else {
				if item.Times == nil {
					item.Times = append(item.Times, &ts)
				} else {
					if item.Times[0].Start > start {
						item.Times[0].Start = start
					}
					if item.Times[0].End < end {
						item.Times[0].End = end
					}
				}
			}
		}
	}
	for _, ts := range item.Times {
		msec := time.Duration(item.Times[0].End-item.Times[0].Start) * time.Millisecond
		if msec > 0 {
			ts.Label = fmt.Sprintf("%s (%s)", item.Label, msec)
			ts.Duration = int64(msec)
		}
	}
	if len(item.Times) == 0 {
		// Items with a null times array will crash d3-timeline.js as it tries
		// to iterate over it. This means the trace doesn't have a single
		// TimespanEvent and is thus invalid.
		return nil, nil
	}
	items = append(items, item)

	for _, child := range t.Sub {
		subItems, err := a.d3timelineInner(child, depth+1)
		if err != nil {
			return nil, err
		}
		items = append(items, subItems...)
	}

	return items, nil
}
Beispiel #2
0
// calcProfile calculates a profile for the given trace and appends it to the
// given buffer (buf), which is then returned (prof). If an error is returned,
// all other returned values are nil. The childProf is literally the *profile
// associated with the given trace (t).
func (a *App) calcProfile(buf []*profile, t *appdash.Trace) (prof []*profile, childProf *profile, err error) {
	// Unmarshal the trace's span events.
	var events []appdash.Event
	if err := appdash.UnmarshalEvents(t.Span.Annotations, &events); err != nil {
		return nil, nil, err
	}

	// Get the proper URL to the trace view.
	var u *url.URL
	if t.ID.Parent == 0 {
		u, err = a.URLToTrace(t.ID.Trace)
		if err != nil {
			return nil, nil, err
		}
	} else {
		u, err = a.URLToTraceSpan(t.ID.Trace, t.ID.Span)
		if err != nil {
			return nil, nil, err
		}
	}

	// Initialize the span's profile structure. We use either the span's given
	// name, or it's ID as a string if it has no given name.
	p := &profile{
		Name: t.Span.Name(),
		URL:  u.String(),
	}
	if len(p.Name) == 0 {
		p.Name = t.Span.ID.Span.String()
	}
	buf = append(buf, p)

	// Store the time for the largest timespan event the span has.
	for _, ev := range events {
		ts, ok := ev.(appdash.TimespanEvent)
		if !ok {
			continue
		}
		// To match the timeline properly we use floats and round up.
		msf := float64(ts.End().Sub(ts.Start())) / float64(time.Millisecond)
		ms := int64(msf + 0.5)
		if ms > p.Time {
			p.Time = ms
		}
	}

	// TimeChildren is our time + the children's time.
	p.TimeChildren = p.Time

	// The cumulative time is our time + all children's time.
	p.TimeCum = p.Time

	// Descend recursively into each sub-trace and calculate the profile for
	// each child span.
	for _, child := range t.Sub {
		buf, childProf, err = a.calcProfile(buf, child)
		if err != nil {
			return nil, nil, err
		}

		// Aggregate our direct children's time.
		p.TimeChildren += childProf.Time

		// As our child's profile has the cumulative time (which is initially,
		// it's self time) -- we can simply aggregate it here and we have our
		// trace's cumulative time (i.e. it is effectively recursive).
		p.TimeCum += childProf.TimeCum
	}
	return buf, p, nil
}