diff --git a/containers/container.go b/containers/container.go index b8cfacef..7e236f50 100644 --- a/containers/container.go +++ b/containers/container.go @@ -3,6 +3,7 @@ package containers import ( "io" "log" + "net/http" "os" "sort" "strings" @@ -743,11 +744,11 @@ func (c *Container) onL7Request(pid uint32, fd uint64, timestamp uint64, r *l7.R case l7.ProtocolHTTP: stats.observe(r.Status.Http(), "", r.Duration) payload := "" - headers := "" method := "" uri := "" response := "" host := "" + headers := http.Header{} req, err := l7.ParseHTTPRequest(r.Payload) if err != nil { log.Printf("Failed to parse payload %s, %q", err, string(r.Payload)) @@ -759,10 +760,6 @@ func (c *Container) onL7Request(pid uint32, fd uint64, timestamp uint64, r *l7.R base64String := base64.StdEncoding.EncodeToString(body) payload = string(base64String) } - if req != nil && req.Header != nil { - headersStr := l7.ConvertHeadersToString(req.Header) - headers = base64.StdEncoding.EncodeToString([]byte(headersStr)) - } method = req.Method if utf8.ValidString(req.URL.Path) { uri = req.URL.Path @@ -772,6 +769,9 @@ func (c *Container) onL7Request(pid uint32, fd uint64, timestamp uint64, r *l7.R uri = string([]rune(req.URL.Path)) } host = req.Host + if req.Header != nil { + headers = req.Header + } } if r.Response != nil { response = base64.StdEncoding.EncodeToString(r.Response) diff --git a/containers/l7.go b/containers/l7.go index b925888a..3da4c98c 100644 --- a/containers/l7.go +++ b/containers/l7.go @@ -69,7 +69,7 @@ func (s L7Stats) get(protocol l7.Protocol, key common.DestinationKey, r *l7.Requ labels = append(labels, "method") case l7.ProtocolHTTP: method, path := l7.ParseHttp(r.Payload) - if ValidUtf8(r.Payload) { + if ValidUtf8([]byte(path)) { constLabels["path"] = path } else { log.Printf("Failed to parse path %s", path) diff --git a/ebpftracer/l7/http.go b/ebpftracer/l7/http.go index 01a8a4a6..1d5fce4a 100644 --- a/ebpftracer/l7/http.go +++ b/ebpftracer/l7/http.go @@ -2,6 +2,7 @@ package l7 import ( "bytes" + "encoding/base64" "encoding/json" "errors" "io" @@ -129,7 +130,7 @@ func ParseHostFromHttpRequest(input string) (string, error) { return host, nil } -func ConvertHeadersToString(headers http.Header) string { +func ConvertHeadersToBase64String(headers http.Header) string { headerMap := make(map[string][]string) for key, values := range headers { headerMap[key] = values @@ -138,7 +139,8 @@ func ConvertHeadersToString(headers http.Header) string { if err != nil { return "" } - return string(jsonString) + header64 := base64.StdEncoding.EncodeToString([]byte(jsonString)) + return string(header64) } func SanitizeString(value string) string { diff --git a/ebpftracer/l7/l7_test.go b/ebpftracer/l7/l7_test.go index ad4194b0..6437d1df 100644 --- a/ebpftracer/l7/l7_test.go +++ b/ebpftracer/l7/l7_test.go @@ -108,7 +108,7 @@ func TestParseHost(t *testing.T) { assert.Nil(t, nil, err) assert.Equal(t, "xzxxxxxxzx", string(body)) - headers := ConvertHeadersToString(req.Header) + headers := ConvertHeadersToBase64String(req.Header) assert.NotNil(t, headers) requestString = "POST / HTTP/1.1\r\nHost: ec2.us-east-1.amazonaws.com\r\nUser-Agent: aws-sdk-go/1.50.10 (go1.21.6; linux; amd64) karpenter.sh-v0.34.0\r\nContent-Length: 422\r\nAuthorization: AWS4-HMAC-SHA256 Credential=ASIAUCTZOIG67J6IJFOY/20240408/us-east-1/ec2/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token, Signature=3d7eadceec976dd17fbdcc07ae43a75264baf179fec70c6aa2be7411fd552e70\r\nContent-Type: application/x-www-form-urlencoded; charset=utf-8\r\nX-Amz-Date: 20240408T094330Z\r\nX-Amz-Security-Token: IQoJb3JpZ2luX2VjEML//////////wEaCXVzLWVhc3QtMSJHMEUCIB8pKvU76WFQ99xqZH8LQXhOBfCP/lhnMSkmIp1LcvLEAiEAk3XkJCPMFGQC4KqMk92boxaeNHiLvbinmW9AQgEGnBcq/gQI6///////////ARABGgwyODA1MDEzMDU3ODkiDJA/sBIYp1t0z9szDirSBPDDQ+mKiBwDvCwqZ/HH9wSh9U6WKYjh0EPX9i3cHE46oee4CAUoQwEmDy9xZp02UZcpOjiDF6iaKyOvi93+LoiwJVBNi53o/PnMoStfI4OmG4Rc61YYcPc6t6SSXeBEGCzeeesWAGBDJ6GE2PWYYQAKS3YrpxYE+UKdb4hxEBO5t8RgEO/ooiHehFC/5VSmjMeMfT/XMdmAxBplX/wCMoz2vMcpkCi3fCsX0RNjxT7bAO/D2jOUrIX8UjM38h2VGNliSfCl8B3HbCETxYiz66AhMGabknMpJ8Dcotm\x00" @@ -118,7 +118,7 @@ func TestParseHost(t *testing.T) { assert.Equal(t, "ec2.us-east-1.amazonaws.com", req.Host) assert.Equal(t, "POST", req.Method) - headers = ConvertHeadersToString(req.Header) + headers = ConvertHeadersToBase64String(req.Header) assert.NotNil(t, headers) requestString = "POST / HTTP/1.1\r\nHost: ec2.us-east-1.amazonaws.com\r\nUser-Agent: aws-sdk-go/1.50.10 (go1.21.6; linux; amd64) karpenter.sh-v0.34.0\r\nContent-Length: 422\r\nAuthorization: AWS4-HMAC-SHA256 Credential=ASIAUCTZOIG67J6IJFOY/20240408/us-east-1/ec2/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token, Signature=3d7eadceec976dd17fbdcc07ae43a75264baf179fec70c6aa2be7411fd552e70\r\nContent-Type: application/x-www-form-urlencoded; charset=utf-8\r\nX-Amz-Date: 20240408T094330Z\r\nX-Amz-Security-Token: IQoJb3JpZ2luX2VjEML//////////wEaCXVzLWVhc3QtMSJHMEUCIB8pKvU76WFQ99xqZH8LQXhOBfCP/lhnMSkmIp1LcvLEAiEAk3XkJCPMFGQC4KqMk92boxaeNHiLvbinmW9AQgEGnBcq/gQI6///////////ARABGgwyODA1MDEzMDU3ODkiDJA/sBIYp1t0z9szDirSBPDDQ+mKiBwDvCwqZ/HH9wSh9U6WKYjh0EPX9i3cHE46oee4CAUoQwEmDy9xZp02UZcpOjiDF6iaKyOvi93+LoiwJVBNi53o/PnMoStfI4OmG4Rc61YYcPc6t6SSXeBEGCzeeesWAGBDJ6GE2PWYYQAKS3YrpxYE+UKdb4hxEBO5t8RgEO/ooiHehFC/5VSmjMeMfT/XMdmAxBplX/wCMoz2vMcpkCi3fCsX0RNjxT7bAO/D2jOUrIX8UjM38h2VGNliSfCl8B3HbCETxYiz66AhMGabknMpJ8Dcotm\x00" @@ -128,7 +128,7 @@ func TestParseHost(t *testing.T) { assert.Equal(t, "ec2.us-east-1.amazonaws.com", req.Host) assert.Equal(t, "POST", req.Method) - headers = ConvertHeadersToString(req.Header) + headers = ConvertHeadersToBase64String(req.Header) assert.NotNil(t, headers) requestString = "GET /api/v1/namespaces/nudgebee-agent/pods/nudgebee-agent-pgnxh/log?container=node-agent&previous=False&tailLines=1000×tamps=True HTTP/1.1\r\nHost: 10.100.0.1\r\nAccept-Encoding: identity\r\nAccept: application/json\r\nUser-Agent: OpenAPI-Generator/26.1.0/python\r\nauthorization: bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNhZTFlNjlkN2I1ZmFkNzhhNTg2YTVjN2JmMDNjOGU3YjUzNGI4N2MifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjIl0sImV4cCI6MTc0NDEwMjY0OSwiaWF0IjoxNzEyNTY2NjQ5LCJpc3MiOiJodHRwczovL29pZGMuZWtzLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tL2lkL0I4RkU4OUQ4MjdEN0Q2RTE0REIyMUMzRkIwQTk1Q0E3Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJudWRnZWJlZS1hZ2VudCIsInBvZCI6eyJuYW1lIjoibnVkZ2ViZWUtYWdlbnQtcnVubmVyLTZiZmY2NmQ3YjgtcWttbmwiLCJ1aWQiOiJiN2M4ZGM2NC04Y2Q0LTRiMWUtODg1Zi00YWMzMWE4NDYwMWIifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6Im51ZGdlYmVlLWFnZW50LXJ1bm5lci1zZXJ2aWNlLWFjY291bnQiLCJ1aWQiOiJjODUzMDc2YS1hODVkLTQzYzItOWU3NS00MTk5ODIzMTVjOGIifSwid2FybmFmdGVyIjoxNzEyNTcwMjU2fSwibmJmIjoxNzEyNTY2NjQ5LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6b\x00" @@ -138,7 +138,7 @@ func TestParseHost(t *testing.T) { assert.Equal(t, "10.100.0.1", req.Host) assert.Equal(t, "GET", req.Method) - headers = ConvertHeadersToString(req.Header) + headers = ConvertHeadersToBase64String(req.Header) assert.NotNil(t, headers) requestString = "PUT /nudgebee-dev-loki-logs/fake/ece64b2028d30d33/18ebcc10d27%3A18ebcfd01d3%3A3377d9aa HTTP/1.1\r\nHost: s3.amazonaws.com\r\nUser-Agent: aws-sdk-go/1.44.315 (go1.21.3; linux; amd64)\r\nContent-Length: 14183\r\nAuthorization: AWS4-HMAC-SHA256 Credential=ASIAUCTZOIG62SVE2F5J/20240408/us-east-1/s3/aws4_request, SignedHeaders=content-length;content-md5;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-storage-class, Signature=a4db153ba7127721c3a5efbbab1a0e194e473d23147a472c92ff672d9dff160c\r\nContent-Md5: VBSxwLVjCvMeC+z4V86k8A==\r\nX-Amz-Content-Sha256: fe19171172133fadb236229e71a8f60169fc5268da749b1eeeeb3396e7d90f64\r\nX-Amz-Date: 20240408T094454Z\r\nX-Amz-Security-Token: IQoJb3JpZ2luX2VjEL3//////////wEaCXVzLWVhc3QtMSJGMEQCIAvNhx/LC+nIkqUC+rRD3MOFuxr39bc+qmRwVPljywxuAiA/8bmpr+kmRy7zXBzBQ7eyayLftqLutyGrzu8TIZWVbyrGBQjm//////////8BEAEaDDI4MDUwMTMwNTc4OSIMRsng8zavN9ilo6+0KpoFc/pdelkZBjWQle/CNG4XKcnd9WFbu87JAB3xa1P97vfIBjhqpmHoPk4V38r21Vepl0NjeUnWahJA6NnOWW2mdhRv51+WGUmKSHz1j6NCRcC8axDXs65jJzF6WgqCGZGNa0aiUQP7oIGGV8\x00" @@ -148,7 +148,7 @@ func TestParseHost(t *testing.T) { assert.Equal(t, "s3.amazonaws.com", req.Host) assert.Equal(t, "PUT", req.Method) - headers = ConvertHeadersToString(req.Header) + headers = ConvertHeadersToBase64String(req.Header) assert.NotNil(t, headers) requestString = "POST /api/v1/write HTTP/1.1\r\nHost: vmsingle-victoria-victoria-metrics-k8s-stack.victoria.svc:8429\r\nUser-Agent: vmagent\r\nContent-Length: 282715\r\nContent-Encoding: snappy\r\nContent-Type: application/x-protobuf\r\nX-Prometheus-Remote-Write-Version: 0.1.0\r\nAccept-Encoding: gzip\r\n\r\n\x91\x9b\xdc\x01\xb0\n\xe4\f\n$\n\b__name__\x12\x18container_memory_failcnt\n$\n\t\x15\x1c\xd0\x12\x17nudgebee-agent-opencost\n;\n\x05image\x122quay.io/kubecost1\x15\n\x00-\x01*X-model:prod-1.108.0\n\x1b\n\t\x01\x87\x18space\x12\x0e6c\x00 \n/\n\x03pod\x12(6\x17\x00\x15z\xd8-55b677fb47-87mz4\n \n\x17beta_kubernetes_io_arch\x12\x05amd64\n.\n J\"\x00pinstance_type\x12\nm5ad.large\n\x1e\n\x15J0\x00\xf0Cos\x12\x05linux\n&\n\x1eeks_amazonaws_com_capacityType\x12\x04SPOT\n5\n(failure_domain_JW\x00Pregion\x12\tus-east-1\n4\n&\x867\x00\x14zone\x12\n\x155\x10a\n(\n\b\x11Ҙ\x12\x1cip-172-31-0-236.ec2.internal\n\x0e\n\x03job\x12\a!1 let\n=\n\x19k8%5\xf0= 400, + traceId, semconv.HTTPURL(fmt.Sprintf("http://%s%s", requestHost, requestPath)), semconv.HTTPMethod(method), semconv.HTTPStatusCode(int(status)), @@ -225,7 +267,7 @@ func (t *Trace) Http2Request(method, path, scheme string, status l7.Status, dura requestPayload = payload } - t.createSpan(method, duration, status > 400, + t.createSpan(method, duration, status > 400, "", semconv.HTTPURL(fmt.Sprintf("%s://%s%s", scheme, t.destination.String(), path)), semconv.HTTPMethod(method), semconv.HTTPStatusCode(int(status)), @@ -237,7 +279,7 @@ func (t *Trace) PostgresQuery(query string, error bool, duration time.Duration) if t == nil || query == "" { return } - t.createSpan("query", duration, error, + t.createSpan("query", duration, error, "", semconv.DBSystemPostgreSQL, semconv.DBStatement(query), ) @@ -247,7 +289,7 @@ func (t *Trace) MysqlQuery(query string, error bool, duration time.Duration) { if t == nil || query == "" { return } - t.createSpan("query", duration, error, + t.createSpan("query", duration, error, "", semconv.DBSystemMySQL, semconv.DBStatement(query), ) @@ -257,7 +299,7 @@ func (t *Trace) MongoQuery(query string, error bool, duration time.Duration) { if t == nil || query == "" { return } - t.createSpan("query", duration, error, + t.createSpan("query", duration, error, "", semconv.DBSystemMongoDB, semconv.DBStatement(query), ) @@ -276,7 +318,7 @@ func (t *Trace) MemcachedQuery(cmd string, items []string, error bool, duration } else if len(items) > 1 { attrs = append(attrs, MemcacheDBItemKeyName.StringSlice(items)) } - t.createSpan(cmd, duration, error, attrs...) + t.createSpan(cmd, duration, error, "", attrs...) } func (t *Trace) RedisQuery(cmd, args string, error bool, duration time.Duration) { @@ -287,7 +329,7 @@ func (t *Trace) RedisQuery(cmd, args string, error bool, duration time.Duration) if args != "" { statement += " " + args } - t.createSpan(cmd, duration, error, + t.createSpan(cmd, duration, error, "", semconv.DBSystemRedis, semconv.DBOperation(cmd), semconv.DBStatement(statement), @@ -298,7 +340,7 @@ func (t *Trace) ClickhouseQuery(query string, error bool, duration time.Duration if t == nil { return } - t.createSpan("query", duration, error, + t.createSpan("query", duration, error, "", semconv.DBSystemClickhouse, semconv.DBStatement(query), ) @@ -315,7 +357,7 @@ func (t *Trace) ZookeeperRequest(op string, args string, status l7.Status, durat if args != "" { statement += " " + args } - t.createSpan(op, duration, status.Zookeeper() != "ok", + t.createSpan(op, duration, status.Zookeeper() != "ok", "", semconv.DBSystemKey.String("zookeeper"), semconv.DBOperation(op), semconv.DBStatementKey.String(statement),