mirror of
https://github.com/golang/go
synced 2024-11-18 13:34:41 -07:00
96e9e165b7
This improves the logging capabilities of the jsonrpc 2 library to always include the method and also an optional elapsed time. This is used to implement an lsp inspector compatible logging mode in the golsp. Change-Id: I2f7ac8b9298c4364b1b89cf6f696b534557ed139 Reviewed-on: https://go-review.googlesource.com/c/146157 Reviewed-by: Rebecca Stambler <rstambler@golang.org>
148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// The golsp command is an LSP server for Go.
|
|
// The Language Server Protocol allows any text editor
|
|
// to be extended with IDE-like features;
|
|
// see https://langserver.org/ for details.
|
|
package main // import "golang.org/x/tools/cmd/golsp"
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"runtime/trace"
|
|
"time"
|
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
"golang.org/x/tools/internal/lsp"
|
|
)
|
|
|
|
var (
|
|
cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
|
|
memprofile = flag.String("memprofile", "", "write memory profile to this file")
|
|
traceFlag = flag.String("trace", "", "write trace log to this file")
|
|
|
|
// Flags for compatitibility with VSCode.
|
|
logfile = flag.String("logfile", "", "filename to log to")
|
|
mode = flag.String("mode", "", "no effect")
|
|
)
|
|
|
|
func main() {
|
|
flag.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, "usage: golsp [flags]\n")
|
|
flag.PrintDefaults()
|
|
}
|
|
flag.Parse()
|
|
if flag.NArg() > 0 {
|
|
flag.Usage()
|
|
os.Exit(2)
|
|
}
|
|
|
|
if *cpuprofile != "" {
|
|
f, err := os.Create(*cpuprofile)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := pprof.StartCPUProfile(f); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// NB: profile won't be written in case of error.
|
|
defer pprof.StopCPUProfile()
|
|
}
|
|
|
|
if *traceFlag != "" {
|
|
f, err := os.Create(*traceFlag)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := trace.Start(f); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// NB: trace log won't be written in case of error.
|
|
defer func() {
|
|
trace.Stop()
|
|
log.Printf("To view the trace, run:\n$ go tool trace view %s", *traceFlag)
|
|
}()
|
|
}
|
|
|
|
if *memprofile != "" {
|
|
f, err := os.Create(*memprofile)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// NB: memprofile won't be written in case of error.
|
|
defer func() {
|
|
runtime.GC() // get up-to-date statistics
|
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
|
log.Fatalf("Writing memory profile: %v", err)
|
|
}
|
|
f.Close()
|
|
}()
|
|
}
|
|
|
|
out := os.Stderr
|
|
if *logfile != "" {
|
|
f, err := os.Create(*logfile)
|
|
if err != nil {
|
|
log.Fatalf("Unable to create log file: %v", err)
|
|
}
|
|
defer f.Close()
|
|
log.SetOutput(io.MultiWriter(os.Stderr, f))
|
|
out = f
|
|
}
|
|
if err := lsp.RunServer(
|
|
context.Background(),
|
|
jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout),
|
|
func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) {
|
|
|
|
if err != nil {
|
|
fmt.Fprintf(out, "[Error - %v] %s %s%s %v", time.Now().Format("3:04:05 PM"), direction, method, id, err)
|
|
return
|
|
}
|
|
fmt.Fprintf(out, "[Trace - %v] ", time.Now().Format("3:04:05 PM"))
|
|
switch direction {
|
|
case jsonrpc2.Send:
|
|
fmt.Fprint(out, "Received ")
|
|
case jsonrpc2.Receive:
|
|
fmt.Fprint(out, "Sending ")
|
|
}
|
|
switch {
|
|
case id == nil:
|
|
fmt.Fprint(out, "notification ")
|
|
case elapsed >= 0:
|
|
fmt.Fprint(out, "response ")
|
|
default:
|
|
fmt.Fprint(out, "request ")
|
|
}
|
|
fmt.Fprintf(out, "'%s", method)
|
|
switch {
|
|
case id == nil:
|
|
// do nothing
|
|
case id.Name != "":
|
|
fmt.Fprintf(out, " - (%s)", id.Name)
|
|
default:
|
|
fmt.Fprintf(out, " - (%d)", id.Number)
|
|
}
|
|
fmt.Fprint(out, "'")
|
|
if elapsed >= 0 {
|
|
fmt.Fprintf(out, " in %vms", elapsed.Nanoseconds()/1000)
|
|
}
|
|
params := string(*payload)
|
|
if params == "null" {
|
|
params = "{}"
|
|
}
|
|
fmt.Fprintf(out, ".\r\nParams: %s\r\n\r\n\r\n", params)
|
|
},
|
|
); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|