httptrace

package
v0.0.0-...-e2786a6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2021 License: MIT Imports: 7 Imported by: 65

Documentation

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.

Index

Constants

View Source
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

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

Functions

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.

func SpanID

func SpanID(r *http.Request) appdash.SpanID

SpanID returns the SpanID set for r by httptrace middleware. It requires that MiddlewareConfig.SetContextSpan is nil. If not, it panics.

func SpanIDFromContext

func SpanIDFromContext(ctx context.Context) (spanID appdash.SpanID, ok bool)

SpanIDFromContext returns the SpanID set for the request context ctx by httptrace middleware. It requires that MiddlewareConfig.SetContextSpan is nil. If not, ok is false.

Types

type ClientEvent

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"`
}

ClientEvent records an HTTP client request event.

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

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 is deprecated. 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)
}

MiddlewareConfig configures the HTTP tracing middleware.

type RequestInfo

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

RequestInfo describes an HTTP request.

type ResponseInfo

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

ResponseInfo describes an HTTP response.

type ServerEvent

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"`
}

ServerEvent records an HTTP server request handling event.

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

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
}

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

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL