Example #1
0
func sampleData(c appdash.Collector) error {
	const numTraces = 60
	log.Printf("Adding sample data (%d traces)", numTraces)
	for i := appdash.ID(1); i <= numTraces; i++ {
		traceID := appdash.NewRootSpanID()
		traceRec := appdash.NewRecorder(traceID, c)
		traceRec.Name(fakeHosts[rand.Intn(len(fakeHosts))])

		// A random length for the trace.
		length := time.Duration(rand.Intn(1000)) * time.Millisecond

		startTime := time.Now().Add(-time.Duration(rand.Intn(100)) * time.Minute)
		traceRec.Event(&sqltrace.SQLEvent{
			ClientSend: startTime,
			ClientRecv: startTime.Add(length),
			SQL:        "SELECT * FROM table_name;",
			Tag:        fmt.Sprintf("fakeTag%d", rand.Intn(10)),
		})

		// We'll split the trace into N (3-7) spans (i.e. "N operations") each with
		// a random duration of time adding up to the length of the trace.
		numSpans := rand.Intn(7-3) + 3
		times := randomSplit(int(length/time.Millisecond), numSpans)

		lastSpanID := traceID
		for j := 1; j <= numSpans; j++ {
			// The parent span is the predecessor.
			spanID := appdash.NewSpanID(lastSpanID)

			rec := appdash.NewRecorder(spanID, c)
			rec.Name(fakeNames[(j+int(i))%len(fakeNames)])
			if j%3 == 0 {
				rec.Log("hello")
			}
			if j%5 == 0 {
				rec.Msg("hi")
			}

			// Generate a span event.
			spanDuration := time.Duration(times[j-1]) * time.Millisecond
			rec.Event(&sqltrace.SQLEvent{
				ClientSend: startTime,
				ClientRecv: startTime.Add(spanDuration),
				SQL:        "SELECT * FROM table_name;",
				Tag:        fmt.Sprintf("fakeTag%d", rand.Intn(10)),
			})

			// Shift the start time forward.
			startTime = startTime.Add(spanDuration)

			// Check for any recorder errors.
			if errs := rec.Errors(); len(errs) > 0 {
				return fmt.Errorf("recorder errors: %v", errs)
			}

			lastSpanID = spanID
		}
	}
	return nil
}
Example #2
0
// Home is the homepage handler for our app.
func Home(w http.ResponseWriter, r *http.Request) {
	// Grab the span from the gorilla context. We do this so that we can grab
	// the span.Trace ID and link directly to the trace on the web-page itself!
	span := context.Get(r, CtxSpanID).(appdash.SpanID)

	// We're going to make some API requests, so we create a HTTP client using
	// a appdash/httptrace transport here. The transport will inform Appdash of
	// the HTTP events occuring.
	httpClient := &http.Client{
		Transport: &httptrace.Transport{
			Recorder: appdash.NewRecorder(span, collector),
			SetName:  true,
		},
	}

	// Make three API requests using our HTTP client.
	for i := 0; i < 3; i++ {
		resp, err := httpClient.Get("http://localhost:8699/endpoint")
		if err != nil {
			log.Println("/endpoint:", err)
			continue
		}
		resp.Body.Close()
	}

	// Render the page.
	fmt.Fprintf(w, `<p>Three API requests have been made!</p>`)
	fmt.Fprintf(w, `<p><a href="http://localhost:8700/traces/%s" target="_">View the trace (ID:%s)</a></p>`, span.Trace, span.Trace)
}
Example #3
0
func (a *demoApp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	span := requestSpans[r]

	switch r.URL.Path {
	case "/":
		io.WriteString(w, `<h1>Appdash demo</h1>
<p>Welcome! Click some links and then view the traces for each HTTP request by following the link at the bottom of the page.
<ul>
<li><a href="/api-calls">Visit a page that issues some API calls</a></li>
</ul>`)
	case "/api-calls":
		httpClient := &http.Client{
			Transport: &httptrace.Transport{Recorder: appdash.NewRecorder(span, a.collector), SetName: true},
		}
		resp, err := httpClient.Get(a.baseURL.ResolveReference(&url.URL{Path: "/endpoint-A"}).String())
		if err == nil {
			defer resp.Body.Close()
		}
		resp, err = httpClient.Get(a.baseURL.ResolveReference(&url.URL{Path: "/endpoint-B"}).String())
		if err == nil {
			defer resp.Body.Close()
		}
		resp, err = httpClient.Get(a.baseURL.ResolveReference(&url.URL{Path: "/endpoint-C"}).String())
		if err == nil {
			defer resp.Body.Close()
		}
		io.WriteString(w, `<a href="/">Home</a><br><br><p>I just made 3 API calls. Check the trace below to see them!</p>`)
	case "/endpoint-A":
		time.Sleep(250 * time.Millisecond)
		io.WriteString(w, "performed an operation!")
		return
	case "/endpoint-B":
		time.Sleep(75 * time.Millisecond)
		io.WriteString(w, "performed another operation!")
		return
	case "/endpoint-C":
		time.Sleep(300 * time.Millisecond)
		io.WriteString(w, "performed yet another operation!")
		return
	}

	spanURL := a.appdashURL.ResolveReference(&url.URL{Path: fmt.Sprintf("/traces/%v", span.Trace)})
	io.WriteString(w, fmt.Sprintf(`<br><br><hr><a href="%s">View request trace on appdash</a> (trace ID is %s)`, spanURL, span.Trace))
}
Example #4
0
func Home(w http.ResponseWriter, r *http.Request) {
	span := context.Get(r, CtxSpanID).(appdash.SpanID)
	httpClient := &http.Client{
		Transport: &httptrace.Transport{
			Recorder: appdash.NewRecorder(span, collector),
			SetName:  true,
		},
	}
	for i := 0; i < 3; i++ {
		resp, err := httpClient.Get("http://localhost:8699/endpoint")
		if err != nil {
			log.Println("/endpoint:", err)
			continue
		}
		resp.Body.Close()
	}
	fmt.Fprintf(w, `<p>Three API requests have been made!</p>`)
	fmt.Fprintf(w, `<p><a href="http://localhost:8700/traces/%s" target="_">View the trace (ID:%s)</a></p>`, span.Trace, span.Trace)
}
Example #5
0
// Middleware creates a new http.Handler middleware
// (negroni-compliant) that records incoming HTTP requests to the
// collector c as "HTTPServer"-schema events.
func Middleware(c appdash.Collector, conf *MiddlewareConfig) func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	return func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		spanID, spanFromHeader, err := getSpanID(r.Header)
		if err != nil {
			log.Printf("Warning: invalid %s header: %s. (Continuing with request handling.)", spanFromHeader, err)
		}
		usingProvidedSpanID := (spanFromHeader == HeaderSpanID)

		if conf.SetContextSpan != nil {
			conf.SetContextSpan(r, *spanID)
		}

		e := NewServerEvent(r)
		e.ServerRecv = time.Now()

		rr := &responseInfoRecorder{ResponseWriter: rw}
		next(rr, r)
		SetSpanIDHeader(rr.Header(), *spanID)

		if !usingProvidedSpanID {
			e.Request = requestInfo(r)
		}
		if conf.RouteName != nil {
			e.Route = conf.RouteName(r)
		}
		if conf.CurrentUser != nil {
			e.User = conf.CurrentUser(r)
		}
		e.Response = responseInfo(rr.partialResponse())
		e.ServerSend = time.Now()

		rec := appdash.NewRecorder(*spanID, c)
		if e.Route != "" {
			rec.Name("Serve " + e.Route)
		} else {
			rec.Name("Serve " + r.URL.Host + r.URL.Path)
		}
		rec.Event(e)
		rec.Finish()
	}
}
Example #6
0
func TestCancelRequest(t *testing.T) {
	ms := appdash.NewMemoryStore()
	rec := appdash.NewRecorder(appdash.SpanID{1, 2, 3}, appdash.NewLocalCollector(ms))
	req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
	transport := &Transport{
		Recorder: rec,
	}
	client := &http.Client{
		Timeout:   1 * time.Millisecond,
		Transport: transport,
	}

	resp, err := client.Do(req)

	expected := "Get http://example.com/foo: net/http: request canceled while waiting for connection"
	if err == nil || !strings.HasPrefix(err.Error(), expected) {
		t.Errorf("got %#v, want %s", err, expected)
	}
	if resp != nil {
		t.Errorf("got http.Response %#v, want nil", resp)
	}
}
Example #7
0
func TestTransport(t *testing.T) {
	ms := appdash.NewMemoryStore()
	rec := appdash.NewRecorder(appdash.SpanID{1, 2, 3}, appdash.NewLocalCollector(ms))

	req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
	req.Header.Set("X-Req-Header", "a")
	mt := &mockTransport{
		resp: &http.Response{
			StatusCode:    200,
			ContentLength: 123,
			Header:        http.Header{"X-Resp-Header": []string{"b"}},
		},
	}
	transport := &Transport{
		Recorder:  rec,
		Transport: mt,
	}

	_, err := transport.RoundTrip(req)
	if err != nil {
		t.Fatal(err)
	}

	spanID, err := appdash.ParseSpanID(mt.req.Header.Get("Span-ID"))
	if err != nil {
		t.Fatal(err)
	}
	if want := (appdash.SpanID{1, spanID.Span, 2}); *spanID != want {
		t.Errorf("got Span-ID in header %+v, want %+v", *spanID, want)
	}

	trace, err := ms.Trace(1)
	if err != nil {
		t.Fatal(err)
	}

	var e ClientEvent
	if err := appdash.UnmarshalEvent(trace.Span.Annotations, &e); err != nil {
		t.Fatal(err)
	}

	wantEvent := ClientEvent{
		Request: RequestInfo{
			Method:  "GET",
			Proto:   "HTTP/1.1",
			URI:     "/foo",
			Host:    "example.com",
			Headers: map[string]string{"X-Req-Header": "a"},
		},
		Response: ResponseInfo{
			StatusCode:    200,
			ContentLength: 123,
			Headers:       map[string]string{"X-Resp-Header": "b"},
		},
	}
	delete(e.Request.Headers, "Span-Id")
	e.ClientSend = time.Time{}
	e.ClientRecv = time.Time{}
	if !reflect.DeepEqual(e, wantEvent) {
		t.Errorf("got ClientEvent %+v, want %+v", e, wantEvent)
	}
}
Example #8
0
func (w *appdashWrapper) Setup(name string) {
	w.rec = appdash.NewRecorder(appdash.NewRootSpanID(), w.coll)
	w.rec.Name(name)
}
Example #9
0
func handleRequest(w http.ResponseWriter, r *http.Request) {
	var result string
	span := appdash.NewRootSpanID()
	fmt.Println("span is ", span)
	collector := appdash.NewRemoteCollector(":3001")

	httpClient := &http.Client{
		Transport: &httptrace.Transport{
			Recorder: appdash.NewRecorder(span, collector),
			SetName:  true,
		},
	}

	//Service A
	resp, err := httpClient.Get("http://localhost:6601")
	if err != nil {
		log.Println("access serviceA err:", err)
	} else {
		log.Println("access serviceA ok")
		resp.Body.Close()
		result += "access serviceA ok\n"
	}

	//Service B
	resp, err = httpClient.Get("http://localhost:6602")
	if err != nil {
		log.Println("access serviceB err:", err)
		return
	} else {
		log.Println("access serviceB ok")
		resp.Body.Close()
		result += "access serviceB ok\n"
	}

	// SQL event
	traceRec := appdash.NewRecorder(span, collector)
	traceRec.Name("sqlevent example")

	// A random length for the trace.
	length := time.Duration(rand.Intn(1000)) * time.Millisecond
	StartTime := time.Now().Add(-time.Duration(rand.Intn(100)) * time.Minute)
	traceRec.Event(&sqltrace.SQLEvent{
		ClientSend: StartTime,
		ClientRecv: StartTime.Add(length),
		SQL:        "SELECT * FROM table_name;",
		Tag:        fmt.Sprintf("fakeTag%d", rand.Intn(10)),
	})

	result += "sql event ok\n"

	// self-customize event - MyEvent
	StartTime = time.Now().Add(-time.Duration(rand.Intn(100)) * time.Minute)
	traceRec.Event(&MyEvent{
		Name:      "MyEvent example",
		StartTime: StartTime,
		EndTime:   StartTime.Add(length),
	})
	result += "MyEvent ok\n"

	w.Write([]byte(result))
}