...

Source file src/github.com/go-kit/kit/tracing/zipkin/http_test.go

Documentation: github.com/go-kit/kit/tracing/zipkin

     1  package zipkin_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"reflect"
    11  	"testing"
    12  
    13  	zipkin "github.com/openzipkin/zipkin-go"
    14  	"github.com/openzipkin/zipkin-go/model"
    15  	"github.com/openzipkin/zipkin-go/propagation/b3"
    16  	"github.com/openzipkin/zipkin-go/reporter/recorder"
    17  
    18  	"github.com/go-kit/kit/endpoint"
    19  	zipkinkit "github.com/go-kit/kit/tracing/zipkin"
    20  	kithttp "github.com/go-kit/kit/transport/http"
    21  )
    22  
    23  const (
    24  	testName     = "test"
    25  	testBody     = "test_body"
    26  	testTagKey   = "test_key"
    27  	testTagValue = "test_value"
    28  )
    29  
    30  func TestHTTPClientTracePropagatesParentSpan(t *testing.T) {
    31  	rec := recorder.NewReporter()
    32  	defer rec.Close()
    33  
    34  	tr, _ := zipkin.NewTracer(rec)
    35  
    36  	rURL, _ := url.Parse("https://httpbin.org/get")
    37  
    38  	clientTracer := zipkinkit.HTTPClientTrace(tr)
    39  	ep := kithttp.NewClient(
    40  		"GET",
    41  		rURL,
    42  		func(ctx context.Context, r *http.Request, i interface{}) error {
    43  			return nil
    44  		},
    45  		func(ctx context.Context, r *http.Response) (response interface{}, err error) {
    46  			return nil, nil
    47  		},
    48  		clientTracer,
    49  	).Endpoint()
    50  
    51  	parentSpan := tr.StartSpan("test")
    52  
    53  	ctx := zipkin.NewContext(context.Background(), parentSpan)
    54  
    55  	_, err := ep(ctx, nil)
    56  	if err != nil {
    57  		t.Fatalf("unexpected error: %s", err.Error())
    58  	}
    59  
    60  	spans := rec.Flush()
    61  	if want, have := 1, len(spans); want != have {
    62  		t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
    63  	}
    64  
    65  	span := spans[0]
    66  	if span.SpanContext.ParentID == nil {
    67  		t.Fatalf("incorrect parent ID, want %s have nil", parentSpan.Context().ID)
    68  	}
    69  
    70  	if want, have := parentSpan.Context().ID, *span.SpanContext.ParentID; want != have {
    71  		t.Fatalf("incorrect parent ID, want %s, have %s", want, have)
    72  	}
    73  }
    74  
    75  func TestHTTPClientTraceAddsExpectedTags(t *testing.T) {
    76  	dataProvider := []struct {
    77  		ResponseStatusCode int
    78  		ErrorTagValue      string
    79  	}{
    80  		{http.StatusOK, ""},
    81  		{http.StatusForbidden, fmt.Sprint(http.StatusForbidden)},
    82  	}
    83  
    84  	for _, data := range dataProvider {
    85  		testHTTPClientTraceCase(t, data.ResponseStatusCode, data.ErrorTagValue)
    86  	}
    87  }
    88  
    89  func testHTTPClientTraceCase(t *testing.T, responseStatusCode int, errTagValue string) {
    90  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    91  		w.WriteHeader(responseStatusCode)
    92  		w.Write([]byte(testBody))
    93  	}))
    94  	defer ts.Close()
    95  
    96  	rec := recorder.NewReporter()
    97  	defer rec.Close()
    98  
    99  	tr, err := zipkin.NewTracer(rec)
   100  	if err != nil {
   101  		t.Errorf("Unwanted error: %s", err.Error())
   102  	}
   103  
   104  	rMethod := "GET"
   105  	rURL, _ := url.Parse(ts.URL)
   106  
   107  	clientTracer := zipkinkit.HTTPClientTrace(
   108  		tr,
   109  		zipkinkit.Name(testName),
   110  		zipkinkit.Tags(map[string]string{testTagKey: testTagValue}),
   111  	)
   112  
   113  	ep := kithttp.NewClient(
   114  		rMethod,
   115  		rURL,
   116  		func(ctx context.Context, r *http.Request, i interface{}) error {
   117  			return nil
   118  		},
   119  		func(ctx context.Context, r *http.Response) (response interface{}, err error) {
   120  			return nil, nil
   121  		},
   122  		clientTracer,
   123  	).Endpoint()
   124  
   125  	_, err = ep(context.Background(), nil)
   126  	if err != nil {
   127  		t.Fatalf("unwanted error: %s", err.Error())
   128  	}
   129  
   130  	spans := rec.Flush()
   131  	if want, have := 1, len(spans); want != have {
   132  		t.Fatalf("incorrect number of spans, wanted %d, got %d", want, have)
   133  	}
   134  
   135  	span := spans[0]
   136  	if span.SpanContext.ParentID != nil {
   137  		t.Fatalf("incorrect parentID, wanted nil, got %s", span.SpanContext.ParentID)
   138  	}
   139  
   140  	if want, have := testName, span.Name; want != have {
   141  		t.Fatalf("incorrect span name, wanted %s, got %s", want, have)
   142  	}
   143  
   144  	if want, have := model.Client, span.Kind; want != have {
   145  		t.Fatalf("incorrect span kind, wanted %s, got %s", want, have)
   146  	}
   147  
   148  	tags := map[string]string{
   149  		testTagKey:                         testTagValue,
   150  		string(zipkin.TagHTTPStatusCode):   fmt.Sprint(responseStatusCode),
   151  		string(zipkin.TagHTTPMethod):       rMethod,
   152  		string(zipkin.TagHTTPUrl):          rURL.String(),
   153  		string(zipkin.TagHTTPResponseSize): fmt.Sprint(len(testBody)),
   154  	}
   155  
   156  	if errTagValue != "" {
   157  		tags[string(zipkin.TagError)] = fmt.Sprint(errTagValue)
   158  	}
   159  
   160  	if !reflect.DeepEqual(span.Tags, tags) {
   161  		t.Fatalf("invalid tags set, wanted %+v, got %+v", tags, span.Tags)
   162  	}
   163  }
   164  
   165  func TestHTTPServerTrace(t *testing.T) {
   166  	rec := recorder.NewReporter()
   167  	defer rec.Close()
   168  
   169  	// explicitly show we use the default of RPC shared spans in Zipkin as it
   170  	// is idiomatic for Zipkin to share span identifiers between client and
   171  	// server side.
   172  	tr, _ := zipkin.NewTracer(rec, zipkin.WithSharedSpans(true))
   173  
   174  	handler := kithttp.NewServer(
   175  		endpoint.Nop,
   176  		func(context.Context, *http.Request) (interface{}, error) { return nil, nil },
   177  		func(context.Context, http.ResponseWriter, interface{}) error { return errors.New("dummy") },
   178  		zipkinkit.HTTPServerTrace(tr),
   179  	)
   180  
   181  	server := httptest.NewServer(handler)
   182  	defer server.Close()
   183  
   184  	const httpMethod = "GET"
   185  
   186  	req, err := http.NewRequest(httpMethod, server.URL, nil)
   187  	if err != nil {
   188  		t.Fatalf("unable to create HTTP request: %s", err.Error())
   189  	}
   190  
   191  	parentSpan := tr.StartSpan("Dummy")
   192  
   193  	b3.InjectHTTP(req)(parentSpan.Context())
   194  
   195  	client := http.Client{}
   196  	resp, err := client.Do(req)
   197  	if err != nil {
   198  		t.Fatalf("unable to send HTTP request: %s", err.Error())
   199  	}
   200  	resp.Body.Close()
   201  
   202  	spans := rec.Flush()
   203  	if want, have := 1, len(spans); want != have {
   204  		t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
   205  	}
   206  
   207  	if want, have := parentSpan.Context().TraceID, spans[0].SpanContext.TraceID; want != have {
   208  		t.Errorf("incorrect TraceID, want %+v, have %+v", want, have)
   209  	}
   210  
   211  	if want, have := parentSpan.Context().ID, spans[0].SpanContext.ID; want != have {
   212  		t.Errorf("incorrect span ID, want %d, have %d", want, have)
   213  	}
   214  
   215  	if want, have := httpMethod, spans[0].Name; want != have {
   216  		t.Errorf("incorrect span name, want %s, have %s", want, have)
   217  	}
   218  
   219  	if want, have := http.StatusText(500), spans[0].Tags["error"]; want != have {
   220  		t.Fatalf("incorrect error tag, want %s, have %s", want, have)
   221  	}
   222  }
   223  
   224  func TestHTTPServerTraceIsRequestBasedSampled(t *testing.T) {
   225  	rec := recorder.NewReporter()
   226  	defer rec.Close()
   227  
   228  	const httpMethod = "DELETE"
   229  
   230  	tr, _ := zipkin.NewTracer(rec)
   231  
   232  	handler := kithttp.NewServer(
   233  		endpoint.Nop,
   234  		func(context.Context, *http.Request) (interface{}, error) { return nil, nil },
   235  		func(context.Context, http.ResponseWriter, interface{}) error { return nil },
   236  		zipkinkit.HTTPServerTrace(tr, zipkinkit.RequestSampler(func(r *http.Request) bool {
   237  			return r.Method == httpMethod
   238  		})),
   239  	)
   240  
   241  	server := httptest.NewServer(handler)
   242  	defer server.Close()
   243  
   244  	req, err := http.NewRequest(httpMethod, server.URL, nil)
   245  	if err != nil {
   246  		t.Fatalf("unable to create HTTP request: %s", err.Error())
   247  	}
   248  
   249  	client := http.Client{}
   250  	resp, err := client.Do(req)
   251  	if err != nil {
   252  		t.Fatalf("unable to send HTTP request: %s", err.Error())
   253  	}
   254  	resp.Body.Close()
   255  
   256  	spans := rec.Flush()
   257  	if want, have := 1, len(spans); want != have {
   258  		t.Fatalf("incorrect number of spans, want %d, have %d", want, have)
   259  	}
   260  }
   261  

View as plain text