mirror of
https://github.com/golang/go
synced 2024-11-18 20:44:45 -07:00
internal/lsp: add server instance to debug info
When debugging multiple instances of gopls simultaneously, it is useful to be able to inspect stateful debugging information for each server instance, such as the location of logfiles and server startup information. This CL adds an additional section to the /info http handler, that formats additional information related to the gopls instance handling the request. Updates golang/go#34111 Change-Id: I6cb8073800ce52b0645f1898461a19e1ac980d2b Reviewed-on: https://go-review.googlesource.com/c/tools/+/214803 Reviewed-by: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
850357681a
commit
98c82cf1f4
@ -62,12 +62,12 @@ func (s *Serve) Run(ctx context.Context, args ...string) error {
|
||||
return tool.CommandLineErrorf("server does not take arguments, got %v", args)
|
||||
}
|
||||
out := os.Stderr
|
||||
if s.Logfile != "" {
|
||||
filename := s.Logfile
|
||||
if filename == "auto" {
|
||||
filename = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
|
||||
logfile := s.Logfile
|
||||
if logfile != "" {
|
||||
if logfile == "auto" {
|
||||
logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
|
||||
}
|
||||
f, err := os.Create(filename)
|
||||
f, err := os.Create(logfile)
|
||||
if err != nil {
|
||||
return errors.Errorf("Unable to create log file: %v", err)
|
||||
}
|
||||
@ -76,7 +76,7 @@ func (s *Serve) Run(ctx context.Context, args ...string) error {
|
||||
out = f
|
||||
}
|
||||
|
||||
debug.Serve(ctx, s.Debug)
|
||||
debug.Serve(ctx, s.Debug, debugServe{s: s, logfile: logfile, start: time.Now()})
|
||||
|
||||
if s.app.Remote != "" {
|
||||
return s.forward()
|
||||
@ -121,6 +121,20 @@ func (s *Serve) forward() error {
|
||||
return <-errc
|
||||
}
|
||||
|
||||
// debugServe implements the debug.Instance interface.
|
||||
type debugServe struct {
|
||||
s *Serve
|
||||
logfile string
|
||||
start time.Time
|
||||
}
|
||||
|
||||
func (d debugServe) Logfile() string { return d.logfile }
|
||||
func (d debugServe) StartTime() time.Time { return d.start }
|
||||
func (d debugServe) Port() int { return d.s.Port }
|
||||
func (d debugServe) Address() string { return d.s.Address }
|
||||
func (d debugServe) Debug() string { return d.s.Debug }
|
||||
func (d debugServe) Workdir() string { return d.s.app.wd }
|
||||
|
||||
type handler struct{}
|
||||
|
||||
type rpcStats struct {
|
||||
|
@ -23,7 +23,21 @@ const (
|
||||
// Version is a manually-updated mechanism for tracking versions.
|
||||
var Version = "master"
|
||||
|
||||
// This writes the version and environment information to a writer.
|
||||
// PrintServerInfo writes HTML debug info to w for the Instance s.
|
||||
func PrintServerInfo(w io.Writer, s Instance) {
|
||||
section(w, HTML, "Server Instance", func() {
|
||||
fmt.Fprintf(w, "Start time: %v\n", s.StartTime())
|
||||
fmt.Fprintf(w, "LogFile: %s\n", s.Logfile())
|
||||
fmt.Fprintf(w, "Working directory: %s\n", s.Workdir())
|
||||
fmt.Fprintf(w, "Address: %s\n", s.Address())
|
||||
fmt.Fprintf(w, "Debug address: %s\n", s.Debug())
|
||||
})
|
||||
PrintVersionInfo(w, true, HTML)
|
||||
}
|
||||
|
||||
// PrintVersionInfo writes version and environment information to w, using the
|
||||
// output format specified by mode. verbose controls whether additional
|
||||
// information is written, including section headers.
|
||||
func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) {
|
||||
if !verbose {
|
||||
printBuildInfo(w, false, mode)
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/telemetry/export"
|
||||
@ -27,6 +28,14 @@ import (
|
||||
"golang.org/x/tools/internal/telemetry/tag"
|
||||
)
|
||||
|
||||
type Instance interface {
|
||||
Logfile() string
|
||||
StartTime() time.Time
|
||||
Address() string
|
||||
Debug() string
|
||||
Workdir() string
|
||||
}
|
||||
|
||||
type Cache interface {
|
||||
ID() string
|
||||
FileSet() *token.FileSet
|
||||
@ -163,10 +172,12 @@ func getFile(r *http.Request) interface{} {
|
||||
return session.File(hash)
|
||||
}
|
||||
|
||||
func getInfo(r *http.Request) interface{} {
|
||||
buf := &bytes.Buffer{}
|
||||
PrintVersionInfo(buf, true, HTML)
|
||||
return template.HTML(buf.String())
|
||||
func getInfo(s Instance) dataFunc {
|
||||
return func(r *http.Request) interface{} {
|
||||
buf := &bytes.Buffer{}
|
||||
PrintServerInfo(buf, s)
|
||||
return template.HTML(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func getMemory(r *http.Request) interface{} {
|
||||
@ -206,7 +217,7 @@ func DropView(view View) {
|
||||
// Serve starts and runs a debug server in the background.
|
||||
// It also logs the port the server starts on, to allow for :0 auto assigned
|
||||
// ports.
|
||||
func Serve(ctx context.Context, addr string) error {
|
||||
func Serve(ctx context.Context, addr string, instance Instance) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if addr == "" {
|
||||
@ -242,7 +253,7 @@ func Serve(ctx context.Context, addr string) error {
|
||||
mux.HandleFunc("/session/", Render(sessionTmpl, getSession))
|
||||
mux.HandleFunc("/view/", Render(viewTmpl, getView))
|
||||
mux.HandleFunc("/file/", Render(fileTmpl, getFile))
|
||||
mux.HandleFunc("/info", Render(infoTmpl, getInfo))
|
||||
mux.HandleFunc("/info", Render(infoTmpl, getInfo(instance)))
|
||||
mux.HandleFunc("/memory", Render(memoryTmpl, getMemory))
|
||||
if err := http.Serve(listener, mux); err != nil {
|
||||
log.Error(ctx, "Debug server failed", err)
|
||||
@ -253,7 +264,9 @@ func Serve(ctx context.Context, addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Render(tmpl *template.Template, fun func(*http.Request) interface{}) func(http.ResponseWriter, *http.Request) {
|
||||
type dataFunc func(*http.Request) interface{}
|
||||
|
||||
func Render(tmpl *template.Template, fun dataFunc) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var data interface{}
|
||||
if fun != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user