diff --git a/api/go.mod b/api/go.mod index 17c6d774b1..acaa99e0bc 100644 --- a/api/go.mod +++ b/api/go.mod @@ -28,7 +28,7 @@ require ( github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.24 github.com/mitchellh/mapstructure v1.5.0 - github.com/ogen-go/ogen v1.4.1 + github.com/ogen-go/ogen v1.9.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.5 github.com/rs/cors v1.11.1 @@ -100,7 +100,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect diff --git a/api/go.sum b/api/go.sum index b4af41dfe7..bf114f040b 100644 --- a/api/go.sum +++ b/api/go.sum @@ -144,8 +144,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -361,8 +361,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/ogen-go/ogen v1.4.1 h1:uVZWKw80eZaMQu7LpTJxpHqwu6oMqYuh8ElqP1Mf2bI= -github.com/ogen-go/ogen v1.4.1/go.mod h1:YeliH7gAS6QToqDqIM5BrnEUOiXiqCnNqNwzEpebDsY= +github.com/ogen-go/ogen v1.9.0 h1:n+lDQpiSFYC9G4hTvuNVWnqmIP0LR8ws0faDn9jX3hU= +github.com/ogen-go/ogen v1.9.0/go.mod h1:vkHpuRyzjdfuRCy81EShi4t9sIgZDcNPGmiDKipRloc= github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= diff --git a/api/pkg/ent/ogent/oas_client_gen.go b/api/pkg/ent/ogent/oas_client_gen.go index 7538684413..24a608c653 100644 --- a/api/pkg/ent/ogent/oas_client_gen.go +++ b/api/pkg/ent/ogent/oas_client_gen.go @@ -21,6 +21,11 @@ import ( "github.com/ogen-go/ogen/uri" ) +func trimTrailingSlashes(u *url.URL) { + u.Path = strings.TrimRight(u.Path, "/") + u.RawPath = strings.TrimRight(u.RawPath, "/") +} + // Invoker invokes operations described by OpenAPI v3 specification. type Invoker interface { // DeleteAppConfig invokes deleteAppConfig operation. @@ -63,11 +68,6 @@ var _ Handler = struct { *Client }{} -func trimTrailingSlashes(u *url.URL) { - u.Path = strings.TrimRight(u.Path, "/") - u.RawPath = strings.TrimRight(u.RawPath, "/") -} - // NewClient initializes new Client defined by OAS. func NewClient(serverURL string, opts ...ClientOption) (*Client, error) { u, err := url.Parse(serverURL) @@ -123,14 +123,14 @@ func (c *Client) sendDeleteAppConfig(ctx context.Context, params DeleteAppConfig defer func() { // Use floating point division here for higher precision (instead of Millisecond method). elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), metric.WithAttributes(otelAttrs...)) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) }() // Increment request counter. c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, "DeleteAppConfig", + ctx, span := c.cfg.Tracer.Start(ctx, DeleteAppConfigOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -354,14 +354,14 @@ func (c *Client) sendHealth(ctx context.Context) (res HealthRes, err error) { defer func() { // Use floating point division here for higher precision (instead of Millisecond method). elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), metric.WithAttributes(otelAttrs...)) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) }() // Increment request counter. c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, "Health", + ctx, span := c.cfg.Tracer.Start(ctx, HealthOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -424,14 +424,14 @@ func (c *Client) sendListAppConfig(ctx context.Context, params ListAppConfigPara defer func() { // Use floating point division here for higher precision (instead of Millisecond method). elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), metric.WithAttributes(otelAttrs...)) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) }() // Increment request counter. c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, "ListAppConfig", + ctx, span := c.cfg.Tracer.Start(ctx, ListAppConfigOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -671,14 +671,14 @@ func (c *Client) sendReadAppConfig(ctx context.Context, params ReadAppConfigPara defer func() { // Use floating point division here for higher precision (instead of Millisecond method). elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), metric.WithAttributes(otelAttrs...)) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) }() // Increment request counter. c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, "ReadAppConfig", + ctx, span := c.cfg.Tracer.Start(ctx, ReadAppConfigOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) @@ -902,14 +902,14 @@ func (c *Client) sendSetAppConfig(ctx context.Context, request *SetAppConfigReq, defer func() { // Use floating point division here for higher precision (instead of Millisecond method). elapsedDuration := time.Since(startTime) - c.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), metric.WithAttributes(otelAttrs...)) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) }() // Increment request counter. c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) // Start a span for this request. - ctx, span := c.cfg.Tracer.Start(ctx, "SetAppConfig", + ctx, span := c.cfg.Tracer.Start(ctx, SetAppConfigOperation, trace.WithAttributes(otelAttrs...), clientSpanKind, ) diff --git a/api/pkg/ent/ogent/oas_handlers_gen.go b/api/pkg/ent/ogent/oas_handlers_gen.go index ae0cc0b743..22456d05ff 100644 --- a/api/pkg/ent/ogent/oas_handlers_gen.go +++ b/api/pkg/ent/ogent/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) +} + // handleDeleteAppConfigRequest handles deleteAppConfig operation. // // Deletes the AppConfig with the requested Key. // // DELETE /app-configs/{key} func (s *Server) handleDeleteAppConfigRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("deleteAppConfig"), semconv.HTTPRequestMethodKey.String("DELETE"), @@ -33,7 +45,7 @@ func (s *Server) handleDeleteAppConfigRequest(args [1]string, argsEscaped bool, } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), "DeleteAppConfig", + ctx, span := s.cfg.Tracer.Start(r.Context(), DeleteAppConfigOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -47,24 +59,48 @@ func (s *Server) handleDeleteAppConfigRequest(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) // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), attrOpt) + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() 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. + code := statusWriter.status + if code >= 100 && code < 500 { + 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{ - Name: "DeleteAppConfig", + Name: DeleteAppConfigOperation, ID: "deleteAppConfig", } ) @@ -83,7 +119,7 @@ func (s *Server) handleDeleteAppConfigRequest(args [1]string, argsEscaped bool, if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: "DeleteAppConfig", + OperationName: DeleteAppConfigOperation, OperationSummary: "", OperationID: "deleteAppConfig", Body: nil, @@ -178,6 +214,8 @@ func (s *Server) handleDeleteAppConfigRequest(args [1]string, argsEscaped bool, // // GET /health func (s *Server) handleHealthRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("Health"), semconv.HTTPRequestMethodKey.String("GET"), @@ -185,7 +223,7 @@ func (s *Server) handleHealthRequest(args [0]string, argsEscaped bool, w http.Re } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), "Health", + ctx, span := s.cfg.Tracer.Start(r.Context(), HealthOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -199,20 +237,44 @@ func (s *Server) handleHealthRequest(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) // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), attrOpt) + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() 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. + code := statusWriter.status + if code >= 100 && code < 500 { + 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 ) @@ -221,7 +283,7 @@ func (s *Server) handleHealthRequest(args [0]string, argsEscaped bool, w http.Re if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: "Health", + OperationName: HealthOperation, OperationSummary: "Simple endpoint to check if the server is up", OperationID: "Health", Body: nil, @@ -269,6 +331,8 @@ func (s *Server) handleHealthRequest(args [0]string, argsEscaped bool, w http.Re // // GET /app-configs func (s *Server) handleListAppConfigRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("listAppConfig"), semconv.HTTPRequestMethodKey.String("GET"), @@ -276,7 +340,7 @@ func (s *Server) handleListAppConfigRequest(args [0]string, argsEscaped bool, w } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), "ListAppConfig", + ctx, span := s.cfg.Tracer.Start(r.Context(), ListAppConfigOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -290,24 +354,48 @@ func (s *Server) handleListAppConfigRequest(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) // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), attrOpt) + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() 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. + code := statusWriter.status + if code >= 100 && code < 500 { + 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{ - Name: "ListAppConfig", + Name: ListAppConfigOperation, ID: "listAppConfig", } ) @@ -326,7 +414,7 @@ func (s *Server) handleListAppConfigRequest(args [0]string, argsEscaped bool, w if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: "ListAppConfig", + OperationName: ListAppConfigOperation, OperationSummary: "", OperationID: "listAppConfig", Body: nil, @@ -425,6 +513,8 @@ func (s *Server) handleListAppConfigRequest(args [0]string, argsEscaped bool, w // // GET /app-configs/{key} func (s *Server) handleReadAppConfigRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("readAppConfig"), semconv.HTTPRequestMethodKey.String("GET"), @@ -432,7 +522,7 @@ func (s *Server) handleReadAppConfigRequest(args [1]string, argsEscaped bool, w } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), "ReadAppConfig", + ctx, span := s.cfg.Tracer.Start(r.Context(), ReadAppConfigOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -446,24 +536,48 @@ func (s *Server) handleReadAppConfigRequest(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) // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), attrOpt) + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() 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. + code := statusWriter.status + if code >= 100 && code < 500 { + 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{ - Name: "ReadAppConfig", + Name: ReadAppConfigOperation, ID: "readAppConfig", } ) @@ -482,7 +596,7 @@ func (s *Server) handleReadAppConfigRequest(args [1]string, argsEscaped bool, w if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: "ReadAppConfig", + OperationName: ReadAppConfigOperation, OperationSummary: "", OperationID: "readAppConfig", Body: nil, @@ -577,6 +691,8 @@ func (s *Server) handleReadAppConfigRequest(args [1]string, argsEscaped bool, w // // POST /app-configs func (s *Server) handleSetAppConfigRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter otelAttrs := []attribute.KeyValue{ otelogen.OperationID("setAppConfig"), semconv.HTTPRequestMethodKey.String("POST"), @@ -584,7 +700,7 @@ func (s *Server) handleSetAppConfigRequest(args [0]string, argsEscaped bool, w h } // Start a span for this request. - ctx, span := s.cfg.Tracer.Start(r.Context(), "SetAppConfig", + ctx, span := s.cfg.Tracer.Start(r.Context(), SetAppConfigOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) @@ -598,24 +714,48 @@ func (s *Server) handleSetAppConfigRequest(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) // Use floating point division here for higher precision (instead of Millisecond method). - s.duration.Record(ctx, float64(float64(elapsedDuration)/float64(time.Millisecond)), attrOpt) + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() 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. + code := statusWriter.status + if code >= 100 && code < 500 { + 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{ - Name: "SetAppConfig", + Name: SetAppConfigOperation, ID: "setAppConfig", } ) @@ -649,7 +789,7 @@ func (s *Server) handleSetAppConfigRequest(args [0]string, argsEscaped bool, w h if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, - OperationName: "SetAppConfig", + OperationName: SetAppConfigOperation, OperationSummary: "", OperationID: "setAppConfig", Body: request, diff --git a/api/pkg/ent/ogent/oas_operations_gen.go b/api/pkg/ent/ogent/oas_operations_gen.go new file mode 100644 index 0000000000..52e79b304a --- /dev/null +++ b/api/pkg/ent/ogent/oas_operations_gen.go @@ -0,0 +1,14 @@ +// Code generated by ogen, DO NOT EDIT. + +package ogent + +// OperationName is the ogen operation name +type OperationName = string + +const ( + DeleteAppConfigOperation OperationName = "DeleteAppConfig" + HealthOperation OperationName = "Health" + ListAppConfigOperation OperationName = "ListAppConfig" + ReadAppConfigOperation OperationName = "ReadAppConfig" + SetAppConfigOperation OperationName = "SetAppConfig" +) diff --git a/api/pkg/ent/ogent/oas_router_gen.go b/api/pkg/ent/ogent/oas_router_gen.go index 7879e681eb..8273c76452 100644 --- a/api/pkg/ent/ogent/oas_router_gen.go +++ b/api/pkg/ent/ogent/oas_router_gen.go @@ -244,7 +244,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { if len(elem) == 0 { switch method { case "GET": - r.name = "ListAppConfig" + r.name = ListAppConfigOperation r.summary = "" r.operationID = "listAppConfig" r.pathPattern = "/app-configs" @@ -252,7 +252,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { r.count = 0 return r, true case "POST": - r.name = "SetAppConfig" + r.name = SetAppConfigOperation r.summary = "" r.operationID = "setAppConfig" r.pathPattern = "/app-configs" @@ -281,7 +281,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { // Leaf node. switch method { case "DELETE": - r.name = "DeleteAppConfig" + r.name = DeleteAppConfigOperation r.summary = "" r.operationID = "deleteAppConfig" r.pathPattern = "/app-configs/{key}" @@ -289,7 +289,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { r.count = 1 return r, true case "GET": - r.name = "ReadAppConfig" + r.name = ReadAppConfigOperation r.summary = "" r.operationID = "readAppConfig" r.pathPattern = "/app-configs/{key}" @@ -317,7 +317,7 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { // Leaf node. switch method { case "GET": - r.name = "Health" + r.name = HealthOperation r.summary = "Simple endpoint to check if the server is up" r.operationID = "Health" r.pathPattern = "/health"