Files
go-otel/internal/sourcemaps/store.go
T
mats 21f04d696b
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 58s
more otel
2026-09-18 20:51:37 +00:00

107 lines
2.5 KiB
Go

package sourcemaps
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
sm "github.com/go-sourcemap/sourcemap"
)
var frameRE = regexp.MustCompile(`(.*?)(https?://[^ )]+|[^ )]+):(\d+):(\d+)(\)?\s*)$`)
type Resolver struct {
dir string
mu sync.RWMutex
maps map[string]*sm.Consumer
}
func New(dir string) (*Resolver, error) {
if dir == "" {
dir = "./sourcemaps"
}
if err := os.MkdirAll(dir, 0750); err != nil {
return nil, err
}
return &Resolver{dir: dir, maps: make(map[string]*sm.Consumer)}, nil
}
func key(release, source string) string { return release + "\x00" + normalize(source) }
func normalize(source string) string {
source = strings.Split(source, "?")[0]
if i := strings.Index(source, "/assets/"); i >= 0 {
return source[i:]
}
return strings.TrimPrefix(source, "./")
}
func (r *Resolver) Upload(release, source string, body io.Reader) error {
if strings.TrimSpace(release) == "" || strings.TrimSpace(source) == "" {
return fmt.Errorf("release and source are required")
}
b, err := io.ReadAll(io.LimitReader(body, 50<<20))
if err != nil {
return err
}
consumer, err := sm.Parse(source+".map", b)
if err != nil {
return fmt.Errorf("invalid source map: %w", err)
}
name := filepath.Join(r.dir, safe(release)+"-"+safe(normalize(source))+".map")
if err := os.WriteFile(name, b, 0600); err != nil {
return err
}
r.mu.Lock()
r.maps[key(release, source)] = consumer
r.mu.Unlock()
return nil
}
func safe(s string) string {
s = strings.ReplaceAll(s, "/", "_")
s = strings.ReplaceAll(s, "\\", "_")
return strings.Map(func(r rune) rune {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || strings.ContainsRune("._-", r) {
return r
}
return '_'
}, s)
}
func (r *Resolver) ResolveStack(release, stack string) string {
if release == "" || stack == "" {
return stack
}
r.mu.RLock()
defer r.mu.RUnlock()
lines := strings.Split(stack, "\n")
for i, line := range lines {
m := frameRE.FindStringSubmatch(line)
if m == nil {
continue
}
genLine, _ := strconv.Atoi(m[3])
genCol, _ := strconv.Atoi(m[4])
consumer := r.maps[key(release, m[2])]
if consumer == nil {
continue
}
source, name, srcLine, srcCol, ok := consumer.Source(genLine, genCol)
if !ok {
continue
}
label := source
if name != "" {
label += " (" + name + ")"
}
lines[i] = fmt.Sprintf("%s%s:%d:%d%s [mapped from %s:%d:%d]", m[1], label, srcLine, srcCol, m[5], m[2], genLine, genCol)
}
return strings.Join(lines, "\n")
}