diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go
index c329b0d44f..73cbcd1de1 100644
--- a/internal/lsp/debug/serve.go
+++ b/internal/lsp/debug/serve.go
@@ -19,6 +19,7 @@ import (
"strconv"
"sync"
+ "golang.org/x/tools/internal/lsp/telemetry"
"golang.org/x/tools/internal/span"
)
@@ -216,6 +217,7 @@ func Serve(ctx context.Context, addr string) error {
mux := http.NewServeMux()
mux.HandleFunc("/", Render(mainTmpl, func(*http.Request) interface{} { return data }))
mux.HandleFunc("/debug/", Render(debugTmpl, nil))
+ telemetry.Handle(mux)
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
@@ -354,6 +356,8 @@ var debugTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
{{define "title"}}GoPls Debug pages{{end}}
{{define "body"}}
Profiling
+RPCz
+Tracez
{{end}}
`))
diff --git a/internal/lsp/telemetry/stats/stats.go b/internal/lsp/telemetry/stats/stats.go
new file mode 100644
index 0000000000..cade4151c0
--- /dev/null
+++ b/internal/lsp/telemetry/stats/stats.go
@@ -0,0 +1,47 @@
+// Copyright 2019 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.
+
+// Package stats provides support for recording telemetry statistics.
+package stats
+
+import "context"
+
+type Measure interface {
+ Name() string
+ Description() string
+ Unit() string
+}
+
+type Float64Measure interface {
+ Measure
+ M(v float64) Measurement
+}
+
+type Int64Measure interface {
+ Measure
+ M(v int64) Measurement
+}
+
+type Measurement interface {
+ Measure() Measure
+ Value() float64
+}
+
+type nullMeasure struct{}
+type nullFloat64Measure struct{ nullMeasure }
+type nullInt64Measure struct{ nullMeasure }
+
+func (nullMeasure) Name() string { return "" }
+func (nullMeasure) Description() string { return "" }
+func (nullMeasure) Unit() string { return "" }
+
+func (nullFloat64Measure) M(v float64) Measurement { return nil }
+func (nullInt64Measure) M(v int64) Measurement { return nil }
+
+func NullFloat64Measure() Float64Measure { return nullFloat64Measure{} }
+func NullInt64Measure() Int64Measure { return nullInt64Measure{} }
+
+var (
+ Record = func(ctx context.Context, ms ...Measurement) {}
+)
diff --git a/internal/lsp/telemetry/tag/tag.go b/internal/lsp/telemetry/tag/tag.go
new file mode 100644
index 0000000000..a59559b342
--- /dev/null
+++ b/internal/lsp/telemetry/tag/tag.go
@@ -0,0 +1,32 @@
+// Copyright 2019 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.
+
+// Package tag adds support for telemetry tags.
+package tag
+
+import "context"
+
+type Map interface{}
+
+type Key interface {
+ Name() string
+}
+
+type Mutator interface {
+ Mutate(Map) (Map, error)
+}
+
+type nullMutator struct{}
+
+func (nullMutator) Mutate(Map) (Map, error) { return nil, nil }
+
+var (
+ New = func(ctx context.Context, mutator ...Mutator) (context.Context, error) { return ctx, nil }
+ NewContext = func(ctx context.Context, m Map) context.Context { return ctx }
+ FromContext = func(ctx context.Context) Map { return nil }
+ Delete = func(k Key) Mutator { return nullMutator{} }
+ Insert = func(k Key, v string) Mutator { return nullMutator{} }
+ Update = func(k Key, v string) Mutator { return nullMutator{} }
+ Upsert = func(k Key, v string) Mutator { return nullMutator{} }
+)
diff --git a/internal/lsp/telemetry/telemetry.go b/internal/lsp/telemetry/telemetry.go
new file mode 100644
index 0000000000..67b80365ec
--- /dev/null
+++ b/internal/lsp/telemetry/telemetry.go
@@ -0,0 +1,33 @@
+// Copyright 2019 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.
+
+// Package telemetry provides the hooks and adapters to allow use of telemetry
+// throughout gopls.
+package telemetry
+
+import (
+ "net/http"
+
+ "golang.org/x/tools/internal/lsp/telemetry/stats"
+ "golang.org/x/tools/internal/lsp/telemetry/tag"
+)
+
+var (
+ Handle = func(mux *http.ServeMux) {}
+
+ Started = stats.NullInt64Measure()
+ ReceivedBytes = stats.NullInt64Measure()
+ SentBytes = stats.NullInt64Measure()
+ Latency = stats.NullFloat64Measure()
+
+ KeyRPCID tag.Key
+ KeyMethod tag.Key
+ KeyStatus tag.Key
+ KeyRPCDirection tag.Key
+)
+
+const (
+ Inbound = "in"
+ Outbound = "out"
+)
diff --git a/internal/lsp/telemetry/trace/trace.go b/internal/lsp/telemetry/trace/trace.go
new file mode 100644
index 0000000000..96760b55f2
--- /dev/null
+++ b/internal/lsp/telemetry/trace/trace.go
@@ -0,0 +1,58 @@
+// Copyright 2019 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.
+
+// Package tag adds support for telemetry tracins.
+package trace
+
+import "context"
+
+type Span interface {
+ AddAttributes(attributes ...Attribute)
+
+ AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64)
+ AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64)
+ Annotate(attributes []Attribute, str string)
+ Annotatef(attributes []Attribute, format string, a ...interface{})
+ End()
+ IsRecordingEvents() bool
+ SetName(name string)
+ SetStatus(status Status)
+}
+
+type Attribute interface{}
+
+type Status struct {
+ Code int32
+ Message string
+}
+
+type nullSpan struct{}
+
+func (nullSpan) AddAttributes(attributes ...Attribute) {}
+func (nullSpan) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {}
+func (nullSpan) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {}
+func (nullSpan) Annotate(attributes []Attribute, str string) {}
+func (nullSpan) Annotatef(attributes []Attribute, format string, a ...interface{}) {}
+func (nullSpan) End() {}
+func (nullSpan) IsRecordingEvents() bool { return false }
+func (nullSpan) SetName(name string) {}
+func (nullSpan) SetStatus(status Status) {}
+
+var (
+ FromContext = func(ctx context.Context) Span { return nullSpan{} }
+ StartSpan = func(ctx context.Context, name string, options ...interface{}) (context.Context, Span) {
+ return ctx, nullSpan{}
+ }
+ BoolAttribute = func(key string, value bool) Attribute { return nil }
+ Float64Attribute = func(key string, value float64) Attribute { return nil }
+ Int64Attribute = func(key string, value int64) Attribute { return nil }
+ StringAttribute = func(key string, value string) Attribute { return nil }
+ WithSpanKind = func(spanKind int) interface{} { return nil }
+)
+
+const (
+ SpanKindUnspecified = iota
+ SpanKindServer
+ SpanKindClient
+)