1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:14:44 -07:00

internal/lsp: move all debugging support to the debug package

We now carry around a debug instance in the app, and it manages the log file and
all telemetry

Change-Id: If4a51a36c38ef301b1b2bbda8e4c0a8d9bfc3c04
Reviewed-on: https://go-review.googlesource.com/c/tools/+/218457
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Ian Cottrell 2020-02-07 13:30:31 +00:00 committed by Robert Findley
parent a1f8cf0047
commit bdd8440908
3 changed files with 74 additions and 44 deletions

View File

@ -18,15 +18,15 @@ import (
"os" "os"
"strings" "strings"
"sync" "sync"
"time"
"golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp" "golang.org/x/tools/internal/lsp"
"golang.org/x/tools/internal/lsp/cache" "golang.org/x/tools/internal/lsp/cache"
"golang.org/x/tools/internal/lsp/debug"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span" "golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/export"
"golang.org/x/tools/internal/telemetry/export/ocagent"
"golang.org/x/tools/internal/tool" "golang.org/x/tools/internal/tool"
"golang.org/x/tools/internal/xcontext" "golang.org/x/tools/internal/xcontext"
errors "golang.org/x/xerrors" errors "golang.org/x/xerrors"
@ -69,6 +69,8 @@ type Application struct {
// PrepareOptions is called to update the options when a new view is built. // PrepareOptions is called to update the options when a new view is built.
// It is primarily to allow the behavior of gopls to be modified by hooks. // It is primarily to allow the behavior of gopls to be modified by hooks.
PrepareOptions func(*source.Options) PrepareOptions func(*source.Options)
debug debug.Instance
} }
// New returns a new Application ready to run. // New returns a new Application ready to run.
@ -130,10 +132,12 @@ gopls flags are:
// If no arguments are passed it will invoke the server sub command, as a // If no arguments are passed it will invoke the server sub command, as a
// temporary measure for compatibility. // temporary measure for compatibility.
func (app *Application) Run(ctx context.Context, args ...string) error { func (app *Application) Run(ctx context.Context, args ...string) error {
ocConfig := ocagent.Discover() app.debug = debug.Instance{
//TODO: we should not need to adjust the discovered configuration StartTime: time.Now(),
ocConfig.Address = app.OCAgent Workdir: app.wd,
export.AddExporters(ocagent.Connect(ocConfig)) OCAgentConfig: app.OCAgent,
}
app.debug.Prepare(ctx)
app.Serve.app = app app.Serve.app = app
if len(args) == 0 { if len(args) == 0 {
return tool.Run(ctx, &app.Serve, args) return tool.Run(ctx, &app.Serve, args)

View File

@ -9,19 +9,14 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"log"
"net" "net"
"os" "os"
"path/filepath"
"time"
"golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/cache" "golang.org/x/tools/internal/lsp/cache"
"golang.org/x/tools/internal/lsp/debug"
"golang.org/x/tools/internal/lsp/lsprpc" "golang.org/x/tools/internal/lsp/lsprpc"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/tool" "golang.org/x/tools/internal/tool"
errors "golang.org/x/xerrors"
) )
// Serve is a struct that exposes the configurable parts of the LSP server as // Serve is a struct that exposes the configurable parts of the LSP server as
@ -58,29 +53,13 @@ func (s *Serve) Run(ctx context.Context, args ...string) error {
if len(args) > 0 { if len(args) > 0 {
return tool.CommandLineErrorf("server does not take arguments, got %v", args) return tool.CommandLineErrorf("server does not take arguments, got %v", args)
} }
out := os.Stderr
logfile := s.Logfile
if logfile != "" {
if logfile == "auto" {
logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
}
f, err := os.Create(logfile)
if err != nil {
return errors.Errorf("Unable to create log file: %v", err)
}
defer f.Close()
log.SetOutput(io.MultiWriter(os.Stderr, f))
out = f
}
debug := debug.Instance{ if err := s.app.debug.SetLogFile(s.Logfile); err != nil {
Logfile: logfile, return err
StartTime: time.Now(),
ServerAddress: s.Address,
DebugAddress: s.Debug,
Workdir: s.app.wd,
} }
debug.Serve(ctx, s.Debug) s.app.debug.ServerAddress = s.Address
s.app.debug.DebugAddress = s.Debug
s.app.debug.Serve(ctx)
if s.app.Remote != "" { if s.app.Remote != "" {
return s.forward() return s.forward()
@ -96,7 +75,7 @@ func (s *Serve) Run(ctx context.Context, args ...string) error {
} }
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout) stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
if s.Trace { if s.Trace {
stream = protocol.LoggingStream(stream, out) stream = protocol.LoggingStream(stream, s.app.debug.LogWriter)
} }
return ss.ServeStream(ctx, stream) return ss.ServeStream(ctx, stream)
} }

View File

@ -7,14 +7,18 @@ package debug
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"go/token" "go/token"
"html/template" "html/template"
"io"
stdlog "log" stdlog "log"
"net" "net"
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
_ "net/http/pprof" // pull in the standard pprof handlers _ "net/http/pprof" // pull in the standard pprof handlers
"os"
"path" "path"
"path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"strconv" "strconv"
@ -24,6 +28,7 @@ import (
"golang.org/x/tools/internal/span" "golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/export" "golang.org/x/tools/internal/telemetry/export"
"golang.org/x/tools/internal/telemetry/export/ocagent"
"golang.org/x/tools/internal/telemetry/export/prometheus" "golang.org/x/tools/internal/telemetry/export/prometheus"
"golang.org/x/tools/internal/telemetry/log" "golang.org/x/tools/internal/telemetry/log"
"golang.org/x/tools/internal/telemetry/tag" "golang.org/x/tools/internal/telemetry/tag"
@ -35,6 +40,14 @@ type Instance struct {
ServerAddress string ServerAddress string
DebugAddress string DebugAddress string
Workdir string Workdir string
OCAgentConfig string
LogWriter io.Writer
ocagent export.Exporter
prometheus *prometheus.Exporter
rpcs *rpcs
traces *traces
} }
type Cache interface { type Cache interface {
@ -229,29 +242,57 @@ func DropView(view View) {
} }
} }
// Prepare gets a debug instance ready for use using the supplied configuration.
func (i *Instance) Prepare(ctx context.Context) {
mu.Lock()
defer mu.Unlock()
i.LogWriter = os.Stderr
ocConfig := ocagent.Discover()
//TODO: we should not need to adjust the discovered configuration
ocConfig.Address = i.OCAgentConfig
i.ocagent = ocagent.Connect(ocConfig)
i.prometheus = prometheus.New()
i.rpcs = &rpcs{}
i.traces = &traces{}
export.AddExporters(i.ocagent, i.prometheus, i.rpcs, i.traces)
}
func (i *Instance) SetLogFile(logfile string) error {
if logfile != "" {
if logfile == "auto" {
logfile = filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.log", os.Getpid()))
}
f, err := os.Create(logfile)
if err != nil {
return fmt.Errorf("Unable to create log file: %v", err)
}
defer f.Close()
stdlog.SetOutput(io.MultiWriter(os.Stderr, f))
i.LogWriter = f
}
i.Logfile = logfile
return nil
}
// Serve starts and runs a debug server in the background. // 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 // It also logs the port the server starts on, to allow for :0 auto assigned
// ports. // ports.
func (i *Instance) Serve(ctx context.Context, addr string) error { func (i *Instance) Serve(ctx context.Context) error {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
if addr == "" { if i.DebugAddress == "" {
return nil return nil
} }
listener, err := net.Listen("tcp", addr) listener, err := net.Listen("tcp", i.DebugAddress)
if err != nil { if err != nil {
return err return err
} }
port := listener.Addr().(*net.TCPAddr).Port port := listener.Addr().(*net.TCPAddr).Port
if strings.HasSuffix(addr, ":0") { if strings.HasSuffix(i.DebugAddress, ":0") {
stdlog.Printf("debug server listening on port %d", port) stdlog.Printf("debug server listening on port %d", port)
} }
log.Print(ctx, "Debug serving", tag.Of("Port", port)) log.Print(ctx, "Debug serving", tag.Of("Port", port))
prometheus := prometheus.New()
rpcs := &rpcs{}
traces := &traces{}
export.AddExporters(prometheus, rpcs, traces)
go func() { go func() {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", render(mainTmpl, func(*http.Request) interface{} { return data })) mux.HandleFunc("/", render(mainTmpl, func(*http.Request) interface{} { return data }))
@ -261,9 +302,15 @@ func (i *Instance) Serve(ctx context.Context, addr string) error {
mux.HandleFunc("/debug/pprof/profile", pprof.Profile) mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace) mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
mux.HandleFunc("/metrics/", prometheus.Serve) if i.prometheus != nil {
mux.HandleFunc("/rpc/", render(rpcTmpl, rpcs.getData)) mux.HandleFunc("/metrics/", i.prometheus.Serve)
mux.HandleFunc("/trace/", render(traceTmpl, traces.getData)) }
if i.rpcs != nil {
mux.HandleFunc("/rpc/", render(rpcTmpl, i.rpcs.getData))
}
if i.traces != nil {
mux.HandleFunc("/trace/", render(traceTmpl, i.traces.getData))
}
mux.HandleFunc("/cache/", render(cacheTmpl, getCache)) mux.HandleFunc("/cache/", render(cacheTmpl, getCache))
mux.HandleFunc("/session/", render(sessionTmpl, getSession)) mux.HandleFunc("/session/", render(sessionTmpl, getSession))
mux.HandleFunc("/view/", render(viewTmpl, getView)) mux.HandleFunc("/view/", render(viewTmpl, getView))