github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/structs/flamebearer/html.go (about) 1 package flamebearer 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "regexp" 10 "text/template" 11 12 "github.com/pyroscope-io/pyroscope/pkg/build" 13 ) 14 15 // TODO(kolesnikovae): Refactor to ./convert? 16 17 // FlamebearerToStandaloneHTML converts and writes a flamebearer into HTML 18 // TODO cache template creation and whatnot? 19 func FlamebearerToStandaloneHTML(fb *FlamebearerProfile, dir http.FileSystem, w io.Writer) error { 20 tmpl, err := getTemplate(dir, "/standalone.html") 21 if err != nil { 22 return fmt.Errorf("unable to get template: %w", err) 23 } 24 var flamegraph []byte 25 flamegraph, err = json.Marshal(fb) 26 if err != nil { 27 return fmt.Errorf("unable to marshal flameberarer profile: %w", err) 28 } 29 30 scriptTpl, err := template.New("standalone").Parse( 31 ` 32 <script type="text/javascript"> 33 window.flamegraph = {{ .Flamegraph }} 34 window.buildInfo = {{ .BuildInfo }}; 35 </script>`, 36 ) 37 if err != nil { 38 return fmt.Errorf("unable to create template: %w", err) 39 } 40 41 var buffer bytes.Buffer 42 err = scriptTpl.Execute(&buffer, map[string]string{ 43 "Flamegraph": string(flamegraph), 44 "BuildInfo": string(build.JSON()), 45 }) 46 if err != nil { 47 return fmt.Errorf("unable to execute template: %w", err) 48 } 49 50 standaloneFlamegraphRegexp := regexp.MustCompile(`(?s)<!--\s*generate-standalone-flamegraph\s*-->`) 51 newContent := standaloneFlamegraphRegexp.ReplaceAll(tmpl, buffer.Bytes()) 52 if bytes.Equal(tmpl, newContent) { 53 return fmt.Errorf("script tag could not be applied") 54 } 55 56 _, err = w.Write(newContent) 57 if err != nil { 58 return fmt.Errorf("failed to write html %w", err) 59 } 60 61 return nil 62 } 63 64 func getTemplate(dir http.FileSystem, path string) ([]byte, error) { 65 f, err := dir.Open(path) 66 if err != nil { 67 return nil, fmt.Errorf("could not find file %s: %w", path, err) 68 } 69 70 b, err := io.ReadAll(f) 71 if err != nil { 72 return nil, fmt.Errorf("could not read file %s: %w", path, err) 73 } 74 75 return b, nil 76 }