mirror of
https://github.com/golang/go
synced 2024-10-01 01:18:32 -06:00
internal/lsp: add new stats library
This is the basic library that allows for recording of stats about the program operation. Change-Id: I09f7e3de5fc37aaf29bc0db46f15b15056fc0eb2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/185338 Run-TryBot: Ian Cottrell <iancottrell@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
75a6f91e26
commit
f82b303b69
@ -16,7 +16,6 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/telemetry"
|
||||
"golang.org/x/tools/internal/lsp/telemetry/stats"
|
||||
"golang.org/x/tools/internal/lsp/telemetry/tag"
|
||||
"golang.org/x/tools/internal/lsp/telemetry/trace"
|
||||
)
|
||||
@ -81,18 +80,12 @@ type Handler func(context.Context, *Request)
|
||||
type Canceler func(context.Context, *Conn, ID)
|
||||
|
||||
type rpcStats struct {
|
||||
server bool
|
||||
method string
|
||||
span trace.Span
|
||||
start time.Time
|
||||
received int64
|
||||
sent int64
|
||||
server bool
|
||||
method string
|
||||
span trace.Span
|
||||
start time.Time
|
||||
}
|
||||
|
||||
type statsKeyType string
|
||||
|
||||
const rpcStatsKey = statsKeyType("rpcStatsKey")
|
||||
|
||||
func start(ctx context.Context, server bool, method string, id *ID) (context.Context, *rpcStats) {
|
||||
if method == "" {
|
||||
panic("no method in rpc stats")
|
||||
@ -102,7 +95,6 @@ func start(ctx context.Context, server bool, method string, id *ID) (context.Con
|
||||
method: method,
|
||||
start: time.Now(),
|
||||
}
|
||||
ctx = context.WithValue(ctx, rpcStatsKey, s)
|
||||
mode := telemetry.Outbound
|
||||
if server {
|
||||
mode = telemetry.Inbound
|
||||
@ -112,7 +104,7 @@ func start(ctx context.Context, server bool, method string, id *ID) (context.Con
|
||||
tag.Tag{Key: telemetry.RPCDirection, Value: mode},
|
||||
tag.Tag{Key: telemetry.RPCID, Value: id},
|
||||
)
|
||||
stats.Record(ctx, telemetry.Started.M(1))
|
||||
telemetry.Started.Record(ctx, 1)
|
||||
return ctx, s
|
||||
}
|
||||
|
||||
@ -124,13 +116,7 @@ func (s *rpcStats) end(ctx context.Context, err *error) {
|
||||
}
|
||||
elapsedTime := time.Since(s.start)
|
||||
latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
|
||||
|
||||
stats.Record(ctx,
|
||||
telemetry.ReceivedBytes.M(s.received),
|
||||
telemetry.SentBytes.M(s.sent),
|
||||
telemetry.Latency.M(latencyMillis),
|
||||
)
|
||||
|
||||
telemetry.Latency.Record(ctx, latencyMillis)
|
||||
s.span.End()
|
||||
}
|
||||
|
||||
@ -199,7 +185,7 @@ func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (e
|
||||
}
|
||||
c.Logger(Send, nil, -1, request.Method, request.Params, nil)
|
||||
n, err := c.stream.Write(ctx, data)
|
||||
rpcStats.sent += n
|
||||
telemetry.SentBytes.Record(ctx, n)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -241,7 +227,7 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
|
||||
before := time.Now()
|
||||
c.Logger(Send, request.ID, -1, request.Method, request.Params, nil)
|
||||
n, err := c.stream.Write(ctx, data)
|
||||
rpcStats.sent += n
|
||||
telemetry.SentBytes.Record(ctx, n)
|
||||
if err != nil {
|
||||
// sending failed, we will never get a response, so don't leave it pending
|
||||
return err
|
||||
@ -336,13 +322,7 @@ func (r *Request) Reply(ctx context.Context, result interface{}, err error) erro
|
||||
}
|
||||
r.conn.Logger(Send, response.ID, elapsed, r.Method, response.Result, response.Error)
|
||||
n, err := r.conn.stream.Write(ctx, data)
|
||||
|
||||
v := ctx.Value(rpcStatsKey)
|
||||
if v != nil {
|
||||
v.(*rpcStats).sent += n
|
||||
} else {
|
||||
panic("no stats available in reply")
|
||||
}
|
||||
telemetry.SentBytes.Record(ctx, n)
|
||||
|
||||
if err != nil {
|
||||
// TODO(iancottrell): if a stream write fails, we really need to shut down
|
||||
@ -407,7 +387,7 @@ func (c *Conn) Run(ctx context.Context) error {
|
||||
// if method is set it must be a request
|
||||
reqCtx, cancelReq := context.WithCancel(ctx)
|
||||
reqCtx, rpcStats := start(reqCtx, true, msg.Method, msg.ID)
|
||||
rpcStats.received += n
|
||||
telemetry.ReceivedBytes.Record(ctx, n)
|
||||
thisRequest := nextRequest
|
||||
nextRequest = make(chan struct{})
|
||||
req := &Request{
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/telemetry"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
@ -217,7 +216,6 @@ 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)
|
||||
|
@ -3,45 +3,103 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package stats provides support for recording telemetry statistics.
|
||||
// It acts as a coordination point between things that want to record stats,
|
||||
// and things that want to aggregate and report stats.
|
||||
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) {}
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Int64Measure is used to record integer values.
|
||||
type Int64Measure struct {
|
||||
name string
|
||||
description string
|
||||
unit Unit
|
||||
subscribers []Int64Subscriber
|
||||
}
|
||||
|
||||
// Int64Measure is used to record floating point values.
|
||||
type Float64Measure struct {
|
||||
name string
|
||||
description string
|
||||
unit Unit
|
||||
subscribers []Float64Subscriber
|
||||
}
|
||||
|
||||
// Int64Subscriber is the type for functions that want to listen to
|
||||
// integer statistic events.
|
||||
type Int64Subscriber func(context.Context, *Int64Measure, int64)
|
||||
|
||||
// Float64Subscriber is the type for functions that want to listen to
|
||||
// floating point statistic events.
|
||||
type Float64Subscriber func(context.Context, *Float64Measure, float64)
|
||||
|
||||
// Unit is used to specify the units for a given measure.
|
||||
// This is can used for display purposes.
|
||||
type Unit int
|
||||
|
||||
const (
|
||||
// UnitDimensionless indicates that a measure has no specified units.
|
||||
UnitDimensionless = Unit(iota)
|
||||
// UnitBytes indicates that that a measure is recording number of bytes.
|
||||
UnitBytes
|
||||
// UnitMilliseconds indicates that a measure is recording a duration in milliseconds.
|
||||
UnitMilliseconds
|
||||
)
|
||||
|
||||
// Int64 creates a new Int64Measure and prepares it for use.
|
||||
func Int64(name string, description string, unit Unit) *Int64Measure {
|
||||
return &Int64Measure{
|
||||
name: name,
|
||||
description: description,
|
||||
unit: unit,
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 creates a new Float64Measure and prepares it for use.
|
||||
func Float64(name string, description string, unit Unit) *Float64Measure {
|
||||
return &Float64Measure{
|
||||
name: name,
|
||||
description: description,
|
||||
unit: unit,
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name this measure was given on construction.
|
||||
func (m *Int64Measure) Name() string { return m.name }
|
||||
|
||||
// Description returns the description this measure was given on construction.
|
||||
func (m *Int64Measure) Description() string { return m.description }
|
||||
|
||||
// Unit returns the units this measure was given on construction.
|
||||
func (m *Int64Measure) Unit() Unit { return m.unit }
|
||||
|
||||
// Subscribe adds a new subscriber to this measure.
|
||||
func (m *Int64Measure) Subscribe(s Int64Subscriber) { m.subscribers = append(m.subscribers, s) }
|
||||
|
||||
// Record delivers a new value to the subscribers of this measure.
|
||||
func (m *Int64Measure) Record(ctx context.Context, value int64) {
|
||||
for _, s := range m.subscribers {
|
||||
s(ctx, m, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name this measure was given on construction.
|
||||
func (m *Float64Measure) Name() string { return m.name }
|
||||
|
||||
// Description returns the description this measure was given on construction.
|
||||
func (m *Float64Measure) Description() string { return m.description }
|
||||
|
||||
// Unit returns the units this measure was given on construction.
|
||||
func (m *Float64Measure) Unit() Unit { return m.unit }
|
||||
|
||||
// Subscribe adds a new subscriber to this measure.
|
||||
func (m *Float64Measure) Subscribe(s Float64Subscriber) { m.subscribers = append(m.subscribers, s) }
|
||||
|
||||
// Record delivers a new value to the subscribers of this measure.
|
||||
func (m *Float64Measure) Record(ctx context.Context, value float64) {
|
||||
for _, s := range m.subscribers {
|
||||
s(ctx, m, value)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,6 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/telemetry/stats"
|
||||
"golang.org/x/tools/internal/lsp/telemetry/tag"
|
||||
)
|
||||
@ -25,12 +23,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
Handle = func(mux *http.ServeMux) {}
|
||||
|
||||
Started = stats.NullInt64Measure()
|
||||
ReceivedBytes = stats.NullInt64Measure()
|
||||
SentBytes = stats.NullInt64Measure()
|
||||
Latency = stats.NullFloat64Measure()
|
||||
// create the stats we measure
|
||||
Started = stats.Int64("started", "Count of started RPCs.", stats.UnitDimensionless)
|
||||
ReceivedBytes = stats.Int64("received_bytes", "Bytes received.", stats.UnitBytes)
|
||||
SentBytes = stats.Int64("sent_bytes", "Bytes sent.", stats.UnitBytes)
|
||||
Latency = stats.Float64("latency_ms", "Elapsed time in milliseconds", stats.UnitMilliseconds)
|
||||
)
|
||||
|
||||
const (
|
||||
|
Loading…
Reference in New Issue
Block a user