github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/server/requestlog/stackdriver_test.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit 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  //     https://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 requestlog
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"go.opencensus.io/trace"
    31  )
    32  
    33  func TestStackdriverLog(t *testing.T) {
    34  	const (
    35  		startTime      = 1507914000
    36  		startTimeNanos = 512
    37  
    38  		latencySec   = 5
    39  		latencyNanos = 123456789
    40  
    41  		endTime      = startTime + latencySec
    42  		endTimeNanos = startTimeNanos + latencyNanos
    43  	)
    44  	ctx, span := trace.StartSpan(context.Background(), "test")
    45  	defer span.End()
    46  	sc := trace.FromContext(ctx).SpanContext()
    47  	buf := new(bytes.Buffer)
    48  	var logErr error
    49  	l := NewStackdriverLogger(buf, func(e error) { logErr = e })
    50  	want := &Entry{
    51  		ReceivedTime:       time.Unix(startTime, startTimeNanos),
    52  		RequestMethod:      "POST",
    53  		RequestURL:         "/foo/bar",
    54  		RequestHeaderSize:  456,
    55  		RequestBodySize:    123000,
    56  		UserAgent:          "Chrome proxied through Firefox and Edge",
    57  		Referer:            "http://www.example.com/",
    58  		Proto:              "HTTP/1.1",
    59  		RemoteIP:           "12.34.56.78",
    60  		ServerIP:           "127.0.0.1",
    61  		Status:             404,
    62  		ResponseHeaderSize: 555,
    63  		ResponseBodySize:   789000,
    64  		Latency:            latencySec*time.Second + latencyNanos*time.Nanosecond,
    65  		TraceID:            sc.TraceID,
    66  		SpanID:             sc.SpanID,
    67  	}
    68  	ent := *want // copy in case Log accidentally mutates
    69  	l.Log(&ent)
    70  	if logErr != nil {
    71  		t.Error("Logger called error callback:", logErr)
    72  	}
    73  
    74  	var got json.RawMessage
    75  	if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
    76  		t.Fatal("Unmarshal:", err)
    77  	}
    78  
    79  	var r map[string]interface{}
    80  	if err := json.Unmarshal(got, &r); err != nil {
    81  		t.Error("Unmarshal record:", err)
    82  	} else {
    83  		rr, _ := r["httpRequest"].(map[string]interface{})
    84  		if rr == nil {
    85  			t.Error("httpRequest does not exist in record or is not a JSON object")
    86  		}
    87  		if got, want := jsonString(rr, "requestMethod"), ent.RequestMethod; got != want {
    88  			t.Errorf("httpRequest.requestMethod = %q; want %q", got, want)
    89  		}
    90  		if got, want := jsonString(rr, "requestUrl"), ent.RequestURL; got != want {
    91  			t.Errorf("httpRequest.requestUrl = %q; want %q", got, want)
    92  		}
    93  		if got, want := jsonString(rr, "requestSize"), "123456"; got != want {
    94  			t.Errorf("httpRequest.requestSize = %q; want %q", got, want)
    95  		}
    96  		if got, want := jsonNumber(rr, "status"), float64(ent.Status); got != want {
    97  			t.Errorf("httpRequest.status = %d; want %d", int64(got), int64(want))
    98  		}
    99  		if got, want := jsonString(rr, "responseSize"), "789555"; got != want {
   100  			t.Errorf("httpRequest.responseSize = %q; want %q", got, want)
   101  		}
   102  		if got, want := jsonString(rr, "userAgent"), ent.UserAgent; got != want {
   103  			t.Errorf("httpRequest.userAgent = %q; want %q", got, want)
   104  		}
   105  		if got, want := jsonString(rr, "remoteIp"), ent.RemoteIP; got != want {
   106  			t.Errorf("httpRequest.remoteIp = %q; want %q", got, want)
   107  		}
   108  		if got, want := jsonString(rr, "referer"), ent.Referer; got != want {
   109  			t.Errorf("httpRequest.referer = %q; want %q", got, want)
   110  		}
   111  		if got, want := jsonString(rr, "latency"), "5.123456789"; parseLatency(got) != want {
   112  			t.Errorf("httpRequest.latency = %q; want %q", got, want+"s")
   113  		}
   114  		ts, _ := r["timestamp"].(map[string]interface{})
   115  		if ts == nil {
   116  			t.Error("timestamp does not exist in record or is not a JSON object")
   117  		}
   118  		if got, want := jsonNumber(ts, "seconds"), float64(endTime); got != want {
   119  			t.Errorf("timestamp.seconds = %g; want %g", got, want)
   120  		}
   121  		if got, want := jsonNumber(ts, "nanos"), float64(endTimeNanos); got != want {
   122  			t.Errorf("timestamp.nanos = %g; want %g", got, want)
   123  		}
   124  		if got, want := jsonString(r, "logging.googleapis.com/trace"), ent.TraceID.String(); got != want {
   125  			t.Errorf("traceID = %q; want %q", got, want)
   126  		}
   127  		if got, want := jsonString(r, "logging.googleapis.com/spanId"), ent.SpanID.String(); got != want {
   128  			t.Errorf("spanID = %q; want %q", got, want)
   129  		}
   130  	}
   131  }
   132  
   133  func parseLatency(s string) string {
   134  	s = strings.TrimSpace(s)
   135  	if !strings.HasSuffix(s, "s") {
   136  		return ""
   137  	}
   138  	s = strings.TrimSpace(s[:len(s)-1])
   139  	for _, c := range s {
   140  		if !(c >= '0' && c <= '9') && c != '.' {
   141  			return ""
   142  		}
   143  	}
   144  	return s
   145  }
   146  
   147  func jsonString(obj map[string]interface{}, k string) string {
   148  	v, _ := obj[k].(string)
   149  	return v
   150  }
   151  
   152  func jsonNumber(obj map[string]interface{}, k string) float64 {
   153  	v, _ := obj[k].(float64)
   154  	return v
   155  }
   156  
   157  func BenchmarkStackdriverLog(b *testing.B) {
   158  	ent := &Entry{
   159  		ReceivedTime:       time.Date(2017, time.October, 13, 17, 0, 0, 512, time.UTC),
   160  		RequestMethod:      "POST",
   161  		RequestURL:         "/foo/bar",
   162  		RequestHeaderSize:  456,
   163  		RequestBodySize:    123000,
   164  		UserAgent:          "Chrome proxied through Firefox and Edge",
   165  		Referer:            "http://www.example.com/",
   166  		Proto:              "HTTP/1.1",
   167  		RemoteIP:           "12.34.56.78",
   168  		ServerIP:           "127.0.0.1",
   169  		Status:             404,
   170  		ResponseHeaderSize: 555,
   171  		ResponseBodySize:   789000,
   172  		Latency:            5 * time.Second,
   173  	}
   174  	var buf bytes.Buffer
   175  	l := NewStackdriverLogger(&buf, func(error) {})
   176  	l.Log(ent)
   177  	b.ReportAllocs()
   178  	b.SetBytes(int64(buf.Len()))
   179  	buf.Reset()
   180  	b.ResetTimer()
   181  
   182  	l = NewStackdriverLogger(ioutil.Discard, func(error) {})
   183  	for i := 0; i < b.N; i++ {
   184  		l.Log(ent)
   185  	}
   186  }
   187  
   188  func BenchmarkE2E(b *testing.B) {
   189  	run := func(b *testing.B, handler http.Handler) {
   190  		s := httptest.NewServer(handler)
   191  		defer s.Close()
   192  		b.ReportAllocs()
   193  		for i := 0; i < b.N; i++ {
   194  			resp, err := http.Get(s.URL)
   195  			if err != nil {
   196  				b.Fatal(err)
   197  			}
   198  			io.Copy(ioutil.Discard, resp.Body)
   199  			resp.Body.Close()
   200  		}
   201  	}
   202  	b.Run("Baseline", func(b *testing.B) {
   203  		run(b, http.HandlerFunc(benchHandler))
   204  	})
   205  	b.Run("WithLog", func(b *testing.B) {
   206  		l := NewStackdriverLogger(ioutil.Discard, func(error) {})
   207  		run(b, NewHandler(l, http.HandlerFunc(benchHandler)))
   208  	})
   209  }
   210  
   211  func benchHandler(w http.ResponseWriter, r *http.Request) {
   212  	const msg = "Hello, World!"
   213  	w.Header().Set("Content-Length", fmt.Sprint(len(msg)))
   214  	io.WriteString(w, msg)
   215  }