mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
nettrace: Add a new tracing library
This commit introduces a new tracing library, that replaces golang.org/x/net/trace, and supports (amongts other thing) nested traces. This is a minimal change, future patches will make use of the new functionality.
This commit is contained in:
255
internal/nettrace/http_test.go
Normal file
255
internal/nettrace/http_test.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package nettrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getValues(t *testing.T, vs url.Values, code int) string {
|
||||
t.Helper()
|
||||
|
||||
req := httptest.NewRequest("GET", "/debug/traces?"+vs.Encode(), nil)
|
||||
w := httptest.NewRecorder()
|
||||
RenderTraces(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != code {
|
||||
t.Errorf("expected %d, got %v", code, resp)
|
||||
}
|
||||
|
||||
return string(body)
|
||||
}
|
||||
|
||||
type v struct {
|
||||
fam, b, lat, trace, ref, all string
|
||||
}
|
||||
|
||||
func getCode(t *testing.T, vs v, code int) string {
|
||||
t.Helper()
|
||||
|
||||
u := url.Values{}
|
||||
if vs.fam != "" {
|
||||
u.Add("fam", vs.fam)
|
||||
}
|
||||
if vs.b != "" {
|
||||
u.Add("b", vs.b)
|
||||
}
|
||||
if vs.lat != "" {
|
||||
u.Add("lat", vs.lat)
|
||||
}
|
||||
if vs.trace != "" {
|
||||
u.Add("trace", vs.trace)
|
||||
}
|
||||
if vs.ref != "" {
|
||||
u.Add("ref", vs.ref)
|
||||
}
|
||||
if vs.all != "" {
|
||||
u.Add("all", vs.all)
|
||||
}
|
||||
|
||||
return getValues(t, u, code)
|
||||
}
|
||||
|
||||
func get(t *testing.T, fam, b, lat, trace, ref, all string) string {
|
||||
t.Helper()
|
||||
return getCode(t, v{fam, b, lat, trace, ref, all}, 200)
|
||||
}
|
||||
|
||||
func getErr(t *testing.T, fam, b, lat, trace, ref, all string, code int, err string) string {
|
||||
t.Helper()
|
||||
body := getCode(t, v{fam, b, lat, trace, ref, all}, code)
|
||||
if !strings.Contains(body, err) {
|
||||
t.Errorf("Body does not contain error message %q", err)
|
||||
t.Logf("Body: %v", body)
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
func checkContains(t *testing.T, body, s string) {
|
||||
t.Helper()
|
||||
if !strings.Contains(body, s) {
|
||||
t.Errorf("Body does not contain %q", s)
|
||||
t.Logf("Body: %v", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTP(t *testing.T) {
|
||||
tr := New("TestHTTP", "http")
|
||||
tr.Printf("entry #1")
|
||||
tr.Finish()
|
||||
|
||||
tr = New("TestHTTP", "http")
|
||||
tr.Printf("entry #2")
|
||||
tr.Finish()
|
||||
|
||||
tr = New("TestHTTP", "http")
|
||||
tr.Errorf("entry #3 (error)")
|
||||
tr.Finish()
|
||||
|
||||
tr = New("TestHTTP", "http")
|
||||
tr.Printf("hola marola")
|
||||
tr.Printf("entry #4")
|
||||
// This one is active until the end.
|
||||
defer tr.Finish()
|
||||
|
||||
// Get the plain index.
|
||||
body := get(t, "", "", "", "", "", "")
|
||||
checkContains(t, body, "TestHTTP")
|
||||
|
||||
// Get a specific family, but no bucket.
|
||||
body = get(t, "TestHTTP", "", "", "", "", "")
|
||||
checkContains(t, body, "TestHTTP")
|
||||
|
||||
// Get a family and active bucket.
|
||||
body = get(t, "TestHTTP", "-1", "", "", "", "")
|
||||
checkContains(t, body, "hola marola")
|
||||
|
||||
// Get a family and error bucket.
|
||||
body = get(t, "TestHTTP", "-2", "", "", "", "")
|
||||
checkContains(t, body, "entry #3 (error)")
|
||||
|
||||
// Get a family and first bucket.
|
||||
body = get(t, "TestHTTP", "0", "", "", "", "")
|
||||
checkContains(t, body, "entry #2")
|
||||
|
||||
// Latency view. There are 3 events because the 4th is active.
|
||||
body = get(t, "TestHTTP", "", "lat", "", "", "")
|
||||
checkContains(t, body, "Count: 3")
|
||||
|
||||
// Get a specific trace. No family given, since it shouldn't be needed (we
|
||||
// take it from the id).
|
||||
body = get(t, "", "", "", string(tr.(*trace).ID), "", "")
|
||||
checkContains(t, body, "hola marola")
|
||||
|
||||
// Check the "all=true" views.
|
||||
body = get(t, "TestHTTP", "0", "", "", "", "true")
|
||||
checkContains(t, body, "entry #2")
|
||||
checkContains(t, body, "?fam=TestHTTP&b=-2&all=true")
|
||||
|
||||
tr.Finish()
|
||||
}
|
||||
|
||||
func TestHTTPLong(t *testing.T) {
|
||||
// Test a long trace.
|
||||
tr := New("TestHTTPLong", "verbose")
|
||||
for i := 0; i < 1000; i++ {
|
||||
tr.Printf("entry #%d", i)
|
||||
}
|
||||
tr.Finish()
|
||||
get(t, "TestHTTPLong", "", "", string(tr.(*trace).ID), "", "")
|
||||
}
|
||||
|
||||
func TestHTTPErrors(t *testing.T) {
|
||||
tr := New("TestHTTPErrors", "http")
|
||||
tr.Printf("entry #1")
|
||||
tr.Finish()
|
||||
|
||||
// Unknown family.
|
||||
getErr(t, "unkfamily", "", "", "", "", "",
|
||||
404, "Unknown family")
|
||||
|
||||
// Invalid bucket.
|
||||
getErr(t, "TestHTTPErrors", "abc", "", "", "", "",
|
||||
400, "Invalid bucket")
|
||||
getErr(t, "TestHTTPErrors", "-3", "", "", "", "",
|
||||
400, "Invalid bucket")
|
||||
getErr(t, "TestHTTPErrors", "9", "", "", "", "",
|
||||
400, "Invalid bucket")
|
||||
|
||||
// Unknown trace id (malformed).
|
||||
getErr(t, "TestHTTPErrors", "", "", "unktrace", "", "",
|
||||
404, "Trace not found")
|
||||
|
||||
// Unknown trace id.
|
||||
getErr(t, "TestHTTPErrors", "", "", string(tr.(*trace).ID)+"xxx", "", "",
|
||||
404, "Trace not found")
|
||||
|
||||
// Check that the trace is actually there.
|
||||
get(t, "", "", "", string(tr.(*trace).ID), "", "")
|
||||
}
|
||||
|
||||
func TestHTTPUroboro(t *testing.T) {
|
||||
trA := New("TestHTTPUroboro", "trA")
|
||||
defer trA.Finish()
|
||||
trA.Printf("this is trace A")
|
||||
|
||||
trB := New("TestHTTPUroboro", "trB")
|
||||
defer trB.Finish()
|
||||
trB.Printf("this is trace B")
|
||||
|
||||
trA.Link(trB, "B is my friend")
|
||||
trB.Link(trA, "A is my friend")
|
||||
|
||||
// Check that we handle cross-linked events well.
|
||||
get(t, "TestHTTPUroboro", "", "", "", "", "")
|
||||
get(t, "TestHTTPUroboro", "-1", "", "", "", "")
|
||||
get(t, "", "", "", string(trA.(*trace).ID), "", "")
|
||||
get(t, "", "", "", string(trB.(*trace).ID), "", "")
|
||||
}
|
||||
|
||||
func TestHTTPDeep(t *testing.T) {
|
||||
tr := New("TestHTTPDeep", "level-0")
|
||||
defer tr.Finish()
|
||||
ts := []Trace{tr}
|
||||
for i := 1; i <= 9; i++ {
|
||||
tr = tr.NewChild("TestHTTPDeep", fmt.Sprintf("level-%d", i))
|
||||
defer tr.Finish()
|
||||
ts = append(ts, tr)
|
||||
}
|
||||
|
||||
// Active view.
|
||||
body := get(t, "TestHTTPDeep", "-1", "", "", "", "")
|
||||
checkContains(t, body, "level-9")
|
||||
|
||||
// Recursive view.
|
||||
body = get(t, "TestHTTPDeep", "", "", string(ts[0].(*trace).ID), "", "")
|
||||
checkContains(t, body, "level-9")
|
||||
}
|
||||
|
||||
func TestStripZeros(t *testing.T) {
|
||||
cases := []struct {
|
||||
d time.Duration
|
||||
exp string
|
||||
}{
|
||||
{0 * time.Second, " . 0"},
|
||||
{1 * time.Millisecond, " . 1000"},
|
||||
{5 * time.Millisecond, " . 5000"},
|
||||
{1 * time.Second, "1.000000"},
|
||||
{1*time.Second + 8*time.Millisecond, "1.008000"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := stripZeros(c.d); got != c.exp {
|
||||
t.Errorf("stripZeros(%s) got %q, expected %q",
|
||||
c.d, got, c.exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterHandler(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
RegisterHandler(mux)
|
||||
|
||||
req := httptest.NewRequest("GET", "/debug/traces", nil)
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
t.Errorf("expected 200, got %v", resp)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if !strings.Contains(string(body), "<h1>Traces</h1>") {
|
||||
t.Errorf("unexpected body: %s", body)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user