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  }