Giter Club home page Giter Club logo

Comments (2)

slimsag avatar slimsag commented on May 20, 2024

Hi @cgilling! -- good question!

The code is written that way (creating a new http.Client upon each request) primarily for simplicity -- it's not too difficult to rework it and make it more efficient and support persistent connections, I've hacked the example into this which uses a global http.Client instead by swapping out the recorder on the http.Transport of the http.Client (see below).

In a real application you will probably want to use a pool of clients rather than a single global one for all incoming requests to your app (depending on what you are doing) -- but the concepts here remain applicable to that situation just as well.

Let me know if you have any other questions! 😃

// webapp: a standalone example Negroni / Gorilla based webapp.
//
// This example demonstrates basic usage of Appdash in a Negroni / Gorilla
// based web application. The entire application is ran locally (i.e. on the
// same server) -- even the Appdash web UI.
package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
    "time"

    "sourcegraph.com/sourcegraph/appdash"
    "sourcegraph.com/sourcegraph/appdash/httptrace"
    "sourcegraph.com/sourcegraph/appdash/traceapp"

    "github.com/codegangsta/negroni"
    "github.com/gorilla/context"
    "github.com/gorilla/mux"
)

// Used to store the SpanID in a request's context (see gorilla/context docs
// for more information).
const CtxSpanID = 0

// We want to create HTTP clients recording to this collector inside our Home
// handler below, so we use a global variable (for simplicity sake) to store
// the collector in use. We could also use gorilla/context to store it.
var collector appdash.Collector

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).
    tapp := traceapp.New(nil)
    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")
}

// The global HTTP client and transport:
//
// Note: in a high-performance app you would probably want to use a pool of
// reusable HTTP clients, rather than a single one for all requests.
var (
    clientLock    sync.Mutex
    httpTransport = &httptrace.Transport{
        SetName: true,
    }
    httpClient = &http.Client{Transport: httpTransport}
)

// 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)

    // Protect against data races on the client as HTTP handlers are invoked in
    // different goroutines.
    clientLock.Lock()
    defer clientLock.Unlock()

    // Assign the transport's event recorder.
    httpTransport.Recorder = appdash.NewRecorder(span, collector)

    // 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)
}

// Endpoint is an example API endpoint. In a real application, the backend of
// your service would be contacting several external and internal API endpoints
// which may be the bottleneck of your application.
//
// For example purposes we just sleep for 200ms before responding to simulate a
// slow API endpoint as the bottleneck of your application.
func Endpoint(w http.ResponseWriter, r *http.Request) {
    time.Sleep(200 * time.Millisecond)
    fmt.Fprintf(w, "Slept for 200ms!")
}

from appdash.

slimsag avatar slimsag commented on May 20, 2024

I'm going to close this issue as it appears a bit stale. @cgilling if you have any other questions just let me know and I'll re-open this 😃

from appdash.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.