package othttp

import "go.opentelemetry.io/otel/plugin/othttp"

Package othttp provides a http.Handler and functions that are intended to be used to add tracing by wrapping existing handlers (with Handler) and routes WithRouteTag.

Index

Examples

Constants

const (
	HostKey       = core.Key("http.host")        // the http host (http.Request.Host)
	MethodKey     = core.Key("http.method")      // the http method (http.Request.Method)
	PathKey       = core.Key("http.path")        // the http path (http.Request.URL.Path)
	URLKey        = core.Key("http.url")         // the http url (http.Request.URL.String())
	UserAgentKey  = core.Key("http.user_agent")  // the http user agent (http.Request.UserAgent())
	RouteKey      = core.Key("http.route")       // the http route (ex: /users/:id)
	StatusCodeKey = core.Key("http.status_code") // if set, the http status
	ReadBytesKey  = core.Key("http.read_bytes")  // if anything was read from the request body, the total number of bytes read
	ReadErrorKey  = core.Key("http.read_error")  // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
	WroteBytesKey = core.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written
	WriteErrorKey = core.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
)

Attribute keys that the Handler can add to a span.

const (
	ReadEvents event = iota
	WriteEvents
)

Different types of events that can be recorded, see WithMessageEvents

Functions

func NewHandler

func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler

NewHandler wraps the passed handler, functioning like middleware, in a span named after the operation and with any provided HandlerOptions.

Example

Code:play 

package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"go.opentelemetry.io/otel/api/core"
	"go.opentelemetry.io/otel/api/global"
	"go.opentelemetry.io/otel/api/trace"
	"go.opentelemetry.io/otel/exporters/trace/stdout"
	"go.opentelemetry.io/otel/plugin/othttp"

	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	/* curl -v -d "a painting" http://localhost:7777/hello/bob/ross
	...
	* upload completely sent off: 10 out of 10 bytes
	< HTTP/1.1 200 OK
	< Traceparent: 00-76ae040ee5753f38edf1c2bd9bd128bd-dd394138cfd7a3dc-01
	< Date: Fri, 04 Oct 2019 02:33:08 GMT
	< Content-Length: 45
	< Content-Type: text/plain; charset=utf-8
	<
	Hello, bob/ross!
	You sent me this:
	a painting
	*/

	// Write spans to stdout
	exporter, err := stdout.NewExporter(stdout.Options{PrettyPrint: true})
	if err != nil {
		log.Fatal(err)
	}

	tp, err := sdktrace.NewProvider(sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
		sdktrace.WithSyncer(exporter))
	if err != nil {
		log.Fatal(err)
	}
	global.SetTraceProvider(tp)

	figureOutName := func(ctx context.Context, s string) (string, error) {
		pp := strings.SplitN(s, "/", 2)
		var err error
		switch pp[1] {
		case "":
			err = fmt.Errorf("expected /hello/:name in %q", s)
		default:
			trace.SpanFromContext(ctx).SetAttributes(core.Key("name").String(pp[1]))
		}
		return pp[1], err
	}

	var mux http.ServeMux
	mux.Handle("/hello/",
		othttp.WithRouteTag("/hello/:name", http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				ctx := r.Context()
				var name string
				// Wrap another function in its own span
				if err := trace.SpanFromContext(ctx).Tracer().WithSpan(ctx, "figureOutName",
					func(ctx context.Context) error {
						var err error
						name, err = figureOutName(ctx, r.URL.Path[1:])
						return err
					}); err != nil {
					log.Println("error figuring out name: ", err)
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}

				d, err := ioutil.ReadAll(r.Body)
				if err != nil {
					log.Println("error reading body: ", err)
					w.WriteHeader(http.StatusBadRequest)
					return
				}

				n, err := io.WriteString(w, "Hello, "+name+"!\nYou sent me this:\n"+string(d))
				if err != nil {
					log.Printf("error writing reply after %d bytes: %s", n, err)
				}
			}),
		),
	)

	if err := http.ListenAndServe(":7777",
		othttp.NewHandler(&mux, "server",
			othttp.WithMessageEvents(othttp.ReadEvents, othttp.WriteEvents),
		),
	); err != nil {
		log.Fatal(err)
	}
}

func WithRouteTag

func WithRouteTag(route string, h http.Handler) http.Handler

WithRouteTag annotates a span with the provided route name using the RouteKey Tag.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is http middleware that corresponds to the http.Handler interface and is designed to wrap a http.Mux (or equivalent), while individual routes on the mux are wrapped with WithRouteTag. A Handler will add various attributes to the span using the core.Keys defined in this package.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves HTTP requests (http.Handler)

type Option

type Option func(*Handler)

Option function used for setting *optional* Handler properties

func WithMessageEvents

func WithMessageEvents(events ...event) Option

WithMessageEvents configures the Handler to record the specified events (span.AddEvent) on spans. By default only summary attributes are added at the end of the request.

Valid events are:

func WithPropagators

func WithPropagators(ps propagation.Propagators) Option

WithPropagators configures the Handler with specific propagators. If this option isn't specified then Propagators with go.opentelemetry.io/otel/api/trace.DefaultHTTPPropagator are used.

func WithPublicEndpoint

func WithPublicEndpoint() Option

WithPublicEndpoint configures the Handler to link the span with an incoming span context. If this option is not provided, then the association is a child association instead of a link.

func WithSpanOptions

func WithSpanOptions(opts ...trace.StartOption) Option

WithSpanOptions configures the Handler with an additional set of trace.StartOptions, which are applied to each new span.

func WithTracer

func WithTracer(tracer trace.Tracer) Option

WithTracer configures the Handler with a specific tracer. If this option isn't specified then the global tracer is used.

Source Files

doc.go handler.go wrap.go

Version
v0.2.3
Published
Mar 4, 2020
Platform
linux/amd64
Imports
8 packages
Last checked
2 hours ago

Tools for package owners.