alot more changes
Build and Publish / Metadata (push) Successful in 8s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 36s

This commit is contained in:
mats committed 2026-09-21 18:59:13 +02:00
1 parent 23243ddbdc
commit 0ed2935f04
40 files changed
+2889 -115

No files matched your search

+119 -1
View File
@@ -4,7 +4,9 @@ package collector
import (
"context"
"crypto/subtle"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"git.tornberg.me/go-otel/internal/sourcemaps"
@@ -20,6 +22,7 @@ import (
"git.tornberg.me/go-otel/internal/issues"
"git.tornberg.me/go-otel/internal/storage"
"git.tornberg.me/go-otel/internal/websocket"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/plog"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/ptrace"
@@ -477,32 +480,116 @@ func encodeIssueCursor(lastSeen int64, id string) string {
// HandleSourceMap accepts a JavaScript source map for a deployed release.
// Multipart fields: release, source, file.
func (c *Collector) HandleSourceMap(w http.ResponseWriter, r *http.Request) {
traceID, spanID := traceContextFromRequest(r)
logUpload := func(severity plog.SeverityNumber, body string, attrs map[string]any) {
if err := c.EmitInternalLog(r.Context(), severity, body, attrs, traceID, spanID); err != nil {
log.Printf("failed to emit sourcemap diagnostic: %v", err)
}
}
logUpload(plog.SeverityNumberInfo, "sourcemap upload received", map[string]any{
"event.name": "sourcemap.upload",
"http.route": "/api/sourcemaps",
"http.request.method": r.Method,
})
if r.Method != http.MethodPost {
logUpload(plog.SeverityNumberWarn, "sourcemap upload rejected: method not allowed", map[string]any{
"event.name": "sourcemap.upload.rejected",
"sourcemap.failure_stage": "method",
})
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
if !c.checkSourceMapKey(r) {
logUpload(plog.SeverityNumberWarn, "sourcemap upload rejected: invalid or missing key", map[string]any{
"event.name": "sourcemap.upload.rejected",
"sourcemap.failure_stage": "auth",
"client.address": clientIP(r),
})
http.Error(w, "invalid or missing sourcemap key", http.StatusUnauthorized)
return
}
if c.sourceMaps == nil {
logUpload(plog.SeverityNumberError, "sourcemap upload failed: resolver is not configured", map[string]any{
"event.name": "sourcemap.upload.failed",
"sourcemap.failure_stage": "configuration",
})
http.Error(w, "source maps are not configured", http.StatusNotImplemented)
return
}
if err := r.ParseMultipartForm(50 << 20); err != nil {
logUpload(plog.SeverityNumberError, "sourcemap upload failed: invalid multipart body", map[string]any{
"event.name": "sourcemap.upload.failed",
"sourcemap.failure_stage": "multipart",
"error.type": "multipart_parse",
})
http.Error(w, "invalid multipart body", http.StatusBadRequest)
return
}
release, source := r.FormValue("release"), r.FormValue("source")
file, _, err := r.FormFile("file")
file, fileHeader, err := r.FormFile("file")
if err != nil {
logUpload(plog.SeverityNumberError, "sourcemap upload failed: file is missing", map[string]any{
"event.name": "sourcemap.upload.failed",
"sourcemap.failure_stage": "file",
"sourcemap.release": release,
"sourcemap.source": source,
"error.type": "missing_file",
})
http.Error(w, "file is required", http.StatusBadRequest)
return
}
defer file.Close()
if err := c.sourceMaps.Upload(release, source, file); err != nil {
logUpload(plog.SeverityNumberError, "sourcemap upload failed", map[string]any{
"event.name": "sourcemap.upload.failed",
"sourcemap.failure_stage": "store",
"sourcemap.release": release,
"sourcemap.source": source,
"sourcemap.size_bytes": fileHeader.Size,
"error.type": "source_map_invalid_or_unwritable",
"error.message": err.Error(),
})
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
logUpload(plog.SeverityNumberInfo, "sourcemap upload stored", map[string]any{
"event.name": "sourcemap.upload.stored",
"sourcemap.release": release,
"sourcemap.source": source,
"sourcemap.size_bytes": fileHeader.Size,
})
w.WriteHeader(http.StatusCreated)
}
// checkSourceMapKey authenticates a source map upload against the shared
// OTEL_SOURCEMAP_KEY (X-Sourcemap-Key header). Uploads are refused while no key
// is configured so the endpoint is never open by accident.
func (c *Collector) checkSourceMapKey(r *http.Request) bool {
if c.sourceMapKey == "" {
return false
}
got := r.Header.Get("X-Sourcemap-Key")
return subtle.ConstantTimeCompare([]byte(got), []byte(c.sourceMapKey)) == 1
}
// traceContextFromRequest preserves the caller's trace linkage for internal
// logs when the upload came through an instrumented client or proxy.
func traceContextFromRequest(r *http.Request) (pcommon.TraceID, pcommon.SpanID) {
var traceID pcommon.TraceID
var spanID pcommon.SpanID
parts := strings.Split(r.Header.Get("Traceparent"), "-")
if len(parts) != 4 || len(parts[1]) != 32 || len(parts[2]) != 16 {
return traceID, spanID
}
if _, err := hex.Decode(traceID[:], []byte(parts[1])); err != nil {
return pcommon.NewTraceIDEmpty(), pcommon.NewSpanIDEmpty()
}
if _, err := hex.Decode(spanID[:], []byte(parts[2])); err != nil {
return pcommon.NewTraceIDEmpty(), pcommon.NewSpanIDEmpty()
}
return traceID, spanID
}
// HandleListSourceMaps lists the release/source pairs currently loaded, so a
// deploy can verify its uploads (and that they survived a restart).
func (c *Collector) HandleListSourceMaps(w http.ResponseWriter, r *http.Request) {
@@ -518,6 +605,18 @@ func (c *Collector) HandleListSourceMaps(w http.ResponseWriter, r *http.Request)
// uploaded source maps. Body: {"items":[{"release":"...","stacktrace":"..."}]};
// the response has one resolved stacktrace per item, in order.
func (c *Collector) HandleResolveStacks(w http.ResponseWriter, r *http.Request) {
traceID, spanID := traceContextFromRequest(r)
logResolve := func(severity plog.SeverityNumber, body string, attrs map[string]any) {
if err := c.EmitInternalLog(r.Context(), severity, body, attrs, traceID, spanID); err != nil {
log.Printf("failed to emit sourcemap diagnostic: %v", err)
}
}
logResolve(plog.SeverityNumberInfo, "sourcemap resolution received", map[string]any{
"event.name": "sourcemap.resolve",
"http.route": "/api/sourcemaps/resolve",
"http.request.method": r.Method,
"sourcemap.resolver_ready": c.sourceMaps != nil,
})
var req struct {
Items []struct {
Release string `json:"release"`
@@ -525,11 +624,17 @@ func (c *Collector) HandleResolveStacks(w http.ResponseWriter, r *http.Request)
} `json:"items"`
}
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4<<20)).Decode(&req); err != nil {
logResolve(plog.SeverityNumberError, "sourcemap resolution failed: invalid body", map[string]any{
"event.name": "sourcemap.resolve.failed",
"sourcemap.failure_stage": "decode",
"error.type": "invalid_json",
})
http.Error(w, "invalid body", http.StatusBadRequest)
return
}
out := make([]string, len(req.Items))
frames := make([][]sourcemaps.Frame, len(req.Items))
statusCounts := map[string]int{}
for i, it := range req.Items {
out[i] = it.Stacktrace
frames[i] = []sourcemaps.Frame{}
@@ -537,7 +642,20 @@ func (c *Collector) HandleResolveStacks(w http.ResponseWriter, r *http.Request)
out[i] = c.sourceMaps.ResolveStack(it.Release, it.Stacktrace)
frames[i] = c.sourceMaps.ResolveFrames(it.Release, it.Stacktrace)
}
for _, frame := range frames[i] {
statusCounts[frame.Status]++
}
}
logResolve(plog.SeverityNumberInfo, "sourcemap resolution completed", map[string]any{
"event.name": "sourcemap.resolve.completed",
"sourcemap.item_count": len(req.Items),
"sourcemap.frame_count": statusCounts["mapped"] + statusCounts["no-release"] + statusCounts["no-map"] + statusCounts["unmapped"],
"sourcemap.mapped": statusCounts["mapped"],
"sourcemap.no_release": statusCounts["no-release"],
"sourcemap.no_map": statusCounts["no-map"],
"sourcemap.unmapped": statusCounts["unmapped"],
"sourcemap.resolver_ready": c.sourceMaps != nil,
})
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"resolved": out, "frames": frames})
}