From eb597d65663a332f208bb8f31ffb8838e2d89547 Mon Sep 17 00:00:00 2001 From: Ron Federman <73110295+RonFed@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:55:37 +0200 Subject: [PATCH] Add status code to the HTTP client probe (#527) * Add status code the HTTP client probe * update tests --- Makefile | 2 +- internal/include/arguments.h | 5 + internal/pkg/inject/offset_results.json | 153 ++++++++++++++++++ .../bpf/net/http/client/bpf/probe.bpf.c | 73 +++++++-- .../bpf/net/http/client/bpf_bpfel_arm64.go | 46 +++--- .../bpf/net/http/client/bpf_bpfel_x86.go | 46 +++--- .../bpf/net/http/client/probe.go | 10 +- .../bpf/net/http/server/probe.go | 2 +- .../bpf/net/http/server/probe_test.go | 2 +- internal/test/e2e/databasesql/traces.json | 8 +- internal/test/e2e/gin/traces.json | 8 +- internal/test/e2e/nethttp/traces.json | 8 +- internal/test/e2e/nethttp/verify.bats | 9 +- internal/tools/inspect/cmd/offsetgen/main.go | 1 + 14 files changed, 306 insertions(+), 67 deletions(-) diff --git a/Makefile b/Makefile index bd166f9b1..b83cab226 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ fixtures/%: if [ ! -d "opentelemetry-helm-charts" ]; then \ git clone https://github.com/open-telemetry/opentelemetry-helm-charts.git; \ fi - helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/opentelemetry-collector + helm install test -f .github/workflows/e2e/k8s/collector-helm-values.yml opentelemetry-helm-charts/charts/opentelemetry-collector kubectl wait --for=condition=Ready --timeout=60s pod/test-opentelemetry-collector-0 kubectl -n default create -f .github/workflows/e2e/k8s/sample-job.yml kubectl wait --for=condition=Complete --timeout=60s job/sample-job diff --git a/internal/include/arguments.h b/internal/include/arguments.h index 8ba197b45..17904c72e 100644 --- a/internal/include/arguments.h +++ b/internal/include/arguments.h @@ -83,4 +83,9 @@ static __always_inline void *get_consistent_key(struct pt_regs *ctx, void *conte return contextContext; } +static __always_inline bool is_register_abi() +{ + return is_registers_abi; +} + #endif diff --git a/internal/pkg/inject/offset_results.json b/internal/pkg/inject/offset_results.json index e1b015cb7..959716b15 100755 --- a/internal/pkg/inject/offset_results.json +++ b/internal/pkg/inject/offset_results.json @@ -2093,6 +2093,159 @@ } ] }, + { + "struct": "Response", + "fields": [ + { + "field": "StatusCode", + "offsets": [ + { + "offset": 16, + "versions": [ + "1.12.0", + "1.12.1", + "1.12.2", + "1.12.3", + "1.12.4", + "1.12.5", + "1.12.6", + "1.12.7", + "1.12.8", + "1.12.9", + "1.12.10", + "1.12.11", + "1.12.12", + "1.12.13", + "1.12.14", + "1.12.15", + "1.12.16", + "1.12.17", + "1.13.0", + "1.13.1", + "1.13.2", + "1.13.3", + "1.13.4", + "1.13.5", + "1.13.6", + "1.13.7", + "1.13.8", + "1.13.9", + "1.13.10", + "1.13.11", + "1.13.12", + "1.13.13", + "1.13.14", + "1.13.15", + "1.14.0", + "1.14.1", + "1.14.2", + "1.14.3", + "1.14.4", + "1.14.5", + "1.14.6", + "1.14.7", + "1.14.8", + "1.14.9", + "1.14.10", + "1.14.11", + "1.14.12", + "1.14.13", + "1.14.14", + "1.14.15", + "1.15.0", + "1.15.1", + "1.15.2", + "1.15.3", + "1.15.4", + "1.15.5", + "1.15.6", + "1.15.7", + "1.15.8", + "1.15.9", + "1.15.10", + "1.15.11", + "1.15.12", + "1.15.13", + "1.15.14", + "1.15.15", + "1.16.0", + "1.16.1", + "1.16.2", + "1.16.3", + "1.16.4", + "1.16.5", + "1.16.6", + "1.16.7", + "1.16.8", + "1.16.9", + "1.16.10", + "1.16.11", + "1.16.12", + "1.16.13", + "1.16.14", + "1.16.15", + "1.17.0", + "1.17.1", + "1.17.2", + "1.17.3", + "1.17.4", + "1.17.5", + "1.17.6", + "1.17.7", + "1.17.8", + "1.17.9", + "1.17.10", + "1.17.11", + "1.17.12", + "1.17.13", + "1.18.0", + "1.18.1", + "1.18.2", + "1.18.3", + "1.18.4", + "1.18.5", + "1.18.6", + "1.18.7", + "1.18.8", + "1.18.9", + "1.18.10", + "1.19.0", + "1.19.1", + "1.19.2", + "1.19.3", + "1.19.4", + "1.19.5", + "1.19.6", + "1.19.7", + "1.19.8", + "1.19.9", + "1.19.10", + "1.19.11", + "1.19.12", + "1.19.13", + "1.20.0", + "1.20.1", + "1.20.2", + "1.20.3", + "1.20.4", + "1.20.5", + "1.20.6", + "1.20.7", + "1.20.8", + "1.20.9", + "1.20.10", + "1.20.11", + "1.21.0", + "1.21.1", + "1.21.2", + "1.21.3", + "1.21.4" + ] + } + ] + } + ] + }, { "struct": "response", "fields": [ diff --git a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c index e94795215..ac60fdd0f 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c +++ b/internal/pkg/instrumentation/bpf/net/http/client/bpf/probe.bpf.c @@ -14,6 +14,7 @@ char __license[] SEC("license") = "Dual MIT/GPL"; struct http_request_t { BASE_SPAN_PROPERTIES + u64 status_code; char method[MAX_METHOD_SIZE]; char path[MAX_PATH_SIZE]; }; @@ -32,6 +33,14 @@ struct { __uint(max_entries, 1); } golang_mapbucket_storage_map SEC(".maps"); +struct +{ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(struct http_request_t)); + __uint(max_entries, 1); +} http_client_uprobe_storage_map SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); } events SEC(".maps"); @@ -43,6 +52,7 @@ volatile const u64 path_ptr_pos; volatile const u64 headers_ptr_pos; volatile const u64 ctx_ptr_pos; volatile const u64 buckets_ptr_pos; +volatile const u64 status_code_pos; static __always_inline long inject_header(void* headers_ptr, struct span_context* propagated_ctx) { // Read the key-value count - this field must be the first one in the hmap struct as documented in src/runtime/map.go @@ -146,12 +156,9 @@ static __always_inline long inject_header(void* headers_ptr, struct span_context } // This instrumentation attaches uprobe to the following function: -// func net/http/client.Do(req *Request) +// func net/http/client.Do(req *Request) (*Response, error) SEC("uprobe/HttpClient_Do") int uprobe_HttpClient_Do(struct pt_regs *ctx) { - struct http_request_t httpReq = {}; - httpReq.start_time = bpf_ktime_get_ns(); - u64 request_pos = 2; void *req_ptr = get_argument(ctx, request_pos); @@ -169,16 +176,27 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { return 0; } + u32 map_id = 0; + struct http_request_t *httpReq = bpf_map_lookup_elem(&http_client_uprobe_storage_map, &map_id); + if (httpReq == NULL) + { + bpf_printk("uprobe/HttpClient_Do: httpReq is NULL"); + return 0; + } + + __builtin_memset(httpReq, 0, sizeof(struct http_request_t)); + httpReq->start_time = bpf_ktime_get_ns(); + struct span_context *parent_span_ctx = get_parent_span_context(context_ptr_val); if (parent_span_ctx != NULL) { - bpf_probe_read(&httpReq.psc, sizeof(httpReq.psc), parent_span_ctx); - copy_byte_arrays(httpReq.psc.TraceID, httpReq.sc.TraceID, TRACE_ID_SIZE); - generate_random_bytes(httpReq.sc.SpanID, SPAN_ID_SIZE); + bpf_probe_read(&httpReq->psc, sizeof(httpReq->psc), parent_span_ctx); + copy_byte_arrays(httpReq->psc.TraceID, httpReq->sc.TraceID, TRACE_ID_SIZE); + generate_random_bytes(httpReq->sc.SpanID, SPAN_ID_SIZE); } else { - httpReq.sc = generate_span_context(); + httpReq->sc = generate_span_context(); } - if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq.method, sizeof(httpReq.method))) { + if (!get_go_string_from_user_ptr((void *)(req_ptr+method_ptr_pos), httpReq->method, sizeof(httpReq->method))) { bpf_printk("uprobe_HttpClient_Do: Failed to get method from request"); return 0; } @@ -186,7 +204,7 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { // get path from Request.URL void *url_ptr = 0; bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr+url_ptr_pos)); - if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq.path, sizeof(httpReq.path))) { + if (!get_go_string_from_user_ptr((void *)(url_ptr+path_ptr_pos), httpReq->path, sizeof(httpReq->path))) { bpf_printk("uprobe_HttpClient_Do: Failed to get path from Request.URL"); return 0; } @@ -194,17 +212,42 @@ int uprobe_HttpClient_Do(struct pt_regs *ctx) { // get headers from Request void *headers_ptr = 0; bpf_probe_read(&headers_ptr, sizeof(headers_ptr), (void *)(req_ptr+headers_ptr_pos)); - long res = inject_header(headers_ptr, &httpReq.sc); + long res = inject_header(headers_ptr, &httpReq->sc); if (res < 0) { bpf_printk("uprobe_HttpClient_Do: Failed to inject header"); } // Write event - bpf_map_update_elem(&http_events, &key, &httpReq, 0); - start_tracking_span(context_ptr_val, &httpReq.sc); + bpf_map_update_elem(&http_events, &key, httpReq, 0); + start_tracking_span(context_ptr_val, &httpReq->sc); return 0; } // This instrumentation attaches uretprobe to the following function: -// func net/http/client.Do(req *Request) -UPROBE_RETURN(HttpClient_Do, struct http_request_t, http_events, events, 2, ctx_ptr_pos, false) \ No newline at end of file +// func net/http/client.Do(req *Request) (*Response, error) +SEC("uprobe/HttpClient_Do") +int uprobe_HttpClient_Do_Returns(struct pt_regs *ctx) { + u64 end_time = bpf_ktime_get_ns(); + void *req_ctx_ptr = get_Go_context(ctx, 2, ctx_ptr_pos, false); + void *key = get_consistent_key(ctx, req_ctx_ptr); + + struct http_request_t *http_req_span = bpf_map_lookup_elem(&http_events, &key); + if (http_req_span == NULL) { + bpf_printk("probe_HttpClient_Do_Returns: entry_state is NULL"); + return 0; + } + bpf_map_delete_elem(&http_events, &key); + + if (is_register_abi()) { + // Getting the returned response + void *resp_ptr = get_argument(ctx, 1); + // Get status code from response + bpf_probe_read(&http_req_span->status_code, sizeof(http_req_span->status_code), (void *)(resp_ptr + status_code_pos)); + } + + http_req_span->end_time = end_time; + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, http_req_span, sizeof(*http_req_span)); + stop_tracking_span(&http_req_span->sc, &http_req_span->psc); + return 0; +} \ No newline at end of file diff --git a/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_arm64.go b/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_arm64.go index 8bb7fecc7..616d42156 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_arm64.go +++ b/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_arm64.go @@ -13,13 +13,14 @@ import ( ) type bpfHttpRequestT struct { - StartTime uint64 - EndTime uint64 - Sc bpfSpanContext - Psc bpfSpanContext - Method [10]int8 - Path [100]int8 - _ [2]byte + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext + StatusCode uint64 + Method [10]int8 + Path [100]int8 + _ [2]byte } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } @@ -78,13 +79,14 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - GolangMapbucketStorageMap *ebpf.MapSpec `ebpf:"golang_mapbucket_storage_map"` - HttpEvents *ebpf.MapSpec `ebpf:"http_events"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + GolangMapbucketStorageMap *ebpf.MapSpec `ebpf:"golang_mapbucket_storage_map"` + HttpClientUprobeStorageMap *ebpf.MapSpec `ebpf:"http_client_uprobe_storage_map"` + HttpEvents *ebpf.MapSpec `ebpf:"http_events"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -106,13 +108,14 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - GolangMapbucketStorageMap *ebpf.Map `ebpf:"golang_mapbucket_storage_map"` - HttpEvents *ebpf.Map `ebpf:"http_events"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + GolangMapbucketStorageMap *ebpf.Map `ebpf:"golang_mapbucket_storage_map"` + HttpClientUprobeStorageMap *ebpf.Map `ebpf:"http_client_uprobe_storage_map"` + HttpEvents *ebpf.Map `ebpf:"http_events"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { @@ -120,6 +123,7 @@ func (m *bpfMaps) Close() error { m.AllocMap, m.Events, m.GolangMapbucketStorageMap, + m.HttpClientUprobeStorageMap, m.HttpEvents, m.SliceArrayBuffMap, m.TrackedSpans, diff --git a/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_x86.go b/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_x86.go index 981511bab..6cd4fc516 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_x86.go +++ b/internal/pkg/instrumentation/bpf/net/http/client/bpf_bpfel_x86.go @@ -13,13 +13,14 @@ import ( ) type bpfHttpRequestT struct { - StartTime uint64 - EndTime uint64 - Sc bpfSpanContext - Psc bpfSpanContext - Method [10]int8 - Path [100]int8 - _ [2]byte + StartTime uint64 + EndTime uint64 + Sc bpfSpanContext + Psc bpfSpanContext + StatusCode uint64 + Method [10]int8 + Path [100]int8 + _ [2]byte } type bpfSliceArrayBuff struct{ Buff [1024]uint8 } @@ -78,13 +79,14 @@ type bpfProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type bpfMapSpecs struct { - AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` - Events *ebpf.MapSpec `ebpf:"events"` - GolangMapbucketStorageMap *ebpf.MapSpec `ebpf:"golang_mapbucket_storage_map"` - HttpEvents *ebpf.MapSpec `ebpf:"http_events"` - SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` + AllocMap *ebpf.MapSpec `ebpf:"alloc_map"` + Events *ebpf.MapSpec `ebpf:"events"` + GolangMapbucketStorageMap *ebpf.MapSpec `ebpf:"golang_mapbucket_storage_map"` + HttpClientUprobeStorageMap *ebpf.MapSpec `ebpf:"http_client_uprobe_storage_map"` + HttpEvents *ebpf.MapSpec `ebpf:"http_events"` + SliceArrayBuffMap *ebpf.MapSpec `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.MapSpec `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.MapSpec `ebpf:"tracked_spans_by_sc"` } // bpfObjects contains all objects after they have been loaded into the kernel. @@ -106,13 +108,14 @@ func (o *bpfObjects) Close() error { // // It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. type bpfMaps struct { - AllocMap *ebpf.Map `ebpf:"alloc_map"` - Events *ebpf.Map `ebpf:"events"` - GolangMapbucketStorageMap *ebpf.Map `ebpf:"golang_mapbucket_storage_map"` - HttpEvents *ebpf.Map `ebpf:"http_events"` - SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` - TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` - TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` + AllocMap *ebpf.Map `ebpf:"alloc_map"` + Events *ebpf.Map `ebpf:"events"` + GolangMapbucketStorageMap *ebpf.Map `ebpf:"golang_mapbucket_storage_map"` + HttpClientUprobeStorageMap *ebpf.Map `ebpf:"http_client_uprobe_storage_map"` + HttpEvents *ebpf.Map `ebpf:"http_events"` + SliceArrayBuffMap *ebpf.Map `ebpf:"slice_array_buff_map"` + TrackedSpans *ebpf.Map `ebpf:"tracked_spans"` + TrackedSpansBySc *ebpf.Map `ebpf:"tracked_spans_by_sc"` } func (m *bpfMaps) Close() error { @@ -120,6 +123,7 @@ func (m *bpfMaps) Close() error { m.AllocMap, m.Events, m.GolangMapbucketStorageMap, + m.HttpClientUprobeStorageMap, m.HttpEvents, m.SliceArrayBuffMap, m.TrackedSpans, diff --git a/internal/pkg/instrumentation/bpf/net/http/client/probe.go b/internal/pkg/instrumentation/bpf/net/http/client/probe.go index 03edd45c9..9f85f8b98 100644 --- a/internal/pkg/instrumentation/bpf/net/http/client/probe.go +++ b/internal/pkg/instrumentation/bpf/net/http/client/probe.go @@ -69,6 +69,10 @@ func New(logger logr.Logger) probe.Probe { Key: "ctx_ptr_pos", Val: structfield.NewID("std", "net/http", "Request", "ctx"), }, + probe.StructFieldConst{ + Key: "status_code_pos", + Val: structfield.NewID("std", "net/http", "Response", "StatusCode"), + }, probe.StructFieldConst{ Key: "buckets_ptr_pos", Val: structfield.NewID("std", "runtime", "hmap", "buckets"), @@ -120,8 +124,9 @@ func uprobeDo(name string, exec *link.Executable, target *process.TargetDetails, // request-response. type event struct { context.BaseSpanProperties - Method [10]byte - Path [100]byte + StatusCode uint64 + Method [10]byte + Path [100]byte } func convertEvent(e *event) *probe.Event { @@ -157,6 +162,7 @@ func convertEvent(e *event) *probe.Event { Attributes: []attribute.KeyValue{ semconv.HTTPMethodKey.String(method), semconv.HTTPTargetKey.String(path), + semconv.HTTPResponseStatusCodeKey.Int(int(e.StatusCode)), }, ParentSpanContext: pscPtr, } diff --git a/internal/pkg/instrumentation/bpf/net/http/server/probe.go b/internal/pkg/instrumentation/bpf/net/http/server/probe.go index adf3f8b90..2abf63637 100644 --- a/internal/pkg/instrumentation/bpf/net/http/server/probe.go +++ b/internal/pkg/instrumentation/bpf/net/http/server/probe.go @@ -168,7 +168,7 @@ func convertEvent(e *event) *probe.Event { Attributes: []attribute.KeyValue{ semconv.HTTPMethodKey.String(method), semconv.HTTPTargetKey.String(path), - semconv.HTTPStatusCodeKey.Int(int(e.StatusCode)), + semconv.HTTPResponseStatusCodeKey.Int(int(e.StatusCode)), }, } } diff --git a/internal/pkg/instrumentation/bpf/net/http/server/probe_test.go b/internal/pkg/instrumentation/bpf/net/http/server/probe_test.go index 1d666d122..07de0d087 100644 --- a/internal/pkg/instrumentation/bpf/net/http/server/probe_test.go +++ b/internal/pkg/instrumentation/bpf/net/http/server/probe_test.go @@ -63,7 +63,7 @@ func TestProbeConvertEvent(t *testing.T) { Attributes: []attribute.KeyValue{ semconv.HTTPMethodKey.String("GET"), semconv.HTTPTargetKey.String("/foo/bar"), - semconv.HTTPStatusCodeKey.Int(200), + semconv.HTTPResponseStatusCodeKey.Int(200), }, } assert.Equal(t, want, got) diff --git a/internal/test/e2e/databasesql/traces.json b/internal/test/e2e/databasesql/traces.json index b9003af3a..0643debde 100644 --- a/internal/test/e2e/databasesql/traces.json +++ b/internal/test/e2e/databasesql/traces.json @@ -88,7 +88,7 @@ } }, { - "key": "http.status_code", + "key": "http.response.status_code", "value": { "intValue": "200" } @@ -114,6 +114,12 @@ "value": { "stringValue": "/query_db" } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } } ], "kind": 3, diff --git a/internal/test/e2e/gin/traces.json b/internal/test/e2e/gin/traces.json index ca882aaf0..e1dab544b 100644 --- a/internal/test/e2e/gin/traces.json +++ b/internal/test/e2e/gin/traces.json @@ -94,7 +94,7 @@ } }, { - "key": "http.status_code", + "key": "http.response.status_code", "value": { "intValue": "200" } @@ -120,6 +120,12 @@ "value": { "stringValue": "/hello-gin" } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } } ], "kind": 3, diff --git a/internal/test/e2e/nethttp/traces.json b/internal/test/e2e/nethttp/traces.json index 1d7abf3dc..53882a42e 100644 --- a/internal/test/e2e/nethttp/traces.json +++ b/internal/test/e2e/nethttp/traces.json @@ -64,7 +64,7 @@ } }, { - "key": "http.status_code", + "key": "http.response.status_code", "value": { "intValue": "200" } @@ -90,6 +90,12 @@ "value": { "stringValue": "/hello" } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } } ], "kind": 3, diff --git a/internal/test/e2e/nethttp/verify.bats b/internal/test/e2e/nethttp/verify.bats index 83e1176a3..3b6141b83 100644 --- a/internal/test/e2e/nethttp/verify.bats +++ b/internal/test/e2e/nethttp/verify.bats @@ -24,8 +24,13 @@ SCOPE="go.opentelemetry.io/auto/net/http" assert_equal "$result" '"/hello"' } -@test "server :: includes http.status_code attribute" { - result=$(server_span_attributes_for ${SCOPE} | jq "select(.key == \"http.status_code\").value.intValue") +@test "server :: includes hhttp.response.status_code attribute" { + result=$(server_span_attributes_for ${SCOPE} | jq "select(.key == \"http.response.status_code\").value.intValue") + assert_equal "$result" '"200"' +} + +@test "client :: includes http.response.status_code attribute" { + result=$(client_span_attributes_for ${SCOPE} | jq "select(.key == \"http.response.status_code\").value.intValue") assert_equal "$result" '"200"' } diff --git a/internal/tools/inspect/cmd/offsetgen/main.go b/internal/tools/inspect/cmd/offsetgen/main.go index b8e259f19..e89e4176e 100644 --- a/internal/tools/inspect/cmd/offsetgen/main.go +++ b/internal/tools/inspect/cmd/offsetgen/main.go @@ -106,6 +106,7 @@ func manifests() ([]inspect.Manifest, error) { structfield.NewID("std", "net/http", "Request", "RemoteAddr"), structfield.NewID("std", "net/http", "Request", "Header"), structfield.NewID("std", "net/http", "Request", "ctx"), + structfield.NewID("std", "net/http", "Response", "StatusCode"), structfield.NewID("std", "net/http", "response", "req"), structfield.NewID("std", "net/http", "response", "status"), structfield.NewID("std", "net/url", "URL", "Path"),