mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-28 22:06:22 +02:00
Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates
This commit is contained in:
parent
694e32be26
commit
41cbfd6e0a
178 changed files with 36008 additions and 399 deletions
83
internal/infrastructure/tracing/middleware.go
Normal file
83
internal/infrastructure/tracing/middleware.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// HTTPMiddleware creates spans for each HTTP request.
|
||||
// Extracts trace context from incoming headers and sets span attributes.
|
||||
func HTTPMiddleware(next http.Handler) http.Handler {
|
||||
tracer := otel.Tracer("sentinel-soc/http")
|
||||
propagator := otel.GetTextMapPropagator()
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract trace context from incoming headers.
|
||||
ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||
|
||||
spanName := fmt.Sprintf("%s %s", r.Method, r.URL.Path)
|
||||
ctx, span := tracer.Start(ctx, spanName,
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
trace.WithAttributes(
|
||||
attribute.String("http.method", r.Method),
|
||||
attribute.String("http.url", r.URL.String()),
|
||||
attribute.String("http.target", r.URL.Path),
|
||||
attribute.String("http.user_agent", r.UserAgent()),
|
||||
attribute.String("net.host.name", r.Host),
|
||||
),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// Wrap response writer to capture status code.
|
||||
sw := &statusWriter{ResponseWriter: w, status: http.StatusOK}
|
||||
next.ServeHTTP(sw, r.WithContext(ctx))
|
||||
|
||||
span.SetAttributes(
|
||||
attribute.Int("http.status_code", sw.status),
|
||||
)
|
||||
if sw.status >= 400 {
|
||||
span.SetAttributes(attribute.Bool("error", true))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// statusWriter captures the HTTP status code for span attributes.
|
||||
// Implements http.Flusher to support SSE/streaming through middleware chain.
|
||||
type statusWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (sw *statusWriter) WriteHeader(code int) {
|
||||
if !sw.wroteHeader {
|
||||
sw.status = code
|
||||
sw.wroteHeader = true
|
||||
}
|
||||
sw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (sw *statusWriter) Write(b []byte) (int, error) {
|
||||
if !sw.wroteHeader {
|
||||
sw.wroteHeader = true
|
||||
}
|
||||
return sw.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
// Flush delegates to the underlying ResponseWriter if it supports http.Flusher.
|
||||
// Required for SSE streaming endpoints to work through the middleware chain.
|
||||
func (sw *statusWriter) Flush() {
|
||||
if f, ok := sw.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying ResponseWriter for Go 1.20+ ResponseController.
|
||||
func (sw *statusWriter) Unwrap() http.ResponseWriter {
|
||||
return sw.ResponseWriter
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue