diff --git a/charts/core/Chart.yaml b/charts/core/Chart.yaml index 46abd58..b54306a 100644 --- a/charts/core/Chart.yaml +++ b/charts/core/Chart.yaml @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn Core type: application -version: v0.1.61 -appVersion: v0.1.61 +version: v0.1.62 +appVersion: v0.1.62 icon: https://assets.unikorn-cloud.org/images/logos/dark-on-light/icon.svg diff --git a/pkg/server/middleware/audit/logging.go b/pkg/server/middleware/audit/logging.go index 6ff1e15..98c1c98 100644 --- a/pkg/server/middleware/audit/logging.go +++ b/pkg/server/middleware/audit/logging.go @@ -17,6 +17,7 @@ limitations under the License. package audit import ( + "encoding/json" "net/http" "regexp" "strings" @@ -59,24 +60,41 @@ func New(next http.Handler, openapi *openapi.Schema, application, version string } // getResource will resolve to a resource type. -func getResource(route *routers.Route, params map[string]string) *Resource { - // We are looking for "/.../resource/{idParameter}" - // or failing that "/.../resource" - matches := regexp.MustCompile(`/([^/]+)/{([^/}]+)}$`).FindStringSubmatch(route.Path) - if matches == nil { +func getResource(w *middleware.LoggingResponseWriter, r *http.Request, route *routers.Route, params map[string]string) *Resource { + // Creates rely on the response containing the resource ID in the response metadata. + if r.Method == http.MethodPost { + // Nothing written, possibly a bug somewhere? + if w.Body() == nil { + return nil + } + + var metadata struct { + Metadata openapi.ResourceReadMetadata `json:"metadata"` + } + + // Not a canonical API resource, possibly a bug somewhere? + if err := json.Unmarshal(w.Body().Bytes(), &metadata); err != nil { + return nil + } + segments := strings.Split(route.Path, "/") return &Resource{ Type: segments[len(segments)-1], + ID: metadata.Metadata.Id, } } - resource := &Resource{ + // Read, updates and deletes you can get the information from the route. + matches := regexp.MustCompile(`/([^/]+)/{([^/}]+)}$`).FindStringSubmatch(route.Path) + if matches == nil { + return nil + } + + return &Resource{ Type: matches[1], ID: params[matches[2]], } - - return resource } // ServeHTTP implements the http.Handler interface. @@ -112,6 +130,12 @@ func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + // If you cannot derive the resource, then discard. + resource := getResource(writer, r, route, params) + if resource == nil { + return + } + logParams := []any{ "component", &Component{ Name: l.application, @@ -124,7 +148,7 @@ func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) { Verb: r.Method, }, "scope", params, - "resource", getResource(route, params), + "resource", resource, "result", &Result{ Status: writer.StatusCode(), }, diff --git a/pkg/server/middleware/opentelemetry/opentelemetry.go b/pkg/server/middleware/opentelemetry/opentelemetry.go index 5b4941b..6eeb8b7 100644 --- a/pkg/server/middleware/opentelemetry/opentelemetry.go +++ b/pkg/server/middleware/opentelemetry/opentelemetry.go @@ -202,10 +202,16 @@ func httpRequestAttributes(r *http.Request) []attribute.KeyValue { } func httpResponseAttributes(w *middleware.LoggingResponseWriter) []attribute.KeyValue { + var bodySize int + + if body := w.Body(); body != nil { + bodySize = body.Len() + } + var attr []attribute.KeyValue attr = append(attr, semconv.HTTPResponseStatusCode(w.StatusCode())) - attr = append(attr, semconv.HTTPResponseBodySize(w.ContentLength())) + attr = append(attr, semconv.HTTPResponseBodySize(bodySize)) attr = append(attr, httpHeaderAttributes(w.Header(), "http.response.header")...) return attr diff --git a/pkg/server/middleware/types.go b/pkg/server/middleware/types.go index 7dfeffe..9ee849b 100644 --- a/pkg/server/middleware/types.go +++ b/pkg/server/middleware/types.go @@ -17,15 +17,16 @@ limitations under the License. package middleware import ( + "bytes" "net/http" ) // LoggingResponseWriter is the ubiquitous reimplementation of a response // writer that allows access to the HTTP status code in middleware. type LoggingResponseWriter struct { - next http.ResponseWriter - code int - contentLength int + next http.ResponseWriter + code int + body *bytes.Buffer } func NewLoggingResponseWriter(next http.ResponseWriter) *LoggingResponseWriter { @@ -42,7 +43,11 @@ func (w *LoggingResponseWriter) Header() http.Header { } func (w *LoggingResponseWriter) Write(body []byte) (int, error) { - w.contentLength += len(body) + if w.body == nil { + w.body = &bytes.Buffer{} + } + + w.body.Write(body) return w.next.Write(body) } @@ -60,6 +65,6 @@ func (w *LoggingResponseWriter) StatusCode() int { return w.code } -func (w *LoggingResponseWriter) ContentLength() int { - return w.contentLength +func (w *LoggingResponseWriter) Body() *bytes.Buffer { + return w.body }