diff --git a/internal/lokiapi/oas_handlers_gen.go b/internal/lokiapi/oas_handlers_gen.go index e8f20b49..a4302d6e 100644 --- a/internal/lokiapi/oas_handlers_gen.go +++ b/internal/lokiapi/oas_handlers_gen.go @@ -20,12 +20,24 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleIndexStatsRequest handles indexStats operation. // // Get index stats. // // GET /loki/api/v1/index/stats func (s *Server) handleIndexStatsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("indexStats"), semconv.HTTPRequestMethodKey.String("GET"), @@ -47,7 +59,16 @@ func (s *Server) handleIndexStatsRequest(args [0]string, argsEscaped bool, w htt startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -59,8 +80,27 @@ func (s *Server) handleIndexStatsRequest(args [0]string, argsEscaped bool, w htt var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -157,6 +197,8 @@ func (s *Server) handleIndexStatsRequest(args [0]string, argsEscaped bool, w htt // // GET /loki/api/v1/label/{name}/values func (s *Server) handleLabelValuesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("labelValues"), semconv.HTTPRequestMethodKey.String("GET"), @@ -178,7 +220,16 @@ func (s *Server) handleLabelValuesRequest(args [1]string, argsEscaped bool, w ht startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -190,8 +241,27 @@ func (s *Server) handleLabelValuesRequest(args [1]string, argsEscaped bool, w ht var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -297,6 +367,8 @@ func (s *Server) handleLabelValuesRequest(args [1]string, argsEscaped bool, w ht // // GET /loki/api/v1/labels func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("labels"), semconv.HTTPRequestMethodKey.String("GET"), @@ -318,7 +390,16 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -330,8 +411,27 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -428,6 +528,8 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re // // POST /loki/api/v1/push func (s *Server) handlePushRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("push"), semconv.HTTPRequestMethodKey.String("POST"), @@ -449,7 +551,16 @@ func (s *Server) handlePushRequest(args [0]string, argsEscaped bool, w http.Resp startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -461,8 +572,27 @@ func (s *Server) handlePushRequest(args [0]string, argsEscaped bool, w http.Resp var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -551,6 +681,8 @@ func (s *Server) handlePushRequest(args [0]string, argsEscaped bool, w http.Resp // // GET /loki/api/v1/query func (s *Server) handleQueryRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("query"), semconv.HTTPRequestMethodKey.String("GET"), @@ -572,7 +704,16 @@ func (s *Server) handleQueryRequest(args [0]string, argsEscaped bool, w http.Res startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -584,8 +725,27 @@ func (s *Server) handleQueryRequest(args [0]string, argsEscaped bool, w http.Res var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -686,6 +846,8 @@ func (s *Server) handleQueryRequest(args [0]string, argsEscaped bool, w http.Res // // GET /loki/api/v1/query_range func (s *Server) handleQueryRangeRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("queryRange"), semconv.HTTPRequestMethodKey.String("GET"), @@ -707,7 +869,16 @@ func (s *Server) handleQueryRangeRequest(args [0]string, argsEscaped bool, w htt startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -719,8 +890,27 @@ func (s *Server) handleQueryRangeRequest(args [0]string, argsEscaped bool, w htt var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -833,6 +1023,8 @@ func (s *Server) handleQueryRangeRequest(args [0]string, argsEscaped bool, w htt // // GET /loki/api/v1/series func (s *Server) handleSeriesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("series"), semconv.HTTPRequestMethodKey.String("GET"), @@ -854,7 +1046,16 @@ func (s *Server) handleSeriesRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -866,8 +1067,27 @@ func (s *Server) handleSeriesRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ diff --git a/internal/otelbotapi/oas_handlers_gen.go b/internal/otelbotapi/oas_handlers_gen.go index de506232..891e2ad5 100644 --- a/internal/otelbotapi/oas_handlers_gen.go +++ b/internal/otelbotapi/oas_handlers_gen.go @@ -20,12 +20,24 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleGetStatusRequest handles getStatus operation. // // Get status. // // GET /status func (s *Server) handleGetStatusRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getStatus"), semconv.HTTPRequestMethodKey.String("GET"), @@ -47,7 +59,16 @@ func (s *Server) handleGetStatusRequest(args [0]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -59,8 +80,27 @@ func (s *Server) handleGetStatusRequest(args [0]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -178,6 +218,8 @@ func (s *Server) handleGetStatusRequest(args [0]string, argsEscaped bool, w http // // GET /ping func (s *Server) handlePingRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("ping"), semconv.HTTPRequestMethodKey.String("GET"), @@ -199,7 +241,16 @@ func (s *Server) handlePingRequest(args [0]string, argsEscaped bool, w http.Resp startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -211,8 +262,27 @@ func (s *Server) handlePingRequest(args [0]string, argsEscaped bool, w http.Resp var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) @@ -282,6 +352,8 @@ func (s *Server) handlePingRequest(args [0]string, argsEscaped bool, w http.Resp // // POST /report/submit func (s *Server) handleSubmitReportRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("submitReport"), semconv.HTTPRequestMethodKey.String("POST"), @@ -303,7 +375,16 @@ func (s *Server) handleSubmitReportRequest(args [0]string, argsEscaped bool, w h startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -315,8 +396,27 @@ func (s *Server) handleSubmitReportRequest(args [0]string, argsEscaped bool, w h var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ diff --git a/internal/promapi/oas_handlers_gen.go b/internal/promapi/oas_handlers_gen.go index 0da78f9a..477f1b39 100644 --- a/internal/promapi/oas_handlers_gen.go +++ b/internal/promapi/oas_handlers_gen.go @@ -20,10 +20,22 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleGetLabelValuesRequest handles getLabelValues operation. // // GET /api/v1/label/{label}/values func (s *Server) handleGetLabelValuesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getLabelValues"), semconv.HTTPRequestMethodKey.String("GET"), @@ -45,7 +57,16 @@ func (s *Server) handleGetLabelValuesRequest(args [1]string, argsEscaped bool, w startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -57,8 +78,27 @@ func (s *Server) handleGetLabelValuesRequest(args [1]string, argsEscaped bool, w var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -161,6 +201,8 @@ func (s *Server) handleGetLabelValuesRequest(args [1]string, argsEscaped bool, w // // GET /api/v1/labels func (s *Server) handleGetLabelsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getLabels"), semconv.HTTPRequestMethodKey.String("GET"), @@ -182,7 +224,16 @@ func (s *Server) handleGetLabelsRequest(args [0]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -194,8 +245,27 @@ func (s *Server) handleGetLabelsRequest(args [0]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -294,6 +364,8 @@ func (s *Server) handleGetLabelsRequest(args [0]string, argsEscaped bool, w http // // GET /api/v1/metadata func (s *Server) handleGetMetadataRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getMetadata"), semconv.HTTPRequestMethodKey.String("GET"), @@ -315,7 +387,16 @@ func (s *Server) handleGetMetadataRequest(args [0]string, argsEscaped bool, w ht startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -327,8 +408,27 @@ func (s *Server) handleGetMetadataRequest(args [0]string, argsEscaped bool, w ht var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -425,6 +525,8 @@ func (s *Server) handleGetMetadataRequest(args [0]string, argsEscaped bool, w ht // // GET /api/v1/query func (s *Server) handleGetQueryRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getQuery"), semconv.HTTPRequestMethodKey.String("GET"), @@ -446,7 +548,16 @@ func (s *Server) handleGetQueryRequest(args [0]string, argsEscaped bool, w http. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -458,8 +569,27 @@ func (s *Server) handleGetQueryRequest(args [0]string, argsEscaped bool, w http. var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -560,6 +690,8 @@ func (s *Server) handleGetQueryRequest(args [0]string, argsEscaped bool, w http. // // GET /api/v1/query_exemplars func (s *Server) handleGetQueryExemplarsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getQueryExemplars"), semconv.HTTPRequestMethodKey.String("GET"), @@ -581,7 +713,16 @@ func (s *Server) handleGetQueryExemplarsRequest(args [0]string, argsEscaped bool startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -593,8 +734,27 @@ func (s *Server) handleGetQueryExemplarsRequest(args [0]string, argsEscaped bool var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -691,6 +851,8 @@ func (s *Server) handleGetQueryExemplarsRequest(args [0]string, argsEscaped bool // // GET /api/v1/query_range func (s *Server) handleGetQueryRangeRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getQueryRange"), semconv.HTTPRequestMethodKey.String("GET"), @@ -712,7 +874,16 @@ func (s *Server) handleGetQueryRangeRequest(args [0]string, argsEscaped bool, w startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -724,8 +895,27 @@ func (s *Server) handleGetQueryRangeRequest(args [0]string, argsEscaped bool, w var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -832,6 +1022,8 @@ func (s *Server) handleGetQueryRangeRequest(args [0]string, argsEscaped bool, w // // GET /api/v1/rules func (s *Server) handleGetRulesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getRules"), semconv.HTTPRequestMethodKey.String("GET"), @@ -853,7 +1045,16 @@ func (s *Server) handleGetRulesRequest(args [0]string, argsEscaped bool, w http. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -865,8 +1066,27 @@ func (s *Server) handleGetRulesRequest(args [0]string, argsEscaped bool, w http. var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -967,6 +1187,8 @@ func (s *Server) handleGetRulesRequest(args [0]string, argsEscaped bool, w http. // // GET /api/v1/series func (s *Server) handleGetSeriesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getSeries"), semconv.HTTPRequestMethodKey.String("GET"), @@ -988,7 +1210,16 @@ func (s *Server) handleGetSeriesRequest(args [0]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1000,8 +1231,27 @@ func (s *Server) handleGetSeriesRequest(args [0]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -1100,6 +1350,8 @@ func (s *Server) handleGetSeriesRequest(args [0]string, argsEscaped bool, w http // // POST /api/v1/labels func (s *Server) handlePostLabelsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("postLabels"), semconv.HTTPRequestMethodKey.String("POST"), @@ -1121,7 +1373,16 @@ func (s *Server) handlePostLabelsRequest(args [0]string, argsEscaped bool, w htt startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1133,8 +1394,27 @@ func (s *Server) handlePostLabelsRequest(args [0]string, argsEscaped bool, w htt var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -1223,6 +1503,8 @@ func (s *Server) handlePostLabelsRequest(args [0]string, argsEscaped bool, w htt // // POST /api/v1/query func (s *Server) handlePostQueryRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("postQuery"), semconv.HTTPRequestMethodKey.String("POST"), @@ -1244,7 +1526,16 @@ func (s *Server) handlePostQueryRequest(args [0]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1256,8 +1547,27 @@ func (s *Server) handlePostQueryRequest(args [0]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -1346,6 +1656,8 @@ func (s *Server) handlePostQueryRequest(args [0]string, argsEscaped bool, w http // // POST /api/v1/query_exemplars func (s *Server) handlePostQueryExemplarsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("postQueryExemplars"), semconv.HTTPRequestMethodKey.String("POST"), @@ -1367,7 +1679,16 @@ func (s *Server) handlePostQueryExemplarsRequest(args [0]string, argsEscaped boo startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1379,8 +1700,27 @@ func (s *Server) handlePostQueryExemplarsRequest(args [0]string, argsEscaped boo var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -1469,6 +1809,8 @@ func (s *Server) handlePostQueryExemplarsRequest(args [0]string, argsEscaped boo // // POST /api/v1/query_range func (s *Server) handlePostQueryRangeRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("postQueryRange"), semconv.HTTPRequestMethodKey.String("POST"), @@ -1490,7 +1832,16 @@ func (s *Server) handlePostQueryRangeRequest(args [0]string, argsEscaped bool, w startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1502,8 +1853,27 @@ func (s *Server) handlePostQueryRangeRequest(args [0]string, argsEscaped bool, w var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -1592,6 +1962,8 @@ func (s *Server) handlePostQueryRangeRequest(args [0]string, argsEscaped bool, w // // POST /api/v1/series func (s *Server) handlePostSeriesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("postSeries"), semconv.HTTPRequestMethodKey.String("POST"), @@ -1613,7 +1985,16 @@ func (s *Server) handlePostSeriesRequest(args [0]string, argsEscaped bool, w htt startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -1625,8 +2006,27 @@ func (s *Server) handlePostSeriesRequest(args [0]string, argsEscaped bool, w htt var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ diff --git a/internal/pyroscopeapi/oas_handlers_gen.go b/internal/pyroscopeapi/oas_handlers_gen.go index 616ca2cc..e521caac 100644 --- a/internal/pyroscopeapi/oas_handlers_gen.go +++ b/internal/pyroscopeapi/oas_handlers_gen.go @@ -20,6 +20,16 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleGetAppsRequest handles getApps operation. // // Returns list of application metadata. @@ -27,6 +37,8 @@ import ( // // GET /api/apps func (s *Server) handleGetAppsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("getApps"), semconv.HTTPRequestMethodKey.String("GET"), @@ -48,7 +60,16 @@ func (s *Server) handleGetAppsRequest(args [0]string, argsEscaped bool, w http.R startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -60,8 +81,27 @@ func (s *Server) handleGetAppsRequest(args [0]string, argsEscaped bool, w http.R var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) @@ -131,6 +171,8 @@ func (s *Server) handleGetAppsRequest(args [0]string, argsEscaped bool, w http.R // // POST /ingest func (s *Server) handleIngestRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("ingest"), semconv.HTTPRequestMethodKey.String("POST"), @@ -152,7 +194,16 @@ func (s *Server) handleIngestRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -164,8 +215,27 @@ func (s *Server) handleIngestRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -297,6 +367,8 @@ func (s *Server) handleIngestRequest(args [0]string, argsEscaped bool, w http.Re // // GET /label-values func (s *Server) handleLabelValuesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("labelValues"), semconv.HTTPRequestMethodKey.String("GET"), @@ -318,7 +390,16 @@ func (s *Server) handleLabelValuesRequest(args [0]string, argsEscaped bool, w ht startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -330,8 +411,27 @@ func (s *Server) handleLabelValuesRequest(args [0]string, argsEscaped bool, w ht var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -432,6 +532,8 @@ func (s *Server) handleLabelValuesRequest(args [0]string, argsEscaped bool, w ht // // GET /labels func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("labels"), semconv.HTTPRequestMethodKey.String("GET"), @@ -453,7 +555,16 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -465,8 +576,27 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -564,6 +694,8 @@ func (s *Server) handleLabelsRequest(args [0]string, argsEscaped bool, w http.Re // // GET /render func (s *Server) handleRenderRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("render"), semconv.HTTPRequestMethodKey.String("GET"), @@ -585,7 +717,16 @@ func (s *Server) handleRenderRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -597,8 +738,27 @@ func (s *Server) handleRenderRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ diff --git a/internal/sentryapi/oas_handlers_gen.go b/internal/sentryapi/oas_handlers_gen.go index 7061362a..c136eb26 100644 --- a/internal/sentryapi/oas_handlers_gen.go +++ b/internal/sentryapi/oas_handlers_gen.go @@ -20,12 +20,24 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleDummyRequest handles dummy operation. // // Dummy endpoint to generate schema definitions. // // GET /dummy func (s *Server) handleDummyRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("dummy"), semconv.HTTPRequestMethodKey.String("GET"), @@ -47,7 +59,16 @@ func (s *Server) handleDummyRequest(args [0]string, argsEscaped bool, w http.Res startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -59,8 +80,27 @@ func (s *Server) handleDummyRequest(args [0]string, argsEscaped bool, w http.Res var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) @@ -130,6 +170,8 @@ func (s *Server) handleDummyRequest(args [0]string, argsEscaped bool, w http.Res // // POST /envelope func (s *Server) handleEnvelopeRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("envelope"), semconv.HTTPRequestMethodKey.String("POST"), @@ -151,7 +193,16 @@ func (s *Server) handleEnvelopeRequest(args [0]string, argsEscaped bool, w http. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -163,8 +214,27 @@ func (s *Server) handleEnvelopeRequest(args [0]string, argsEscaped bool, w http. var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ diff --git a/internal/tempoapi/oas_handlers_gen.go b/internal/tempoapi/oas_handlers_gen.go index 9367a6de..cbecc6e9 100644 --- a/internal/tempoapi/oas_handlers_gen.go +++ b/internal/tempoapi/oas_handlers_gen.go @@ -20,6 +20,16 @@ import ( "github.com/ogen-go/ogen/otelogen" ) +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + // handleBuildInfoRequest handles buildInfo operation. // // Returns Tempo buildinfo, in the same format as Prometheus `/api/v1/status/buildinfo`. @@ -27,6 +37,8 @@ import ( // // GET /api/status/buildinfo func (s *Server) handleBuildInfoRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("buildInfo"), semconv.HTTPRequestMethodKey.String("GET"), @@ -48,7 +60,16 @@ func (s *Server) handleBuildInfoRequest(args [0]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -60,8 +81,27 @@ func (s *Server) handleBuildInfoRequest(args [0]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) @@ -131,6 +171,8 @@ func (s *Server) handleBuildInfoRequest(args [0]string, argsEscaped bool, w http // // GET /api/echo func (s *Server) handleEchoRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("echo"), semconv.HTTPRequestMethodKey.String("GET"), @@ -152,7 +194,16 @@ func (s *Server) handleEchoRequest(args [0]string, argsEscaped bool, w http.Resp startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -164,8 +215,27 @@ func (s *Server) handleEchoRequest(args [0]string, argsEscaped bool, w http.Resp var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error ) @@ -235,6 +305,8 @@ func (s *Server) handleEchoRequest(args [0]string, argsEscaped bool, w http.Resp // // GET /api/search func (s *Server) handleSearchRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("search"), semconv.HTTPRequestMethodKey.String("GET"), @@ -256,7 +328,16 @@ func (s *Server) handleSearchRequest(args [0]string, argsEscaped bool, w http.Re startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -268,8 +349,27 @@ func (s *Server) handleSearchRequest(args [0]string, argsEscaped bool, w http.Re var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -386,6 +486,8 @@ func (s *Server) handleSearchRequest(args [0]string, argsEscaped bool, w http.Re // // GET /api/search/tag/{tag_name}/values func (s *Server) handleSearchTagValuesRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("searchTagValues"), semconv.HTTPRequestMethodKey.String("GET"), @@ -407,7 +509,16 @@ func (s *Server) handleSearchTagValuesRequest(args [1]string, argsEscaped bool, startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -419,8 +530,27 @@ func (s *Server) handleSearchTagValuesRequest(args [1]string, argsEscaped bool, var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -522,6 +652,8 @@ func (s *Server) handleSearchTagValuesRequest(args [1]string, argsEscaped bool, // // GET /api/v2/search/tag/{attribute_selector}/values func (s *Server) handleSearchTagValuesV2Request(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("searchTagValuesV2"), semconv.HTTPRequestMethodKey.String("GET"), @@ -543,7 +675,16 @@ func (s *Server) handleSearchTagValuesV2Request(args [1]string, argsEscaped bool startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -555,8 +696,27 @@ func (s *Server) handleSearchTagValuesV2Request(args [1]string, argsEscaped bool var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -657,6 +817,8 @@ func (s *Server) handleSearchTagValuesV2Request(args [1]string, argsEscaped bool // // GET /api/search/tags func (s *Server) handleSearchTagsRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("searchTags"), semconv.HTTPRequestMethodKey.String("GET"), @@ -678,7 +840,16 @@ func (s *Server) handleSearchTagsRequest(args [0]string, argsEscaped bool, w htt startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -690,8 +861,27 @@ func (s *Server) handleSearchTagsRequest(args [0]string, argsEscaped bool, w htt var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -788,6 +978,8 @@ func (s *Server) handleSearchTagsRequest(args [0]string, argsEscaped bool, w htt // // GET /api/v2/search/tags func (s *Server) handleSearchTagsV2Request(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("searchTagsV2"), semconv.HTTPRequestMethodKey.String("GET"), @@ -809,7 +1001,16 @@ func (s *Server) handleSearchTagsV2Request(args [0]string, argsEscaped bool, w h startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -821,8 +1022,27 @@ func (s *Server) handleSearchTagsV2Request(args [0]string, argsEscaped bool, w h var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ @@ -919,6 +1139,8 @@ func (s *Server) handleSearchTagsV2Request(args [0]string, argsEscaped bool, w h // // GET /api/traces/{traceID} func (s *Server) handleTraceByIDRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("traceByID"), semconv.HTTPRequestMethodKey.String("GET"), @@ -940,7 +1162,16 @@ func (s *Server) handleTraceByIDRequest(args [1]string, argsEscaped bool, w http startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) - attrOpt := metric.WithAttributeSet(labeler.AttributeSet()) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) @@ -952,8 +1183,27 @@ func (s *Server) handleTraceByIDRequest(args [1]string, argsEscaped bool, w http var ( recordError = func(stage string, err error) { span.RecordError(err) - span.SetStatus(codes.Error, stage) - s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + setStatus := true + code := statusWriter.status + if code >= 100 && code < 400 { + setStatus = false + } + if setStatus { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{