mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2018, OpenCensus Authors
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package ochttp
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"go.opencensus.io/stats"
 | |
| 	"go.opencensus.io/tag"
 | |
| 	"go.opencensus.io/trace"
 | |
| 	"go.opencensus.io/trace/propagation"
 | |
| )
 | |
| 
 | |
| // Handler is an http.Handler wrapper to instrument your HTTP server with
 | |
| // OpenCensus. It supports both stats and tracing.
 | |
| //
 | |
| // Tracing
 | |
| //
 | |
| // This handler is aware of the incoming request's span, reading it from request
 | |
| // headers as configured using the Propagation field.
 | |
| // The extracted span can be accessed from the incoming request's
 | |
| // context.
 | |
| //
 | |
| //    span := trace.FromContext(r.Context())
 | |
| //
 | |
| // The server span will be automatically ended at the end of ServeHTTP.
 | |
| type Handler struct {
 | |
| 	// Propagation defines how traces are propagated. If unspecified,
 | |
| 	// B3 propagation will be used.
 | |
| 	Propagation propagation.HTTPFormat
 | |
| 
 | |
| 	// Handler is the handler used to handle the incoming request.
 | |
| 	Handler http.Handler
 | |
| 
 | |
| 	// StartOptions are applied to the span started by this Handler around each
 | |
| 	// request.
 | |
| 	//
 | |
| 	// StartOptions.SpanKind will always be set to trace.SpanKindServer
 | |
| 	// for spans started by this transport.
 | |
| 	StartOptions trace.StartOptions
 | |
| 
 | |
| 	// GetStartOptions allows to set start options per request. If set,
 | |
| 	// StartOptions is going to be ignored.
 | |
| 	GetStartOptions func(*http.Request) trace.StartOptions
 | |
| 
 | |
| 	// IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
 | |
| 	// servers. If true, any trace metadata set on the incoming request will
 | |
| 	// be added as a linked trace instead of being added as a parent of the
 | |
| 	// current trace.
 | |
| 	IsPublicEndpoint bool
 | |
| 
 | |
| 	// FormatSpanName holds the function to use for generating the span name
 | |
| 	// from the information found in the incoming HTTP Request. By default the
 | |
| 	// name equals the URL Path.
 | |
| 	FormatSpanName func(*http.Request) string
 | |
| 
 | |
| 	// IsHealthEndpoint holds the function to use for determining if the
 | |
| 	// incoming HTTP request should be considered a health check. This is in
 | |
| 	// addition to the private isHealthEndpoint func which may also indicate
 | |
| 	// tracing should be skipped.
 | |
| 	IsHealthEndpoint func(*http.Request) bool
 | |
| }
 | |
| 
 | |
| func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | |
| 	var tags addedTags
 | |
| 	r, traceEnd := h.startTrace(w, r)
 | |
| 	defer traceEnd()
 | |
| 	w, statsEnd := h.startStats(w, r)
 | |
| 	defer statsEnd(&tags)
 | |
| 	handler := h.Handler
 | |
| 	if handler == nil {
 | |
| 		handler = http.DefaultServeMux
 | |
| 	}
 | |
| 	r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
 | |
| 	handler.ServeHTTP(w, r)
 | |
| }
 | |
| 
 | |
| func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
 | |
| 	if h.IsHealthEndpoint != nil && h.IsHealthEndpoint(r) || isHealthEndpoint(r.URL.Path) {
 | |
| 		return r, func() {}
 | |
| 	}
 | |
| 	var name string
 | |
| 	if h.FormatSpanName == nil {
 | |
| 		name = spanNameFromURL(r)
 | |
| 	} else {
 | |
| 		name = h.FormatSpanName(r)
 | |
| 	}
 | |
| 	ctx := r.Context()
 | |
| 
 | |
| 	startOpts := h.StartOptions
 | |
| 	if h.GetStartOptions != nil {
 | |
| 		startOpts = h.GetStartOptions(r)
 | |
| 	}
 | |
| 
 | |
| 	var span *trace.Span
 | |
| 	sc, ok := h.extractSpanContext(r)
 | |
| 	if ok && !h.IsPublicEndpoint {
 | |
| 		ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
 | |
| 			trace.WithSampler(startOpts.Sampler),
 | |
| 			trace.WithSpanKind(trace.SpanKindServer))
 | |
