internal/server: add instrumentation to track gopls command usage

- Add source field to args in server/code_lens
- Interpet and log source field in executeComand

Change-Id: Ia5cbe8176f97c69c2ed0e0e72024c5c898846e37
Reviewed-on: https://go-review.googlesource.com/c/tools/+/709956
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Ethan Lee <ethanalee@google.com>
Reviewed-by: Hongxiang Jiang <hxjiang@golang.org>
diff --git a/gopls/internal/server/code_lens.go b/gopls/internal/server/code_lens.go
index b644f8b..3a7afe4 100644
--- a/gopls/internal/server/code_lens.go
+++ b/gopls/internal/server/code_lens.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"sort"
 
@@ -19,6 +20,10 @@
 	"golang.org/x/tools/internal/event"
 )
 
+type codeLensMetadata struct {
+	Source string `json:"source"`
+}
+
 // CodeLens reports the set of available CodeLenses
 // (range-associated commands) in the given file.
 func (s *server) CodeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
@@ -55,6 +60,18 @@
 		}
 		lenses = append(lenses, added...)
 	}
+	for i := range lenses {
+		if lenses[i].Command == nil {
+			continue
+		}
+		meta := codeLensMetadata{Source: "codelens"}
+		metaBytes, err := json.Marshal(meta)
+		if err != nil {
+			event.Error(ctx, "failed to marshal codelens metadata", err)
+			continue
+		}
+		lenses[i].Command.Arguments = append(lenses[i].Command.Arguments, json.RawMessage(metaBytes))
+	}
 	sort.Slice(lenses, func(i, j int) bool {
 		a, b := lenses[i], lenses[j]
 		if cmp := protocol.CompareRange(a.Range, b.Range); cmp != 0 {
diff --git a/gopls/internal/server/command.go b/gopls/internal/server/command.go
index 532a353..675a29d 100644
--- a/gopls/internal/server/command.go
+++ b/gopls/internal/server/command.go
@@ -65,6 +65,19 @@
 		return nil, fmt.Errorf("%s is not a supported command", params.Command)
 	}
 
+	if len(params.Arguments) > 0 {
+		last := params.Arguments[len(params.Arguments)-1]
+		var meta struct {
+			Source string `json:"source"`
+		}
+		if err := json.Unmarshal(last, &meta); err == nil && meta.Source != "" {
+			commandName := strings.TrimPrefix(params.Command, "gopls.")
+			counterName := fmt.Sprintf("gopls/cmd/source:%s-%s", commandName, meta.Source)
+			counter.New(counterName).Inc()
+			params.Arguments = params.Arguments[:len(params.Arguments)-1]
+		}
+	}
+
 	handler := &commandHandler{
 		s:      s,
 		params: params,