Example #1
0
func main() {
	var (
		memStore = appdash.NewMemoryStore()
		Store    = appdash.Store(memStore)
		Queryer  = memStore
	)

	app := traceapp.New(nil)
	app.Store = Store
	app.Queryer = Queryer

	var h http.Handler = app
	var l net.Listener
	var proto string
	var err error
	l, err = net.Listen("tcp", c.CollectorAddr)
	if err != nil {
		log.Fatal(err)
	}
	proto = "plaintext TCP (no security)"
	log.Printf("appdash collector listening on %s (%s)",
		c.CollectorAddr, proto)
	cs := appdash.NewServer(l, appdash.NewLocalCollector(Store))
	go cs.Start()

	log.Printf("appdash HTTP server listening on %s", c.HTTPAddr)
	err = http.ListenAndServe(c.HTTPAddr, h)
	if err != nil {
		fmt.Println("listenandserver listen err:", err)
	}
}
Example #2
0
// Execute execudes the commands with the given arguments and returns an error,
// if any.
func (c *DemoCmd) Execute(args []string) error {
	// We create a new in-memory store. All information about traces will
	// eventually be stored here.
	store := appdash.NewMemoryStore()

	// Listen on any available TCP port locally.
	l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
	if err != nil {
		log.Fatal(err)
	}
	collectorPort := l.Addr().(*net.TCPAddr).Port
	log.Printf("Appdash collector listening on tcp:%d", collectorPort)

	// Start an Appdash collection server that will listen for spans and
	// annotations and add them to the local collector (stored in-memory).
	cs := appdash.NewServer(l, appdash.NewLocalCollector(store))
	cs.Debug = c.Debug // Debug logging
	cs.Trace = c.Trace // Trace logging
	go cs.Start()

	// Print the URL at which the web UI will be running.
	appdashURLStr := "http://localhost" + c.AppdashHTTPAddr
	appdashURL, err := url.Parse(appdashURLStr)
	if err != nil {
		log.Fatalf("Error parsing http://localhost:%s: %s", c.AppdashHTTPAddr, err)
	}
	log.Printf("Appdash web UI running at %s", appdashURL)

	// Start the web UI in a separate goroutine.
	tapp := traceapp.New(nil)
	tapp.Store = store
	tapp.Queryer = store
	go func() {
		log.Fatal(http.ListenAndServe(c.AppdashHTTPAddr, tapp))
	}()

	// Print the URL at which the demo app is running.
	demoURLStr := "http://localhost" + c.DemoHTTPAddr
	demoURL, err := url.Parse(demoURLStr)
	if err != nil {
		log.Fatalf("Error parsing http://localhost:%s: %s", c.DemoHTTPAddr, err)
	}
	log.Println()
	log.Printf("Appdash demo app running at %s", demoURL)

	// The Appdash collection server that our demo app will use is running
	// locally with our HTTP server in this case, so we set this up now.
	localCollector := appdash.NewRemoteCollector(fmt.Sprintf(":%d", collectorPort))

	// Handle the root path of our app.
	http.Handle("/", &middlewareHandler{
		middleware: httptrace.Middleware(localCollector, &httptrace.MiddlewareConfig{
			RouteName:      func(r *http.Request) string { return r.URL.Path },
			SetContextSpan: requestSpans.setRequestSpan,
		}),
		next: &demoApp{collector: localCollector, baseURL: demoURL, appdashURL: appdashURL},
	})
	return http.ListenAndServe(c.DemoHTTPAddr, nil)
}
Example #3
0
func main() {
	// Create a recent in-memory store, evicting data after 20s.
	//
	// The store defines where information about traces (i.e. spans and
	// annotations) will be stored during the lifetime of the application. This
	// application uses a MemoryStore store wrapped by a RecentStore with an
	// eviction time of 20s (i.e. all data after 20s is deleted from memory).
	memStore := appdash.NewMemoryStore()
	store := &appdash.RecentStore{
		MinEvictAge: 20 * time.Second,
		DeleteStore: memStore,
	}

	// Start the Appdash web UI on port 8700.
	//
	// This is the actual Appdash web UI -- usable as a Go package itself, We
	// embed it directly into our application such that visiting the web server
	// on HTTP port 8700 will bring us to the web UI, displaying information
	// about this specific web-server (another alternative would be to connect
	// to a centralized Appdash collection server).
	url, err := url.Parse("http://localhost:8700")
	if err != nil {
		log.Fatal(err)
	}
	tapp, err := traceapp.New(nil, url)
	if err != nil {
		log.Fatal(err)
	}
	tapp.Store = store
	tapp.Queryer = memStore
	log.Println("Appdash web UI running on HTTP :8700")
	go func() {
		log.Fatal(http.ListenAndServe(":8700", tapp))
	}()

	// We will use a local collector (as we are running the Appdash web UI
	// embedded within our app).
	//
	// A collector is responsible for collecting the information about traces
	// (i.e. spans and annotations) and placing them into a store. In this app
	// we use a local collector (we could also use a remote collector, sending
	// the information to a remote Appdash collection server).
	collector := appdash.NewLocalCollector(store)

	// Here we use the local collector to create a new opentracing.Tracer
	tracer := appdashtracer.NewTracer(collector)
	opentracing.InitGlobalTracer(tracer)

	// Setup our router (for information, see the gorilla/mux docs):
	router := mux.NewRouter()
	router.HandleFunc("/", Home)
	router.HandleFunc("/endpoint", Endpoint)

	// Setup Negroni for our app (for information, see the negroni docs):
	n := negroni.Classic()
	n.UseHandler(router)
	n.Run(":8699")
}
Example #4
0
func TestMiddleware_useSpanFromHeaders(t *testing.T) {
	ms := appdash.NewMemoryStore()
	c := appdash.NewLocalCollector(ms)

	req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
	req.Header.Set("X-Req-Header", "a")

	spanID := appdash.SpanID{1, 2, 3}
	SetSpanIDHeader(req.Header, spanID)

	var setContextSpan appdash.SpanID
	mw := Middleware(c, &MiddlewareConfig{
		RouteName:      func(r *http.Request) string { return "r" },
		CurrentUser:    func(r *http.Request) string { return "u" },
		SetContextSpan: func(r *http.Request, id appdash.SpanID) { setContextSpan = id },
	})

	w := httptest.NewRecorder()
	mw(w, req, func(http.ResponseWriter, *http.Request) {})

	if setContextSpan != spanID {
		t.Errorf("set context span to %v, want %v", setContextSpan, spanID)
	}

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

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

	wantEvent := ServerEvent{
		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,
			Headers:    map[string]string{"Span-Id": "0000000000000001/0000000000000002/0000000000000003"},
		},
		User:  "******",
		Route: "r",
	}

	delete(e.Request.Headers, "Span-Id")
	e.ServerRecv = time.Time{}
	e.ServerSend = time.Time{}
	if !reflect.DeepEqual(e, wantEvent) {
		t.Errorf("got ServerEvent %+v, want %+v", e, wantEvent)
	}
}
Example #5
0
func TestMiddleware_createNewSpan(t *testing.T) {
	ms := appdash.NewMemoryStore()
	c := appdash.NewLocalCollector(ms)

	req, _ := http.NewRequest("GET", "http://example.com/foo", nil)

	var setContextSpan appdash.SpanID
	mw := Middleware(c, &MiddlewareConfig{
		SetContextSpan: func(r *http.Request, id appdash.SpanID) { setContextSpan = id },
	})

	w := httptest.NewRecorder()
	mw(w, req, func(http.ResponseWriter, *http.Request) {})

	if setContextSpan == (appdash.SpanID{0, 0, 0}) {
		t.Errorf("context span is zero, want it to be set")
	}

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

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

	wantEvent := ServerEvent{
		Request: RequestInfo{
			Method: "GET",
			Proto:  "HTTP/1.1",
			URI:    "/foo",
			Host:   "example.com",
		},
		Response: ResponseInfo{
			StatusCode: 200,
			Headers:    map[string]string{"Span-Id": setContextSpan.String()},
		},
	}
	delete(e.Request.Headers, "Span-Id")
	e.ServerRecv = time.Time{}
	e.ServerSend = time.Time{}
	if !reflect.DeepEqual(e, wantEvent) {
		t.Errorf("got ServerEvent %+v, want %+v", e, wantEvent)
	}
}
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
// Execute execudes the commands with the given arguments and returns an error,
// if any.
func (c *ServeCmd) Execute(args []string) error {
	var (
		memStore = appdash.NewMemoryStore()
		Store    = appdash.Store(memStore)
		Queryer  = memStore
	)

	if c.StoreFile != "" {
		f, err := os.Open(c.StoreFile)
		if err != nil && !os.IsNotExist(err) {
			return err
		}
		if f != nil {
			if n, err := memStore.ReadFrom(f); err == nil {
				log.Printf("Read %d traces from file %s", n, c.StoreFile)
			} else if err != nil {
				f.Close()
				return err
			}
			if err := f.Close(); err != nil {
				return err
			}
		}
		if c.PersistInterval != 0 {
			go func() {
				if err := appdash.PersistEvery(memStore, c.PersistInterval, c.StoreFile); err != nil {
					log.Fatal(err)
				}
			}()
		}
	}

	if c.DeleteAfter > 0 {
		Store = &appdash.RecentStore{
			MinEvictAge: c.DeleteAfter,
			DeleteStore: memStore,
			Debug:       true,
		}
	}

	url, err := c.urlOrDefault()
	if err != nil {
		log.Fatal(err)
	}
	app, err := traceapp.New(nil, url)
	if err != nil {
		log.Fatal(err)
	}
	app.Store = Store
	app.Queryer = Queryer

	var h http.Handler
	if c.BasicAuth != "" {
		parts := strings.SplitN(c.BasicAuth, ":", 2)
		if len(parts) != 2 {
			log.Fatalf("Basic auth must be specified as 'user:passwd'.")
		}
		user, passwd := parts[0], parts[1]
		if user == "" || passwd == "" {
			log.Fatalf("Basic auth user and passwd must both be nonempty.")
		}
		log.Printf("Requiring HTTP Basic auth")
		h = newBasicAuthHandler(user, passwd, app)
	} else {
		h = app
	}

	if c.SampleData {
		sampleData(Store)
	}

	var l net.Listener
	var proto string
	if c.TLSCert != "" || c.TLSKey != "" {
		certBytes, err := ioutil.ReadFile(c.TLSCert)
		if err != nil {
			log.Fatal(err)
		}
		keyBytes, err := ioutil.ReadFile(c.TLSKey)
		if err != nil {
			log.Fatal(err)
		}

		var tc tls.Config
		cert, err := tls.X509KeyPair(certBytes, keyBytes)
		if err != nil {
			log.Fatal(err)
		}
		tc.Certificates = []tls.Certificate{cert}
		l, err = tls.Listen("tcp", c.CollectorAddr, &tc)
		if err != nil {
			log.Fatal(err)
		}
		proto = fmt.Sprintf("TLS cert %s, key %s", c.TLSCert, c.TLSKey)
	} else {
		var err error
		l, err = net.Listen("tcp", c.CollectorAddr)
		if err != nil {
			log.Fatal(err)
		}
		proto = "plaintext TCP (no security)"
	}
	log.Printf("appdash collector listening on %s (%s)", c.CollectorAddr, proto)
	cs := appdash.NewServer(l, appdash.NewLocalCollector(Store))
	cs.Debug = c.Debug
	cs.Trace = c.Trace
	go cs.Start()

	if c.TLSCert != "" || c.TLSKey != "" {
		log.Printf("appdash HTTPS server listening on %s (TLS cert %s, key %s)", c.HTTPAddr, c.TLSCert, c.TLSKey)
		return http.ListenAndServeTLS(c.HTTPAddr, c.TLSCert, c.TLSKey, h)
	}

	log.Printf("appdash HTTP server listening on %s", c.HTTPAddr)
	return http.ListenAndServe(c.HTTPAddr, h)
}
Example #8
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 #9
0
func main() {
	// Create a recent in-memory store, evicting data after 20s.
	//
	// The store defines where information about traces (i.e. spans and
	// annotations) will be stored during the lifetime of the application. This
	// application uses a MemoryStore store wrapped by a RecentStore with an
	// eviction time of 20s (i.e. all data after 20s is deleted from memory).
	memStore := appdash.NewMemoryStore()
	store := &appdash.RecentStore{
		MinEvictAge: 20 * time.Second,
		DeleteStore: memStore,
	}

	// Start the Appdash web UI on port 8700.
	//
	// This is the actual Appdash web UI -- usable as a Go package itself, We
	// embed it directly into our application such that visiting the web server
	// on HTTP port 8700 will bring us to the web UI, displaying information
	// about this specific web-server (another alternative would be to connect
	// to a centralized Appdash collection server).
	url, err := url.Parse("http://localhost:8700")
	if err != nil {
		log.Fatal(err)
	}
	tapp, err := traceapp.New(nil, url)
	if err != nil {
		log.Fatal(err)
	}
	tapp.Store = store
	tapp.Queryer = memStore
	log.Println("Appdash web UI running on HTTP :8700")
	go func() {
		log.Fatal(http.ListenAndServe(":8700", tapp))
	}()

	// We will use a local collector (as we are running the Appdash web UI
	// embedded within our app).
	//
	// A collector is responsible for collecting the information about traces
	// (i.e. spans and annotations) and placing them into a store. In this app
	// we use a local collector (we could also use a remote collector, sending
	// the information to a remote Appdash collection server).
	collector = appdash.NewLocalCollector(store)

	// Create the appdash/httptrace middleware.
	//
	// Here we initialize the appdash/httptrace middleware. It is a Negroni
	// compliant HTTP middleware that will generate HTTP events for Appdash to
	// display. We could also instruct Appdash with events manually, if we
	// wanted to.
	tracemw := httptrace.Middleware(collector, &httptrace.MiddlewareConfig{
		RouteName: func(r *http.Request) string { return r.URL.Path },
		SetContextSpan: func(r *http.Request, spanID appdash.SpanID) {
			context.Set(r, CtxSpanID, spanID)
		},
	})

	// Setup our router (for information, see the gorilla/mux docs):
	router := mux.NewRouter()
	router.HandleFunc("/", Home)
	router.HandleFunc("/endpoint", Endpoint)

	// Setup Negroni for our app (for information, see the negroni docs):
	n := negroni.Classic()
	n.Use(negroni.HandlerFunc(tracemw)) // Register appdash's HTTP middleware.
	n.UseHandler(router)
	n.Run(":8699")
}