httptrace - ActiveState ActiveGo 1.8
...

Package httptrace

import "sourcegraph.com/sourcegraph/appdash/httptrace"
Overview
Index

Overview ▾

Package httptrace implements support for tracing HTTP applications.

This package exposes a HTTP middleware usable for generating traces for measuring the performance and debugging distributed HTTP applications using appdash.

The middleware is Negroni-compliant, and can thus be used with Negroni easily or with a pure net/http (i.e. stdlib-only) application with ease.

Trace Collection Server

Trace collection occurs anywhere (on this HTTP server, remotely on another, etc). It is independent from this package. One approach is to run a local collection server (on the HTTP server itself) that keeps the last 20s of appdash events in-memory, like so:

// Create a recent in-memory store, evicting data after 20s.
store := &appdash.RecentStore{
    MinEvictAge: 20 * time.Second,
    DeleteStore: appdash.NewMemoryStore(),
}

// Listen on port 7701.
ln, err := net.Listen("tcp", ":7701")
if err != nil {
    // handle error
}

// Create an appdash server, listen and serve in a separate goroutine.
cs := appdash.NewServer(ln, appdash.NewLocalCollector(store))
go cs.Start()

Note that the above server exposes the traces in plain-text (i.e. insecurely) over the given port. Allowing access to that port outside your network allows others to potentially see API keys and other information about HTTP requests going through your network.

If you intend to make appdash available outside your network, use a secure appdash server instead (see the appdash package for details).

Server Init

Whether you plan to use Negroni, or just net/http, you'll first need to make a collector. For example, by connecting to the appdash server that we made earlier:

// Connect to a remote collection server.
collector := appdash.NewRemoteCollector(":7701")

And a basic middleware:

// Create a httptrace middleware.
tracemw := httptrace.Middleware(collector, &httptrace.MiddlewareConfig{})

With Negroni

Negroni is a idiomatic web middleware package for Go, and the middleware exposed by this package is fully compliant with it's requirements -- which makes using it a breeze:

// Create app handler:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Hello world!")
})

// Setup Negroni for our app:
n := negroni.Classic()
n.Use(negroni.HandlerFunc(tracemw)) // Register appdash's HTTP middleware.
n.UseHandler(mux)
n.Run(":3000")

With The http Package

The HTTP middleware can also be used without Negroni, although slightly more verbose. Say for example that you have a net/http handler for your app:

func appHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
}

Simply create a middleware and pass each HTTP request through it, continuing with your application handler:

// Let all requests pass through the middleware, and then go on to let our
// app handler serve the request.
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    tracemw(w, r, appHandler)
})

Other details such as outbound client requests, displaying the trace ID in the webpage e.g. to let users give you their trace ID for troubleshooting, and much more are covered in the example application provided at cmd/appdash/example_app.go.

Constants

const (
    // HeaderSpanID is the name of the HTTP header by which the trace
    // and span IDs are passed along.
    HeaderSpanID = "Span-ID"

    // HeaderParentSpanID is the name of the HTTP header by which the
    // parent trace and span IDs are passed along. It should only be
    // set by clients that are incapable of creating their own span
    // IDs (e.g., JavaScript API clients in a web page, which can
    // easily pass along an existing parent span ID but not create a
    // new child span ID).
    HeaderParentSpanID = "Parent-Span-ID"
)

Variables

var (
    // RedactedHeaders is a slice of header names whose values should be
    // entirely redacted from logs.
    RedactedHeaders = []string{"Authorization"}
)

func GetSpanID

func GetSpanID(h http.Header) (*appdash.SpanID, error)

GetSpanID returns the SpanID for the current request, based on the values in the HTTP headers. If a Span-ID header is provided, it is parsed; if a Parent-Span-ID header is provided, a new child span is created and it is returned; otherwise a new root SpanID is created.

func Middleware

func Middleware(c appdash.Collector, conf *MiddlewareConfig) func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

Middleware creates a new http.Handler middleware (negroni-compliant) that records incoming HTTP requests to the collector c as "HTTPServer"-schema events.

func SetSpanIDHeader

func SetSpanIDHeader(h http.Header, e appdash.SpanID)

SetSpanIDHeader sets the Span-ID header.

