github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/witch/witch.go (about)

     1  package witch
     2  
     3  import "net/http"
     4  import (
     5  	"bytes"
     6  	"github.com/rakyll/statik/fs"
     7  	"github.com/v2pro/plz/countlog"
     8  	_ "github.com/v2pro/plz/witch/statik"
     9  	"io/ioutil"
    10  	"github.com/v2pro/plz/countlog/output"
    11  	"github.com/v2pro/plz/countlog/output/json"
    12  	"expvar"
    13  	"path"
    14  	"runtime"
    15  	"reflect"
    16  )
    17  
    18  var files = []string{
    19  	"ide.html",
    20  	"log-viewer.html", "filters.html", "data-sources.html", "columns.html",
    21  	"state-viewer.html", "snapshots.html",
    22  	"viz.html", "viz-item.html", "viz-value.html",
    23  	"viz-struct.html", "viz-array.html",
    24  	"viz-plain.html", "viz-ptr.html"}
    25  
    26  //go:generate $GOPATH/bin/statik -src $PWD/webroot
    27  
    28  var viewerHtml []byte
    29  
    30  var Mux = &http.ServeMux{}
    31  
    32  func init() {
    33  	Mux.HandleFunc("/witch/more-events", moreEvents)
    34  	Mux.Handle("/witch/expvar", expvar.Handler())
    35  	Mux.HandleFunc("/witch/", homepage)
    36  }
    37  
    38  func initViewerHtml() error {
    39  	if viewerHtml != nil {
    40  		return nil
    41  	}
    42  	statikFS, err := fs.New()
    43  	if err != nil {
    44  		countlog.Error("event!witch.failed to load witch viewer web resource", "err", err)
    45  		return err
    46  	}
    47  	indexHtmlFile, err := statikFS.Open("/index.html")
    48  	if err != nil {
    49  		countlog.Error("event!witch.failed to open index.html", "err", err)
    50  		return err
    51  	}
    52  	indexHtml, err := ioutil.ReadAll(indexHtmlFile)
    53  	if err != nil {
    54  		countlog.Error("event!witch.failed to read index.html", "err", err)
    55  		return err
    56  	}
    57  	components := []byte{}
    58  	for _, file := range files {
    59  		f, err := statikFS.Open("/" + file)
    60  		if err != nil {
    61  			countlog.Error("event!witch.failed to open file", "err", err, "file", file)
    62  			return err
    63  		}
    64  		fileHtml, err := ioutil.ReadAll(f)
    65  		if err != nil {
    66  			countlog.Error("event!witch.failed to read file html", "err", err, "file", file)
    67  			return err
    68  		}
    69  		components = append(components, fileHtml...)
    70  	}
    71  	viewerHtml = bytes.Replace(indexHtml, []byte("{{ COMPONENTS }}"), components, -1)
    72  	return nil
    73  }
    74  
    75  var isDebug = true
    76  var thisFile string
    77  
    78  func init() {
    79  	pc := reflect.ValueOf(loadDebugViewerHtml).Pointer()
    80  	thisFile, _ = runtime.FuncForPC(pc).FileLine(pc)
    81  }
    82  
    83  func loadDebugViewerHtml() ([]byte, error) {
    84  	dir := path.Join(path.Dir(thisFile), "webroot")
    85  	indexHtml, err := ioutil.ReadFile(path.Join(dir, "index.html"))
    86  	if err != nil {
    87  		countlog.Error("event!witch.failed to read index.html", "err", err)
    88  		return nil, err
    89  	}
    90  	components := []byte{}
    91  	for _, file := range files {
    92  		fileHtml, err := ioutil.ReadFile(path.Join(dir, file))
    93  		if err != nil {
    94  			countlog.Error("event!witch.failed to read file", "err", err, "file", file)
    95  			return nil, err
    96  		}
    97  		components = append(components, fileHtml...)
    98  	}
    99  	components = append(components, `
   100  	<script>
   101  		window.isDebug = true;
   102  	</script>
   103  	`...)
   104  	return bytes.Replace(indexHtml, []byte("{{ COMPONENTS }}"), components, -1), nil
   105  }
   106  
   107  func Start(addr string) {
   108  	err := initViewerHtml()
   109  	if err != nil {
   110  		countlog.Error("event!witch.failed to init viewer html", "err", err)
   111  		return
   112  	}
   113  	countlog.Info("event!witch.viewer started", "addr", addr)
   114  	countlog.EventWriter = output.NewEventWriter(output.EventWriterConfig{
   115  		Format: &json.Format{},
   116  		Writer: theEventQueue,
   117  	})
   118  	if addr != "" {
   119  		go func() {
   120  			setCurrentGoRoutineIsKoala()
   121  			http.ListenAndServe(addr, Mux)
   122  		}()
   123  	}
   124  }
   125  
   126  func homepage(respWriter http.ResponseWriter, req *http.Request) {
   127  	setCurrentGoRoutineIsKoala()
   128  	if !isDebug {
   129  		respWriter.Write(viewerHtml)
   130  		return
   131  	}
   132  	debugViewerHtml, err := loadDebugViewerHtml()
   133  	if err != nil {
   134  		respWriter.Write([]byte(err.Error()))
   135  	} else {
   136  		respWriter.Write(debugViewerHtml)
   137  	}
   138  }