...

Source file src/go.opencensus.io/plugin/ochttp/server.go

Documentation: go.opencensus.io/plugin/ochttp

     1  // Copyright 2018, OpenCensus Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ochttp
    16  
    17  import (
    18  	"context"
    19  	"io"
    20  	"net/http"
    21  	"strconv"
    22  	"sync"
    23  	"time"
    24  
    25  	"go.opencensus.io/stats"
    26  	"go.opencensus.io/tag"
    27  	"go.opencensus.io/trace"
    28  	"go.opencensus.io/trace/propagation"
    29  )
    30  
    31  // Handler is an http.Handler wrapper to instrument your HTTP server with
    32  // OpenCensus. It supports both stats and tracing.
    33  //
    34  // # Tracing
    35  //
    36  // This handler is aware of the incoming request's span, reading it from request
    37  // headers as configured using the Propagation field.
    38  // The extracted span can be accessed from the incoming request's
    39  // context.
    40  //
    41  //	span := trace.FromContext(r.Context())
    42  //
    43  // The server span will be automatically ended at the end of ServeHTTP.
    44  type Handler struct {
    45  	// Propagation defines how traces are propagated. If unspecified,
    46  	// B3 propagation will be used.
    47  	Propagation propagation.HTTPFormat
    48  
    49  	// Handler is the handler used to handle the incoming request.
    50  	Handler http.Handler
    51  
    52  	// StartOptions are applied to the span started by this Handler around each
    53  	// request.
    54  	//
    55  	// StartOptions.SpanKind will always be set to trace.SpanKindServer
    56  	// for spans started by this transport.
    57  	StartOptions trace.StartOptions
    58  
    59  	// GetStartOptions allows to set start options per request. If set,
    60  	// StartOptions is going to be ignored.
    61  	GetStartOptions func(*http.Request) trace.StartOptions
    62  
    63  	// IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
    64  	// servers. If true, any trace metadata set on the incoming request will
    65  	// be added as a linked trace instead of being added as a parent of the
    66  	// current trace.
    67  	IsPublicEndpoint bool
    68  
    69  	// FormatSpanName holds the function to use for generating the span name
    70  	// from the information found in the incoming HTTP Request. By default the
    71  	// name equals the URL Path.
    72  	FormatSpanName func(*http.Request) string
    73  
    74  	// IsHealthEndpoint holds the function to use for determining if the
    75  	// incoming HTTP request should be considered a health check. This is in
    76  	// addition to the private isHealthEndpoint func which may also indicate
    77  	// tracing should be skipped.
    78  	IsHealthEndpoint func(*http.Request) bool
    79  }
    80  
    81  func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    82  	var tags addedTags
    83  	r, traceEnd := h.startTrace(w, r)
    84  	defer traceEnd()
    85  	w, statsEnd := h.startStats(w, r)
    86  	defer statsEnd(&tags)
    87  	handler := h.Handler
    88  	if handler == nil {
    89  		handler = http.DefaultServeMux
    90  	}
    91  	r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
    92  	handler.ServeHTTP(w, r)
    93  }
    94  
    95  func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
    96  	if h.IsHealthEndpoint != nil && h.IsHealthEndpoint(r) || isHealthEndpoint(r.URL.Path) {
    97  		return r, func() {}
    98  	}
    99  	var name string
   100  	if h.FormatSpanName == nil {
   101  		name = spanNameFromURL(r)
   102  	} else {
   103  		name = h.FormatSpanName(r)
   104  	}
   105  	ctx := r.Context()
   106  
   107  	startOpts := h.StartOptions
   108  	if h.GetStartOptions != nil {
   109  		startOpts = h.GetStartOptions(r)
   110  	}
   111  
   112  	var span *trace.Span
   113  	sc, ok := h.extractSpanContext(r)
   114  	if ok && !h.IsPublicEndpoint {
   115  		ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
   116  			trace.WithSampler(startOpts.Sampler),
   117  			trace.WithSpanKind(trace.SpanKindServer))
   118  	} else {
   119  		ctx, span = trace.StartSpan(ctx, name,
   120  			trace.WithSampler(startOpts.Sampler),
   121  			trace.WithSpanKind(trace.SpanKindServer),
   122  		)
   123  		if ok {
   124  			span.AddLink(trace.Link{
   125  				TraceID:    sc.TraceID,
   126  				SpanID:     sc.SpanID,
   127  				Type:       trace.LinkTypeParent,
   128  				Attributes: nil,
   129  			})
   130  		}
   131  	}
   132  	span.AddAttributes(requestAttrs(r)...)
   133  	if r.Body == nil {
   134  		// TODO: Handle cases where ContentLength is not set.
   135  	} else if r.ContentLength > 0 {
   136  		span.AddMessageReceiveEvent(0, /* TODO: messageID */
   137  			r.ContentLength, -1)
   138  	}
   139  	return r.WithContext(ctx), span.End
   140  }
   141  
   142  func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
   143  	if h.Propagation == nil {
   144  		return defaultFormat.SpanContextFromRequest(r)
   145  	}
   146  	return h.Propagation.SpanContextFromRequest(r)
   147  }
   148  
   149  func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
   150  	ctx, _ := tag.New(r.Context(),
   151  		tag.Upsert(Host, r.Host),
   152  		tag.Upsert(Path, r.URL.Path),
   153  		tag.Upsert(Method, r.Method))
   154  	track := &trackingResponseWriter{
   155  		start:  time.Now(),
   156  		ctx:    ctx,
   157  		writer: w,
   158  	}
   159  	if r.Body == nil {
   160  		// TODO: Handle cases where ContentLength is not set.
   161  		track.reqSize = -1
   162  	} else if r.ContentLength > 0 {
   163  		track.reqSize = r.ContentLength
   164  	}
   165  	stats.Record(ctx, ServerRequestCount.M(1))
   166  	return track.wrappedResponseWriter(), track.end
   167  }
   168  
   169  type trackingResponseWriter struct {
   170  	ctx        context.Context
   171  	reqSize    int64
   172  	respSize   int64
   173  	start      time.Time
   174  	statusCode int
   175  	statusLine string
   176  	endOnce    sync.Once
   177  	writer     http.ResponseWriter
   178  }
   179  
   180  // Compile time assertion for ResponseWriter interface
   181  var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
   182  
   183  func (t *trackingResponseWriter) end(tags *addedTags) {
   184  	t.endOnce.Do(func() {
   185  		if t.statusCode == 0 {
   186  			t.statusCode = 200
   187  		}
   188  
   189  		span := trace.FromContext(t.ctx)
   190  		span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
   191  		span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
   192  
   193  		m := []stats.Measurement{
   194  			ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
   195  			ServerResponseBytes.M(t.respSize),
   196  		}
   197  		if t.reqSize >= 0 {
   198  			m = append(m, ServerRequestBytes.M(t.reqSize))
   199  		}
   200  		allTags := make([]tag.Mutator, len(tags.t)+1)
   201  		allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
   202  		copy(allTags[1:], tags.t)
   203  		stats.RecordWithTags(t.ctx, allTags, m...)
   204  	})
   205  }
   206  
   207  func (t *trackingResponseWriter) Header() http.Header {
   208  	return t.writer.Header()
   209  }
   210  
   211  func (t *trackingResponseWriter) Write(data []byte) (int, error) {
   212  	n, err := t.writer.Write(data)
   213  	t.respSize += int64(n)
   214  	// Add message event for request bytes sent.
   215  	span := trace.FromContext(t.ctx)
   216  	span.AddMessageSendEvent(0 /* TODO: messageID */, int64(n), -1)
   217  	return n, err
   218  }
   219  
   220  func (t *trackingResponseWriter) WriteHeader(statusCode int) {
   221  	t.writer.WriteHeader(statusCode)
   222  	t.statusCode = statusCode
   223  	t.statusLine = http.StatusText(t.statusCode)
   224  }
   225  
   226  // wrappedResponseWriter returns a wrapped version of the original
   227  //
   228  //	ResponseWriter and only implements the same combination of additional
   229  //
   230  // interfaces as the original.
   231  // This implementation is based on https://github.com/felixge/httpsnoop.
   232  func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
   233  	var (
   234  		hj, i0 = t.writer.(http.Hijacker)
   235  		cn, i1 = t.writer.(http.CloseNotifier)
   236  		pu, i2 = t.writer.(http.Pusher)
   237  		fl, i3 = t.writer.(http.Flusher)
   238  		rf, i4 = t.writer.(io.ReaderFrom)
   239  	)
   240  
   241  	switch {
   242  	case !i0 && !i1 && !i2 && !i3 && !i4:
   243  		return struct {
   244  			http.ResponseWriter
   245  		}{t}
   246  	case !i0 && !i1 && !i2 && !i3 && i4:
   247  		return struct {
   248  			http.ResponseWriter
   249  			io.ReaderFrom
   250  		}{t, rf}
   251  	case !i0 && !i1 && !i2 && i3 && !i4:
   252  		return struct {
   253  			http.ResponseWriter
   254  			http.Flusher
   255  		}{t, fl}
   256  	case !i0 && !i1 && !i2 && i3 && i4:
   257  		return struct {
   258  			http.ResponseWriter
   259  			http.Flusher
   260  			io.ReaderFrom
   261  		}{t, fl, rf}
   262  	case !i0 && !i1 && i2 && !i3 && !i4:
   263  		return struct {
   264  			http.ResponseWriter
   265  			http.Pusher
   266  		}{t, pu}
   267  	case !i0 && !i1 && i2 && !i3 && i4:
   268  		return struct {
   269  			http.ResponseWriter
   270  			http.Pusher
   271  			io.ReaderFrom
   272  		}{t, pu, rf}
   273  	case !i0 && !i1 && i2 && i3 && !i4:
   274  		return struct {
   275  			http.ResponseWriter
   276  			http.Pusher
   277  			http.Flusher
   278  		}{t, pu, fl}
   279  	case !i0 && !i1 && i2 && i3 && i4:
   280  		return struct {
   281  			http.ResponseWriter
   282  			http.Pusher
   283  			http.Flusher
   284  			io.ReaderFrom
   285  		}{t, pu, fl, rf}
   286  	case !i0 && i1 && !i2 && !i3 && !i4:
   287  		return struct {
   288  			http.ResponseWriter
   289  			http.CloseNotifier
   290  		}{t, cn}
   291  	case !i0 && i1 && !i2 && !i3 && i4:
   292  		return struct {
   293  			http.ResponseWriter
   294  			http.CloseNotifier
   295  			io.ReaderFrom
   296  		}{t, cn, rf}
   297  	case !i0 && i1 && !i2 && i3 && !i4:
   298  		return struct {
   299  			http.ResponseWriter
   300  			http.CloseNotifier
   301  			http.Flusher
   302  		}{t, cn, fl}
   303  	case !i0 && i1 && !i2 && i3 && i4:
   304  		return struct {
   305  			http.ResponseWriter
   306  			http.CloseNotifier
   307  			http.Flusher
   308  			io.ReaderFrom
   309  		}{t, cn, fl, rf}
   310  	case !i0 && i1 && i2 && !i3 && !i4:
   311  		return struct {
   312  			http.ResponseWriter
   313  			http.CloseNotifier
   314  			http.Pusher
   315  		}{t, cn, pu}
   316  	case !i0 && i1 && i2 && !i3 && i4:
   317  		return struct {
   318  			http.ResponseWriter
   319  			http.CloseNotifier
   320  			http.Pusher
   321  			io.ReaderFrom
   322  		}{t, cn, pu, rf}
   323  	case !i0 && i1 && i2 && i3 && !i4:
   324  		return struct {
   325  			http.ResponseWriter
   326  			http.CloseNotifier
   327  			http.Pusher
   328  			http.Flusher
   329  		}{t, cn, pu, fl}
   330  	case !i0 && i1 && i2 && i3 && i4:
   331  		return struct {
   332  			http.ResponseWriter
   333  			http.CloseNotifier
   334  			http.Pusher
   335  			http.Flusher
   336  			io.ReaderFrom
   337  		}{t, cn, pu, fl, rf}
   338  	case i0 && !i1 && !i2 && !i3 && !i4:
   339  		return struct {
   340  			http.ResponseWriter
   341  			http.Hijacker
   342  		}{t, hj}
   343  	case i0 && !i1 && !i2 && !i3 && i4:
   344  		return struct {
   345  			http.ResponseWriter
   346  			http.Hijacker
   347  			io.ReaderFrom
   348  		}{t, hj, rf}
   349  	case i0 && !i1 && !i2 && i3 && !i4:
   350  		return struct {
   351  			http.ResponseWriter
   352  			http.Hijacker
   353  			http.Flusher
   354  		}{t, hj, fl}
   355  	case i0 && !i1 && !i2 && i3 && i4:
   356  		return struct {
   357  			http.ResponseWriter
   358  			http.Hijacker
   359  			http.Flusher
   360  			io.ReaderFrom
   361  		}{t, hj, fl, rf}
   362  	case i0 && !i1 && i2 && !i3 && !i4:
   363  		return struct {
   364  			http.ResponseWriter
   365  			http.Hijacker
   366  			http.Pusher
   367  		}{t, hj, pu}
   368  	case i0 && !i1 && i2 && !i3 && i4:
   369  		return struct {
   370  			http.ResponseWriter
   371  			http.Hijacker
   372  			http.Pusher
   373  			io.ReaderFrom
   374  		}{t, hj, pu, rf}
   375  	case i0 && !i1 && i2 && i3 && !i4:
   376  		return struct {
   377  			http.ResponseWriter
   378  			http.Hijacker
   379  			http.Pusher
   380  			http.Flusher
   381  		}{t, hj, pu, fl}
   382  	case i0 && !i1 && i2 && i3 && i4:
   383  		return struct {
   384  			http.ResponseWriter
   385  			http.Hijacker
   386  			http.Pusher
   387  			http.Flusher
   388  			io.ReaderFrom
   389  		}{t, hj, pu, fl, rf}
   390  	case i0 && i1 && !i2 && !i3 && !i4:
   391  		return struct {
   392  			http.ResponseWriter
   393  			http.Hijacker
   394  			http.CloseNotifier
   395  		}{t, hj, cn}
   396  	case i0 && i1 && !i2 && !i3 && i4:
   397  		return struct {
   398  			http.ResponseWriter
   399  			http.Hijacker
   400  			http.CloseNotifier
   401  			io.ReaderFrom
   402  		}{t, hj, cn, rf}
   403  	case i0 && i1 && !i2 && i3 && !i4:
   404  		return struct {
   405  			http.ResponseWriter
   406  			http.Hijacker
   407  			http.CloseNotifier
   408  			http.Flusher
   409  		}{t, hj, cn, fl}
   410  	case i0 && i1 && !i2 && i3 && i4:
   411  		return struct {
   412  			http.ResponseWriter
   413  			http.Hijacker
   414  			http.CloseNotifier
   415  			http.Flusher
   416  			io.ReaderFrom
   417  		}{t, hj, cn, fl, rf}
   418  	case i0 && i1 && i2 && !i3 && !i4:
   419  		return struct {
   420  			http.ResponseWriter
   421  			http.Hijacker
   422  			http.CloseNotifier
   423  			http.Pusher
   424  		}{t, hj, cn, pu}
   425  	case i0 && i1 && i2 && !i3 && i4:
   426  		return struct {
   427  			http.ResponseWriter
   428  			http.Hijacker
   429  			http.CloseNotifier
   430  			http.Pusher
   431  			io.ReaderFrom
   432  		}{t, hj, cn, pu, rf}
   433  	case i0 && i1 && i2 && i3 && !i4:
   434  		return struct {
   435  			http.ResponseWriter
   436  			http.Hijacker
   437  			http.CloseNotifier
   438  			http.Pusher
   439  			http.Flusher
   440  		}{t, hj, cn, pu, fl}
   441  	case i0 && i1 && i2 && i3 && i4:
   442  		return struct {
   443  			http.ResponseWriter
   444  			http.Hijacker
   445  			http.CloseNotifier
   446  			http.Pusher
   447  			http.Flusher
   448  			io.ReaderFrom
   449  		}{t, hj, cn, pu, fl, rf}
   450  	default:
   451  		return struct {
   452  			http.ResponseWriter
   453  		}{t}
   454  	}
   455  }
   456  

View as plain text