| 	} else {
 | |
| 		ctx, span = trace.StartSpan(ctx, name,
 | |
| 			trace.WithSampler(startOpts.Sampler),
 | |
| 			trace.WithSpanKind(trace.SpanKindServer),
 | |
| 		)
 | |
| 		if ok {
 | |
| 			span.AddLink(trace.Link{
 | |
| 				TraceID:    sc.TraceID,
 | |
| 				SpanID:     sc.SpanID,
 | |
| 				Type:       trace.LinkTypeParent,
 | |
| 				Attributes: nil,
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 	span.AddAttributes(requestAttrs(r)...)
 | |
| 	if r.Body == nil {
 | |
| 		// TODO: Handle cases where ContentLength is not set.
 | |
| 	} else if r.ContentLength > 0 {
 | |
| 		span.AddMessageReceiveEvent(0, /* TODO: messageID */
 | |
| 			r.ContentLength, -1)
 | |
| 	}
 | |
| 	return r.WithContext(ctx), span.End
 | |
| }
 | |
| 
 | |
| func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
 | |
| 	if h.Propagation == nil {
 | |
| 		return defaultFormat.SpanContextFromRequest(r)
 | |
| 	}
 | |
| 	return h.Propagation.SpanContextFromRequest(r)
 | |
| }
 | |
| 
 | |
| func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
 | |
| 	ctx, _ := tag.New(r.Context(),
 | |
| 		tag.Upsert(Host, r.Host),
 | |
| 		tag.Upsert(Path, r.URL.Path),
 | |
| 		tag.Upsert(Method, r.Method))
 | |
| 	track := &trackingResponseWriter{
 | |
| 		start:  time.Now(),
 | |
| 		ctx:    ctx,
 | |
| 		writer: w,
 | |
| 	}
 | |
| 	if r.Body == nil {
 | |
| 		// TODO: Handle cases where ContentLength is not set.
 | |
| 		track.reqSize = -1
 | |
| 	} else if r.ContentLength > 0 {
 | |
| 		track.reqSize = r.ContentLength
 | |
| 	}
 | |
| 	stats.Record(ctx, ServerRequestCount.M(1))
 | |
| 	return track.wrappedResponseWriter(), track.end
 | |
| }
 | |
| 
 | |
| type trackingResponseWriter struct {
 | |
| 	ctx        context.Context
 | |
| 	reqSize    int64
 | |
| 	respSize   int64
 | |
| 	start      time.Time
 | |
| 	statusCode int
 | |
| 	statusLine string
 | |
| 	endOnce    sync.Once
 | |
| 	writer     http.ResponseWriter
 | |
| }
 | |
| 
 | |
| // Compile time assertion for ResponseWriter interface
 | |
| var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
 | |
| 
 | |
| func (t *trackingResponseWriter) end(tags *addedTags) {
 | |
| 	t.endOnce.Do(func() {
 | |
| 		if t.statusCode == 0 {
 | |
| 			t.statusCode = 200
 | |
| 		}
 | |
| 
 | |
| 		span := trace.FromContext(t.ctx)
 | |
| 		span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
 | |
| 		span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
 | |
| 
 | |
| 		m := []stats.Measurement{
 | |
| 			ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
 | |
| 			ServerResponseBytes.M(t.respSize),
 | |
| 		}
 | |
| 		if t.reqSize >= 0 {
 | |
| 			m = append(m, ServerRequestBytes.M(t.reqSize))
 | |
| 		}
 | |
| 		allTags := make([]tag.Mutator, len(tags.t)+1)
 | |
| 		allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
 | |
| 		copy(allTags[1:], tags.t)
 | |
| 		stats.RecordWithTags(t.ctx, allTags, m...)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (t *trackingResponseWriter) Header() http.Header {
 | |
| 	return t.writer.Header()
 | |
| }
 | |
| 
 | |
| func (t *trackingResponseWriter) Write(data []byte) (int, error) {
 | |
| 	n, err := t.writer.Write(data)
 | |
| 	t.respSize += int64(n)
 | |
| 	// Add message event for request bytes sent.
 | |
| 	span := trace.FromContext(t.ctx)
 | |
| 	span.AddMessageSendEvent(0 /* TODO: messageID */, int64(n), -1)
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (t *trackingResponseWriter) WriteHeader(statusCode int) {
 | |
| 	t.writer.WriteHeader(statusCode)
 | |
| 	t.statusCode = statusCode
 | |
| 	t.statusLine = http.StatusText(t.statusCode)
 | |
| }
 | |
| 
 | |
| // wrappedResponseWriter returns a wrapped version of the original
 | |
| //  ResponseWriter and only implements the same combination of additional
 | |
| // interfaces as the original.
 | |
| // This implementation is based on https://github.com/felixge/httpsnoop.
 | |
| func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
 | |
| 	var (
 | |
| 		hj, i0 = t.writer.(http.Hijacker)
 | |
| 		cn, i1 = t.writer.(http.CloseNotifier)
 | |
| 		pu, i2 = t.writer.(http.Pusher)
 | |
| 		fl, i3 = t.writer.(http.Flusher)
 | |
| 		rf, i4 = t.writer.(io.ReaderFrom)
 | |
| 	)
 | |
| 
 | |
| 	switch {
 | |
| 	case !i0 && !i1 && !i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 		}{t}
 | |
| 	case !i0 && !i1 && !i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			io.ReaderFrom
 | |
| 		}{t, rf}
 | |
| 	case !i0 && !i1 && !i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Flusher
 | |
| 		}{t, fl}
 | |
| 	case !i0 && !i1 && !i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, fl, rf}
 | |
| 	case !i0 && !i1 && i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Pusher
 | |
| 		}{t, pu}
 | |
| 	case !i0 && !i1 && i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Pusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, pu, rf}
 | |
