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) } }
// 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) }
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") }
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) } }
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) } }
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) } }
// 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) }
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) } }
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") }
func main() { // Create a default InfluxDB configuration. conf, err := appdash.NewInfluxDBConfig() if err != nil { log.Fatalf("failed to create influxdb config, error: %v", err) } // Enable InfluxDB server HTTP basic auth. conf.Server.HTTPD.AuthEnabled = true conf.AdminUser = appdash.InfluxDBAdminUser{ Username: "******", Password: "******", } // If you do not want metrics to be reported (see: https://docs.influxdata.com/influxdb/v0.10/administration/config/#reporting-disabled-false) uncomment the following line: //conf.Server.ReportingDisabled = true // Configure InfluxDB ports, if you desire: //conf.Server.Admin.BindAddress = ":8083" //conf.Server.BindAddress = ":8088" //conf.Server.CollectdInputs[0].BindAddress = "" // auto-chosen //conf.Server.GraphiteInputs[0].BindAddress = ":2003" //conf.Server.HTTPD.BindAddress = ":8086" //conf.Server.OpenTSDBInputs[0].BindAddress = ":4242" //conf.Server.UDPInputs[0].BindAddress = "" // auto-chosen // Control where InfluxDB server logs are written to, if desired: //conf.LogOutput = ioutil.Discard store, err := appdash.NewInfluxDBStore(conf) if err != nil { log.Fatalf("failed to create influxdb store, error: %v", err) } defer func() { if err := store.Close(); err != nil { log.Fatal(err) } }() 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 = store tapp.Aggregator = store log.Println("Appdash web UI running on HTTP :8700") go func() { log.Fatal(http.ListenAndServe(":8700", tapp)) }() collector = appdash.NewLocalCollector(store) 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) }, }) router := mux.NewRouter() router.HandleFunc("/", Home) router.HandleFunc("/endpoint", Endpoint) n := negroni.Classic() n.Use(negroni.HandlerFunc(tracemw)) n.UseHandler(router) n.Run(":8699") }