Пример #1
0
func (a *App) serveTrace(w http.ResponseWriter, r *http.Request) error {
	v := mux.Vars(r)

	traceID, err := appdash.ParseID(v["Trace"])
	if err != nil {
		return err
	}

	trace, err := a.Store.Trace(traceID)
	if err != nil {
		return err
	}

	// Get sub-span if the Span route var is present.
	if spanIDStr := v["Span"]; spanIDStr != "" {
		spanID, err := appdash.ParseID(spanIDStr)
		if err != nil {
			return err
		}
		trace = trace.FindSpan(spanID)
		if trace == nil {
			return errors.New("could not find the specified trace span")
		}
	}

	// We could use a separate handler for this, but as we need the above to
	// determine the correct trace (or therein sub-trace), we just handle any
	// JSON profile requests here.
	if path.Base(r.URL.Path) == "profile" {
		return a.profile(trace, w)
	}

	visData, err := a.d3timeline(trace)
	if err != nil {
		return err
	}

	// Determine the profile URL.
	var profile *url.URL
	if trace.ID.Parent == 0 {
		profile, err = a.Router.URLToTraceProfile(trace.Span.ID.Trace)
	} else {
		profile, err = a.Router.URLToTraceSpanProfile(trace.Span.ID.Trace, trace.Span.ID.Span)
	}
	if err != nil {
		return err
	}

	return a.renderTemplate(w, r, "trace.html", http.StatusOK, &struct {
		TemplateCommon
		Trace      *appdash.Trace
		VisData    []timelineItem
		ProfileURL string
	}{
		Trace:      trace,
		VisData:    visData,
		ProfileURL: profile.String(),
	})
}
Пример #2
0
func (a *App) serveTraces(w http.ResponseWriter, r *http.Request) error {
	// Parse the query for a comma-separated list of traces that we should only
	// show (all others are hidden).
	var showJust []appdash.ID
	if show := r.URL.Query().Get("show"); len(show) > 0 {
		for _, idStr := range strings.Split(show, ",") {
			id, err := appdash.ParseID(idStr)
			if err == nil {
				showJust = append(showJust, id)
			}
		}
	}

	traces, err := a.Queryer.Traces(appdash.TracesOpts{
		TraceIDs: showJust,
	})
	if err != nil {
		return err
	}

	return a.renderTemplate(w, r, "traces.html", http.StatusOK, &struct {
		TemplateCommon
		Traces  []*appdash.Trace
		Visible func(*appdash.Trace) bool
	}{
		Traces: traces,
		Visible: func(t *appdash.Trace) bool {
			return true
		},
	})
}
Пример #3
0
func (a *App) serveTraces(w http.ResponseWriter, r *http.Request) error {
	traces, err := a.Queryer.Traces()
	if err != nil {
		return err
	}

	// Parse the query for a comma-separated list of traces that we should only
	// show (all others are hidden).
	var showJust []appdash.ID
	if show := r.URL.Query().Get("show"); len(show) > 0 {
		for _, idStr := range strings.Split(show, ",") {
			id, err := appdash.ParseID(idStr)
			if err == nil {
				showJust = append(showJust, id)
			}
		}
	}

	// Sort the traces by ID to ensure that the display order doesn't change upon
	// multiple page reloads if Queryer.Traces is e.g. backed by a map (which has
	// a random iteration order).
	sort.Sort(tracesByID(traces))

	return a.renderTemplate(w, r, "traces.html", http.StatusOK, &struct {
		TemplateCommon
		Traces  []*appdash.Trace
		Visible func(*appdash.Trace) bool
	}{
		Traces: traces,
		Visible: func(t *appdash.Trace) bool {
			// Hide the traces that contain aggregate events (that's all they have, so
			// they are not very useful to users).
			if t.IsAggregate() {
				return false
			}

			if len(showJust) > 0 {
				// Showing just a few traces.
				for _, id := range showJust {
					if id == t.Span.ID.Trace {
						return true
					}
				}
				return false
			}
			return true
		},
	})
}
Пример #4
0
func (a *App) serveAggregate(w http.ResponseWriter, r *http.Request) error {
	// By default we display all traces.
	traces, err := a.Queryer.Traces()
	if err != nil {
		return err
	}

	q := r.URL.Query()

	// If they specified a comma-separated list of specific trace IDs that they
	// are interested in, then we only show those.
	selection := q.Get("selection")
	if len(selection) > 0 {
		var selected []*appdash.Trace
		for _, idStr := range strings.Split(selection, ",") {
			id, err := appdash.ParseID(idStr)
			if err != nil {
				return err
			}
			for _, t := range traces {
				if t.Span.ID.Trace == id {
					selected = append(selected, t)
				}
			}
		}
		traces = selected
	}

	// Perform the aggregation and render the data.
	aggregated, err := a.aggregate(traces, parseAggMode(q.Get("view-mode")))
	if err != nil {
		return err
	}
	return a.renderTemplate(w, r, "aggregate.html", http.StatusOK, &struct {
		TemplateCommon
		Aggregated []*aggItem
	}{
		Aggregated: aggregated,
	})
}
Пример #5
0
func (a *App) serveTrace(w http.ResponseWriter, r *http.Request) error {
	v := mux.Vars(r)

	traceID, err := appdash.ParseID(v["Trace"])
	if err != nil {
		return err
	}

	trace, err := a.Store.Trace(traceID)
	if err != nil {
		return err
	}

	// Get sub-span if the Span route var is present.
	if spanIDStr := v["Span"]; spanIDStr != "" {
		spanID, err := appdash.ParseID(spanIDStr)
		if err != nil {
			return err
		}
		trace = trace.FindSpan(spanID)
		if trace == nil {
			return errors.New("could not find the specified trace span")
		}
	}

	// We could use a separate handler for this, but as we need the above to
	// determine the correct trace (or therein sub-trace), we just handle any
	// JSON profile requests here.
	if path.Base(r.URL.Path) == "profile" {
		return a.profile(trace, w)
	}

	// Do not show d3 timeline chart when timeline item fields are invalid.
	// So we avoid JS code breaking due missing values.
	var showTimelineChart bool = true
	visData, err := a.d3timeline(trace)
	switch err {
	case ErrTimelineItemValidation:
		showTimelineChart = false
	case nil:
		break
	default:
		return err
	}

	// Determine the profile URL.
	var profile *url.URL
	if trace.ID.Parent == 0 {
		profile, err = a.Router.URLToTraceProfile(trace.Span.ID.Trace)
	} else {
		profile, err = a.Router.URLToTraceSpanProfile(trace.Span.ID.Trace, trace.Span.ID.Span)
	}
	if err != nil {
		return err
	}

	return a.renderTemplate(w, r, "trace.html", http.StatusOK, &struct {
		TemplateCommon
		Trace             *appdash.Trace
		ShowTimelineChart bool
		VisData           []timelineItem
		ProfileURL        string
	}{
		Trace:             trace,
		ShowTimelineChart: showTimelineChart,
		VisData:           visData,
		ProfileURL:        profile.String(),
	})
}
Пример #6
0
func (a *App) serveTrace(w http.ResponseWriter, r *http.Request) error {
	v := mux.Vars(r)

	if permalink := r.URL.Query().Get("permalink"); permalink != "" {
		// If the user specified a permalink, then decode it directly into a
		// trace structure and place it into storage for viewing.
		gz, err := gzip.NewReader(base64.NewDecoder(base64.RawURLEncoding, strings.NewReader(permalink)))
		if err != nil {
			return err
		}
		var upload *appdash.Trace
		if err := json.NewDecoder(gz).Decode(&upload); err != nil {
			return err
		}
		if err := a.uploadTraces(upload); err != nil {
			return err
		}
	}

	// Look in the store for the trace.
	traceID, err := appdash.ParseID(v["Trace"])
	if err != nil {
		return err
	}

	trace, err := a.Store.Trace(traceID)
	if err != nil {
		return err
	}

	// Get sub-span if the Span route var is present.
	if spanIDStr := v["Span"]; spanIDStr != "" {
		spanID, err := appdash.ParseID(spanIDStr)
		if err != nil {
			return err
		}
		trace = trace.FindSpan(spanID)
		if trace == nil {
			return errors.New("could not find the specified trace span")
		}
	}

	// We could use a separate handler for this, but as we need the above to
	// determine the correct trace (or therein sub-trace), we just handle any
	// JSON profile requests here.
	if path.Base(r.URL.Path) == "profile" {
		return a.profile(trace, w)
	}

	// Do not show d3 timeline chart when timeline item fields are invalid.
	// So we avoid JS code breaking due missing values.
	var showTimelineChart bool = true
	visData, err := a.d3timeline(trace)
	switch err {
	case errTimelineItemValidation:
		showTimelineChart = false
	case nil:
		break
	default:
		return err
	}

	// Determine the profile URL.
	var profile *url.URL
	if trace.ID.Parent == 0 {
		profile, err = a.Router.URLToTraceProfile(trace.Span.ID.Trace)
	} else {
		profile, err = a.Router.URLToTraceSpanProfile(trace.Span.ID.Trace, trace.Span.ID.Span)
	}
	if err != nil {
		return err
	}

	// The JSON trace is the human-readable trace form for exporting.
	jsonTrace, err := json.MarshalIndent([]*appdash.Trace{trace}, "", "  ")
	if err != nil {
		return err
	}

	// The permalink of the trace is literally the JSON encoded trace gzipped & base64 encoded.
	var buf bytes.Buffer
	gz := gzip.NewWriter(base64.NewEncoder(base64.RawURLEncoding, &buf))
	err = json.NewEncoder(gz).Encode(trace)
	if err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}
	permalink, err := a.URLToTrace(trace.ID.Trace)
	if err != nil {
		return err
	}
	permalink.RawQuery = "permalink=" + buf.String()

	return a.renderTemplate(w, r, "trace.html", http.StatusOK, &struct {
		TemplateCommon
		Trace             *appdash.Trace
		ShowTimelineChart bool
		VisData           []timelineItem
		ProfileURL        string
		Permalink         string
		JSONTrace         string
	}{
		Trace:             trace,
		ShowTimelineChart: showTimelineChart,
		VisData:           visData,
		ProfileURL:        profile.String(),
		Permalink:         permalink.String(),
		JSONTrace:         string(jsonTrace),
	})
}