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 +)