type ClientEvent

ClientEvent records an HTTP client request event.

type ClientEvent struct {
    Request    RequestInfo  `trace:"Client.Request"`
    Response   ResponseInfo `trace:"Client.Response"`
    ClientSend time.Time    `trace:"Client.Send"`
    ClientRecv time.Time    `trace:"Client.Recv"`
}

func NewClientEvent

func NewClientEvent(r *http.Request) *ClientEvent

NewClientEvent returns an event which records various aspects of an HTTP request. The returned value is incomplete, and should have the response status, size, and the ClientSend/ClientRecv times set before being logged.

func (ClientEvent) End

func (e ClientEvent) End() time.Time

End implements the appdash TimespanEvent interface.

func (ClientEvent) Important

func (ClientEvent) Important() []string

Important implements the appdash ImportantEvent.

func (ClientEvent) Schema

func (ClientEvent) Schema() string

Schema returns the constant "HTTPClient".

func (ClientEvent) Start

func (e ClientEvent) Start() time.Time

Start implements the appdash TimespanEvent interface.

type MiddlewareConfig

MiddlewareConfig configures the HTTP tracing middleware.

type MiddlewareConfig struct {
    // RouteName, if non-nil, is called to get the current route's
    // name. This name is used as the span's name.
    RouteName func(*http.Request) string

    // CurrentUser, if non-nil, is called to get the current user ID
    // (which may be a login or a numeric ID).
    CurrentUser func(*http.Request) string

    // SetContextSpan, if non-nil, is called to set the span (which is
    // either taken from the client request header or created anew) in
    // the HTTP request context, so it may be used by other parts of
    // the handling process.
    SetContextSpan func(*http.Request, appdash.SpanID)
}

type RequestInfo

RequestInfo describes an HTTP request.

type RequestInfo struct {
    Method        string
    URI           string
    Proto         string
    Headers       map[string]string
    Host          string
    RemoteAddr    string
    ContentLength int64
}

type ResponseInfo

ResponseInfo describes an HTTP response.

type ResponseInfo struct {
    Headers       map[string]string
    ContentLength int64
    StatusCode    int
}

type ServerEvent

ServerEvent records an HTTP server request handling event.

type ServerEvent struct {
    Request    RequestInfo  `trace:"Server.Request"`
    Response   ResponseInfo `trace:"Server.Response"`
    Route      string       `trace:"Server.Route"`
    User       string       `trace:"Server.User"`
    ServerRecv time.Time    `trace:"Server.Recv"`
    ServerSend time.Time    `trace:"Server.Send"`
}

func NewServerEvent

func NewServerEvent(r *http.Request) *ServerEvent

NewServerEvent returns an event which records various aspects of an HTTP response. It takes an HTTP request, not response, as input because the information it records is derived from the request, and HTTP handlers don't have access to the response struct (only http.ResponseWriter, which requires wrapping or buffering to introspect).

The returned value is incomplete and should have its Response and ServerRecv/ServerSend values set before being logged.

func (ServerEvent) End

func (e ServerEvent) End() time.Time

End implements the appdash TimespanEvent interface.

func (ServerEvent) Important

func (ServerEvent) Important() []string

Important implements the appdash ImportantEvent.

func (ServerEvent) Schema

func (ServerEvent) Schema() string

Schema returns the constant "HTTPServer".

func (ServerEvent) Start

func (e ServerEvent) Start() time.Time

Start implements the appdash TimespanEvent interface.

type Transport

Transport is an HTTP transport that adds appdash span ID headers to requests so that downstream operations are associated with the same trace.

type Transport struct {
    // Recorder is the current span's recorder. A new child Recorder
    // (with a new child SpanID) is created for each HTTP roundtrip.
    *appdash.Recorder

    // Transport is the underlying HTTP transport to use when making
    // requests.  It will default to http.DefaultTransport if nil.
    Transport http.RoundTripper

    SetName bool
    // contains filtered or unexported fields
}

func (*Transport) CancelRequest

func (t *Transport) CancelRequest(req *http.Request)

CancelRequest cancels an in-flight request by closing its connection.

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(original *http.Request) (*http.Response, error)

RoundTrip implements the RoundTripper interface.