| 	case !i0 && !i1 && i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 		}{t, pu, fl}
 | |
| 	case !i0 && !i1 && i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, pu, fl, rf}
 | |
| 	case !i0 && i1 && !i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 		}{t, cn}
 | |
| 	case !i0 && i1 && !i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			io.ReaderFrom
 | |
| 		}{t, cn, rf}
 | |
| 	case !i0 && i1 && !i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Flusher
 | |
| 		}{t, cn, fl}
 | |
| 	case !i0 && i1 && !i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, cn, fl, rf}
 | |
| 	case !i0 && i1 && i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 		}{t, cn, pu}
 | |
| 	case !i0 && i1 && i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, cn, pu, rf}
 | |
| 	case !i0 && i1 && i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 		}{t, cn, pu, fl}
 | |
| 	case !i0 && i1 && i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, cn, pu, fl, rf}
 | |
| 	case i0 && !i1 && !i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 		}{t, hj}
 | |
| 	case i0 && !i1 && !i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, rf}
 | |
| 	case i0 && !i1 && !i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Flusher
 | |
| 		}{t, hj, fl}
 | |
| 	case i0 && !i1 && !i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, fl, rf}
 | |
| 	case i0 && !i1 && i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Pusher
 | |
| 		}{t, hj, pu}
 | |
| 	case i0 && !i1 && i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Pusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, pu, rf}
 | |
| 	case i0 && !i1 && i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 		}{t, hj, pu, fl}
 | |
| 	case i0 && !i1 && i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, pu, fl, rf}
 | |
| 	case i0 && i1 && !i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 		}{t, hj, cn}
 | |
| 	case i0 && i1 && !i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, cn, rf}
 | |
| 	case i0 && i1 && !i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Flusher
 | |
| 		}{t, hj, cn, fl}
 | |
| 	case i0 && i1 && !i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, cn, fl, rf}
 | |
| 	case i0 && i1 && i2 && !i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 		}{t, hj, cn, pu}
 | |
| 	case i0 && i1 && i2 && !i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, cn, pu, rf}
 | |
| 	case i0 && i1 && i2 && i3 && !i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 		}{t, hj, cn, pu, fl}
 | |
| 	case i0 && i1 && i2 && i3 && i4:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 			http.Hijacker
 | |
| 			http.CloseNotifier
 | |
| 			http.Pusher
 | |
| 			http.Flusher
 | |
| 			io.ReaderFrom
 | |
| 		}{t, hj, cn, pu, fl, rf}
 | |
| 	default:
 | |
| 		return struct {
 | |
| 			http.ResponseWriter
 | |
| 		}{t}
 | |
| 	}
 | |
| }
 |