gopls: normalize logging attributes

As part of the log audit in golang/go#66746, I noticed several
irregularities addressed by this CL:
- Move "tags" (domain-specific event keys, which are used construct
  labels) closer to the packages that use them. Specifically, the
  internal/event/tag package contained gopls-specific tags, but x/tools
  should not care about gopls.
- Use consistent values for log attributes. For example, "method" was
  being used to mean jsonrpc2 method and Go method. Also, "directory"
  was being used as both file path and URI.
- Use log attributes for the view attributes logged when views are
  created.
- Eliminate (yet another) redundant log during Load.
- Include the ViewID with snapshot.Labels, since snapshot IDs are only
  meaningful relative to a View.

With these changes, my audit of logging is complete.

Fixes golang/go#66746

Change-Id: Iaa60797a7412fb8e222e78e2e58eff2da9563bbb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/579335
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/cache/analysis.go b/gopls/internal/cache/analysis.go
index e0c65cb..79ce29d 100644
--- a/gopls/internal/cache/analysis.go
+++ b/gopls/internal/cache/analysis.go
@@ -36,6 +36,7 @@
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/filecache"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/progress"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
@@ -44,7 +45,6 @@
 	"golang.org/x/tools/gopls/internal/util/frob"
 	"golang.org/x/tools/gopls/internal/util/maps"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/facts"
 	"golang.org/x/tools/internal/gcimporter"
 	"golang.org/x/tools/internal/typesinternal"
@@ -186,7 +186,7 @@
 		sort.Strings(keys)
 		tagStr = strings.Join(keys, ",")
 	}
-	ctx, done := event.Start(ctx, "snapshot.Analyze", tag.Package.Of(tagStr))
+	ctx, done := event.Start(ctx, "snapshot.Analyze", label.Package.Of(tagStr))
 	defer done()
 
 	// Filter and sort enabled root analyzers.
diff --git a/gopls/internal/cache/check.go b/gopls/internal/cache/check.go
index e15b743..10e2d5b 100644
--- a/gopls/internal/cache/check.go
+++ b/gopls/internal/cache/check.go
@@ -28,13 +28,13 @@
 	"golang.org/x/tools/gopls/internal/cache/typerefs"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/filecache"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/gopls/internal/util/slices"
 	"golang.org/x/tools/internal/analysisinternal"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gcimporter"
 	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/tokeninternal"
@@ -347,7 +347,7 @@
 //
 // Both pre and post may be called concurrently.
 func (s *Snapshot) forEachPackage(ctx context.Context, ids []PackageID, pre preTypeCheck, post postTypeCheck) error {
-	ctx, done := event.Start(ctx, "cache.forEachPackage", tag.PackageCount.Of(len(ids)))
+	ctx, done := event.Start(ctx, "cache.forEachPackage", label.PackageCount.Of(len(ids)))
 	defer done()
 
 	if len(ids) == 0 {
@@ -612,7 +612,7 @@
 // importPackage loads the given package from its export data in p.exportData
 // (which must already be populated).
 func (b *typeCheckBatch) importPackage(ctx context.Context, mp *metadata.Package, data []byte) (*types.Package, error) {
-	ctx, done := event.Start(ctx, "cache.typeCheckBatch.importPackage", tag.Package.Of(string(mp.ID)))
+	ctx, done := event.Start(ctx, "cache.typeCheckBatch.importPackage", label.Package.Of(string(mp.ID)))
 	defer done()
 
 	impMap := b.importMap(mp.ID)
@@ -678,7 +678,7 @@
 // checkPackageForImport type checks, but skips function bodies and does not
 // record syntax information.
 func (b *typeCheckBatch) checkPackageForImport(ctx context.Context, ph *packageHandle) (*types.Package, error) {
-	ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackageForImport", tag.Package.Of(string(ph.mp.ID)))
+	ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackageForImport", label.Package.Of(string(ph.mp.ID)))
 	defer done()
 
 	onError := func(e error) {
@@ -1469,7 +1469,7 @@
 // deps holds the future results of type-checking the direct dependencies.
 func (b *typeCheckBatch) checkPackage(ctx context.Context, ph *packageHandle) (*Package, error) {
 	inputs := ph.localInputs
-	ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackage", tag.Package.Of(string(inputs.id)))
+	ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackage", label.Package.Of(string(inputs.id)))
 	defer done()
 
 	pkg := &syntaxPackage{
@@ -1586,7 +1586,7 @@
 	for _, e := range pkg.parseErrors {
 		diags, err := parseErrorDiagnostics(pkg, e)
 		if err != nil {
-			event.Error(ctx, "unable to compute positions for parse errors", err, tag.Package.Of(string(inputs.id)))
+			event.Error(ctx, "unable to compute positions for parse errors", err, label.Package.Of(string(inputs.id)))
 			continue
 		}
 		for _, diag := range diags {
diff --git a/gopls/internal/cache/fs_memoized.go b/gopls/internal/cache/fs_memoized.go
index dd8293f..9f156e3 100644
--- a/gopls/internal/cache/fs_memoized.go
+++ b/gopls/internal/cache/fs_memoized.go
@@ -11,9 +11,9 @@
 	"time"
 
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/robustio"
 )
 
@@ -149,7 +149,7 @@
 	}
 	defer func() { <-ioLimit }()
 
-	ctx, done := event.Start(ctx, "cache.readFile", tag.File.Of(uri.Path()))
+	ctx, done := event.Start(ctx, "cache.readFile", label.File.Of(uri.Path()))
 	_ = ctx
 	defer done()
 
diff --git a/gopls/internal/cache/imports.go b/gopls/internal/cache/imports.go
index 7964427..dd58ef5 100644
--- a/gopls/internal/cache/imports.go
+++ b/gopls/internal/cache/imports.go
@@ -11,9 +11,9 @@
 	"time"
 
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/keys"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/imports"
 )
 
@@ -95,7 +95,7 @@
 	timer, ok := c.timers[dir]
 	if !ok {
 		timer = newRefreshTimer(func() {
-			_, done := event.Start(ctx, "cache.sharedModCache.refreshDir", tag.Directory.Of(dir))
+			_, done := event.Start(ctx, "cache.sharedModCache.refreshDir", label.Directory.Of(dir))
 			defer done()
 			imports.ScanModuleCache(dir, cache, logf)
 		})
diff --git a/gopls/internal/cache/load.go b/gopls/internal/cache/load.go
index 44b5b3c..b709e4d 100644
--- a/gopls/internal/cache/load.go
+++ b/gopls/internal/cache/load.go
@@ -18,13 +18,13 @@
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/immutable"
 	"golang.org/x/tools/gopls/internal/util/pathutil"
 	"golang.org/x/tools/gopls/internal/util/slices"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/packagesinternal"
 	"golang.org/x/tools/internal/xcontext"
@@ -113,9 +113,11 @@
 	}
 	sort.Strings(query) // for determinism
 
-	ctx, done := event.Start(ctx, "cache.snapshot.load", tag.Query.Of(query))
+	ctx, done := event.Start(ctx, "cache.snapshot.load", label.Query.Of(query))
 	defer done()
 
+	startTime := time.Now()
+
 	flags := LoadWorkspace
 	if allowNetwork {
 		flags |= AllowNetwork
@@ -145,11 +147,17 @@
 	}
 
 	// This log message is sought for by TestReloadOnlyOnce.
-	labels := append(s.Labels(), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
-	if err != nil {
-		event.Error(ctx, eventName, err, labels...)
-	} else {
-		event.Log(ctx, eventName, labels...)
+	{
+		lbls := append(s.Labels(),
+			label.Query.Of(query),
+			label.PackageCount.Of(len(pkgs)),
+			label.Duration.Of(time.Since(startTime)),
+		)
+		if err != nil {
+			event.Error(ctx, eventName, err, lbls...)
+		} else {
+			event.Log(ctx, eventName, lbls...)
+		}
 	}
 
 	if standalone {
@@ -228,8 +236,8 @@
 		if s.Options().VerboseOutput {
 			event.Log(ctx, eventName, append(
 				s.Labels(),
-				tag.Package.Of(pkg.ID),
-				tag.Files.Of(pkg.CompiledGoFiles))...)
+				label.Package.Of(pkg.ID),
+				label.Files.Of(pkg.CompiledGoFiles))...)
 		}
 
 		// Ignore packages with no sources, since we will never be able to
@@ -289,7 +297,9 @@
 		}
 	}
 
-	event.Log(ctx, fmt.Sprintf("%s: updating metadata for %d packages", eventName, len(updates)))
+	if s.Options().VerboseOutput {
+		event.Log(ctx, fmt.Sprintf("%s: updating metadata for %d packages", eventName, len(updates)))
+	}
 
 	meta := s.meta.Update(updates)
 	workspacePackages := computeWorkspacePackagesLocked(ctx, s, meta)
@@ -560,7 +570,7 @@
 		if err != nil {
 			// There are certain cases where the go command returns invalid
 			// positions, so we cannot panic or even bug.Reportf here.
-			event.Error(ctx, "unable to compute positions for list errors", err, tag.Package.Of(string(mp.ID)))
+			event.Error(ctx, "unable to compute positions for list errors", err, label.Package.Of(string(mp.ID)))
 			continue
 		}
 		diags = append(diags, pkgDiags...)
@@ -574,7 +584,7 @@
 		if ctx.Err() == nil {
 			// TODO(rfindley): consider making this a bug.Reportf. depsErrors should
 			// not normally fail.
-			event.Error(ctx, "unable to compute deps errors", err, tag.Package.Of(string(mp.ID)))
+			event.Error(ctx, "unable to compute deps errors", err, label.Package.Of(string(mp.ID)))
 		}
 	} else {
 		diags = append(diags, depsDiags...)
diff --git a/gopls/internal/cache/mod.go b/gopls/internal/cache/mod.go
index a120037..5373a04 100644
--- a/gopls/internal/cache/mod.go
+++ b/gopls/internal/cache/mod.go
@@ -15,10 +15,10 @@
 	"golang.org/x/mod/modfile"
 	"golang.org/x/mod/module"
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/memoize"
 )
@@ -70,7 +70,7 @@
 // parseModImpl parses the go.mod file whose name and contents are in fh.
 // It may return partial results and an error.
 func parseModImpl(ctx context.Context, fh file.Handle) (*ParsedModule, error) {
-	_, done := event.Start(ctx, "cache.ParseMod", tag.URI.Of(fh.URI()))
+	_, done := event.Start(ctx, "cache.ParseMod", label.URI.Of(fh.URI()))
 	defer done()
 
 	contents, err := fh.Content()
@@ -155,7 +155,7 @@
 
 // parseWorkImpl parses a go.work file. It may return partial results and an error.
 func parseWorkImpl(ctx context.Context, fh file.Handle) (*ParsedWorkFile, error) {
-	_, done := event.Start(ctx, "cache.ParseWork", tag.URI.Of(fh.URI()))
+	_, done := event.Start(ctx, "cache.ParseWork", label.URI.Of(fh.URI()))
 	defer done()
 
 	content, err := fh.Content()
@@ -265,7 +265,7 @@
 
 // modWhyImpl returns the result of "go mod why -m" on the specified go.mod file.
 func modWhyImpl(ctx context.Context, snapshot *Snapshot, fh file.Handle) (map[string]string, error) {
-	ctx, done := event.Start(ctx, "cache.ModWhy", tag.URI.Of(fh.URI()))
+	ctx, done := event.Start(ctx, "cache.ModWhy", label.URI.Of(fh.URI()))
 	defer done()
 
 	pm, err := snapshot.ParseMod(ctx, fh)
diff --git a/gopls/internal/cache/mod_tidy.go b/gopls/internal/cache/mod_tidy.go
index 7986785..ccf77a6 100644
--- a/gopls/internal/cache/mod_tidy.go
+++ b/gopls/internal/cache/mod_tidy.go
@@ -18,11 +18,11 @@
 	"golang.org/x/mod/modfile"
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/memoize"
 )
@@ -99,7 +99,7 @@
 
 // modTidyImpl runs "go mod tidy" on a go.mod file.
 func modTidyImpl(ctx context.Context, snapshot *Snapshot, filename string, pm *ParsedModule) (*TidiedModule, error) {
-	ctx, done := event.Start(ctx, "cache.ModTidy", tag.URI.Of(filename))
+	ctx, done := event.Start(ctx, "cache.ModTidy", label.File.Of(filename))
 	defer done()
 
 	inv := &gocommand.Invocation{
diff --git a/gopls/internal/cache/parsego/parse.go b/gopls/internal/cache/parsego/parse.go
index 739ee13..0143a36 100644
--- a/gopls/internal/cache/parsego/parse.go
+++ b/gopls/internal/cache/parsego/parse.go
@@ -14,12 +14,12 @@
 	"go/token"
 	"reflect"
 
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/astutil"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // Common parse modes; these should be reused wherever possible to increase
@@ -42,7 +42,7 @@
 	if purgeFuncBodies {
 		src = astutil.PurgeFuncBodies(src)
 	}
-	ctx, done := event.Start(ctx, "cache.ParseGoSrc", tag.File.Of(uri.Path()))
+	ctx, done := event.Start(ctx, "cache.ParseGoSrc", label.File.Of(uri.Path()))
 	defer done()
 
 	file, err := parser.ParseFile(fset, uri.Path(), src, mode)
@@ -84,7 +84,7 @@
 			// of the last changes we made to aid in debugging.
 			if i == 9 {
 				unified := diff.Unified("before", "after", string(src), string(newSrc))
-				event.Log(ctx, fmt.Sprintf("fixSrc loop - last diff:\n%v", unified), tag.File.Of(tok.Name()))
+				event.Log(ctx, fmt.Sprintf("fixSrc loop - last diff:\n%v", unified), label.File.Of(tok.Name()))
 			}
 
 			newFile, newErr := parser.ParseFile(fset, uri.Path(), newSrc, mode)
diff --git a/gopls/internal/cache/session.go b/gopls/internal/cache/session.go
index e3553ff..3ea7b58 100644
--- a/gopls/internal/cache/session.go
+++ b/gopls/internal/cache/session.go
@@ -20,12 +20,14 @@
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/cache/typerefs"
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/persistent"
 	"golang.org/x/tools/gopls/internal/util/slices"
 	"golang.org/x/tools/gopls/internal/vulncheck"
 	"golang.org/x/tools/internal/event"
+	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/imports"
 	"golang.org/x/tools/internal/memoize"
@@ -263,7 +265,15 @@
 	}
 
 	// Record the environment of the newly created view in the log.
-	event.Log(ctx, viewEnv(v))
+	event.Log(ctx, fmt.Sprintf("Created View (#%s)", v.id),
+		label.Directory.Of(v.folder.Dir.Path()),
+		viewTypeKey.Of(v.typ.String()),
+		rootDirKey.Of(string(v.root)),
+		goVersionKey.Of(strings.TrimRight(v.folder.Env.GoVersionOutput, "\n")),
+		buildFlagsKey.Of(fmt.Sprint(v.folder.Options.BuildFlags)),
+		envKey.Of(fmt.Sprintf("%+v", v.folder.Env)),
+		envOverlayKey.Of(v.EnvOverlay()),
+	)
 
 	// Initialize the view without blocking.
 	initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
@@ -281,6 +291,16 @@
 	return v, snapshot, snapshot.Acquire()
 }
 
+// These keys are used to log view metadata in createView.
+var (
+	viewTypeKey   = keys.NewString("view_type", "")
+	rootDirKey    = keys.NewString("root_dir", "")
+	goVersionKey  = keys.NewString("go_version", "")
+	buildFlagsKey = keys.New("build_flags", "")
+	envKey        = keys.New("env", "")
+	envOverlayKey = keys.New("env_overlay", "")
+)
+
 // RemoveView removes from the session the view rooted at the specified directory.
 // It reports whether a view of that directory was removed.
 func (s *Session) RemoveView(dir protocol.DocumentURI) bool {
diff --git a/gopls/internal/cache/snapshot.go b/gopls/internal/cache/snapshot.go
index 27b3bcf..7df021c 100644
--- a/gopls/internal/cache/snapshot.go
+++ b/gopls/internal/cache/snapshot.go
@@ -35,6 +35,7 @@
 	"golang.org/x/tools/gopls/internal/cache/xrefs"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/filecache"
+	label1 "golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/gopls/internal/settings"
@@ -47,7 +48,6 @@
 	"golang.org/x/tools/gopls/internal/vulncheck"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/memoize"
 	"golang.org/x/tools/internal/packagesinternal"
@@ -269,7 +269,11 @@
 // SnapshotLabels returns a new slice of labels that should be used for events
 // related to a snapshot.
 func (s *Snapshot) Labels() []label.Label {
-	return []label.Label{tag.Snapshot.Of(s.SequenceID()), tag.Directory.Of(s.Folder())}
+	return []label.Label{
+		label1.ViewID.Of(s.view.id),
+		label1.Snapshot.Of(s.SequenceID()),
+		label1.Directory.Of(s.Folder().Path()),
+	}
 }
 
 // Folder returns the folder at the base of this snapshot.
diff --git a/gopls/internal/cache/view.go b/gopls/internal/cache/view.go
index 3a68ff6..7554b95 100644
--- a/gopls/internal/cache/view.go
+++ b/gopls/internal/cache/view.go
@@ -10,7 +10,6 @@
 package cache
 
 import (
-	"bytes"
 	"context"
 	"encoding/json"
 	"errors"
@@ -302,15 +301,15 @@
 func (t ViewType) String() string {
 	switch t {
 	case GoPackagesDriverView:
-		return "GoPackagesDriverView"
+		return "GoPackagesDriver"
 	case GOPATHView:
-		return "GOPATHView"
+		return "GOPATH"
 	case GoModView:
-		return "GoModView"
+		return "GoMod"
 	case GoWorkView:
-		return "GoWorkView"
+		return "GoWork"
 	case AdHocView:
-		return "AdHocView"
+		return "AdHoc"
 	default:
 		return "Unknown"
 	}
@@ -407,32 +406,6 @@
 	return nil
 }
 
-// viewEnv returns a string describing the environment of a newly created view.
-//
-// It must not be called concurrently with any other view methods.
-// TODO(rfindley): rethink this function, or inline sole call.
-func viewEnv(v *View) string {
-	var buf bytes.Buffer
-	fmt.Fprintf(&buf, `go info for %v
-(view type %v)
-(root dir %s)
-(go version %s)
-(build flags: %v)
-(go env: %+v)
-(env overlay: %v)
-`,
-		v.folder.Dir.Path(),
-		v.typ,
-		v.root.Path(),
-		strings.TrimRight(v.folder.Env.GoVersionOutput, "\n"),
-		v.folder.Options.BuildFlags,
-		v.folder.Env,
-		v.envOverlay,
-	)
-
-	return buf.String()
-}
-
 // RunProcessEnvFunc runs fn with the process env for this snapshot's view.
 // Note: the process env contains cached module and filesystem state.
 func (s *Snapshot) RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error {
diff --git a/gopls/internal/debug/log/log.go b/gopls/internal/debug/log/log.go
index e3eaa10..d015f9b 100644
--- a/gopls/internal/debug/log/log.go
+++ b/gopls/internal/debug/log/log.go
@@ -10,9 +10,9 @@
 	"context"
 	"fmt"
 
+	label1 "golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // Level parameterizes log severity.
@@ -29,7 +29,7 @@
 
 // Log exports a log event labeled with level l.
 func (l Level) Log(ctx context.Context, msg string) {
-	event.Log(ctx, msg, tag.Level.Of(int(l)))
+	event.Log(ctx, msg, label1.Level.Of(int(l)))
 }
 
 // Logf formats and exports a log event labeled with level l.
@@ -39,5 +39,5 @@
 
 // LabeledLevel extracts the labeled log l
 func LabeledLevel(lm label.Map) Level {
-	return Level(tag.Level.Get(lm))
+	return Level(label1.Level.Get(lm))
 }
diff --git a/gopls/internal/debug/metrics.go b/gopls/internal/debug/metrics.go
index c8da803..d8bfe52 100644
--- a/gopls/internal/debug/metrics.go
+++ b/gopls/internal/debug/metrics.go
@@ -7,7 +7,7 @@
 import (
 	"golang.org/x/tools/internal/event/export/metric"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
+	"golang.org/x/tools/internal/jsonrpc2"
 )
 
 var (
@@ -18,41 +18,41 @@
 	receivedBytes = metric.HistogramInt64{
 		Name:        "received_bytes",
 		Description: "Distribution of received bytes, by method.",
-		Keys:        []label.Key{tag.RPCDirection, tag.Method},
+		Keys:        []label.Key{jsonrpc2.RPCDirection, jsonrpc2.Method},
 		Buckets:     bytesDistribution,
 	}
 
 	sentBytes = metric.HistogramInt64{
 		Name:        "sent_bytes",
 		Description: "Distribution of sent bytes, by method.",
-		Keys:        []label.Key{tag.RPCDirection, tag.Method},
+		Keys:        []label.Key{jsonrpc2.RPCDirection, jsonrpc2.Method},
 		Buckets:     bytesDistribution,
 	}
 
 	latency = metric.HistogramFloat64{
 		Name:        "latency",
 		Description: "Distribution of latency in milliseconds, by method.",
-		Keys:        []label.Key{tag.RPCDirection, tag.Method},
+		Keys:        []label.Key{jsonrpc2.RPCDirection, jsonrpc2.Method},
 		Buckets:     millisecondsDistribution,
 	}
 
 	started = metric.Scalar{
 		Name:        "started",
 		Description: "Count of RPCs started by method.",
-		Keys:        []label.Key{tag.RPCDirection, tag.Method},
+		Keys:        []label.Key{jsonrpc2.RPCDirection, jsonrpc2.Method},
 	}
 
 	completed = metric.Scalar{
 		Name:        "completed",
 		Description: "Count of RPCs completed by method and status.",
-		Keys:        []label.Key{tag.RPCDirection, tag.Method, tag.StatusCode},
+		Keys:        []label.Key{jsonrpc2.RPCDirection, jsonrpc2.Method, jsonrpc2.StatusCode},
 	}
 )
 
 func registerMetrics(m *metric.Config) {
-	receivedBytes.Record(m, tag.ReceivedBytes)
-	sentBytes.Record(m, tag.SentBytes)
-	latency.Record(m, tag.Latency)
-	started.Count(m, tag.Started)
-	completed.Count(m, tag.Latency)
+	receivedBytes.Record(m, jsonrpc2.ReceivedBytes)
+	sentBytes.Record(m, jsonrpc2.SentBytes)
+	latency.Record(m, jsonrpc2.Latency)
+	started.Count(m, jsonrpc2.Started)
+	completed.Count(m, jsonrpc2.Latency)
 }
diff --git a/gopls/internal/debug/rpc.go b/gopls/internal/debug/rpc.go
index 0fee0f4..8a696f8 100644
--- a/gopls/internal/debug/rpc.go
+++ b/gopls/internal/debug/rpc.go
@@ -17,7 +17,7 @@
 	"golang.org/x/tools/internal/event/core"
 	"golang.org/x/tools/internal/event/export"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
+	"golang.org/x/tools/internal/jsonrpc2"
 )
 
 var RPCTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
@@ -93,8 +93,8 @@
 			endRPC(span, stats)
 		}
 	case event.IsMetric(ev):
-		sent := byteUnits(tag.SentBytes.Get(lm))
-		rec := byteUnits(tag.ReceivedBytes.Get(lm))
+		sent := byteUnits(jsonrpc2.SentBytes.Get(lm))
+		rec := byteUnits(jsonrpc2.ReceivedBytes.Get(lm))
 		if sent != 0 || rec != 0 {
 			if _, stats := r.getRPCSpan(ctx); stats != nil {
 				stats.Sent += sent
@@ -164,12 +164,12 @@
 }
 
 func (r *Rpcs) getRPCStats(lm label.Map) *rpcStats {
-	method := tag.Method.Get(lm)
+	method := jsonrpc2.Method.Get(lm)
 	if method == "" {
 		return nil
 	}
 	set := &r.Inbound
-	if tag.RPCDirection.Get(lm) != tag.Inbound {
+	if jsonrpc2.RPCDirection.Get(lm) != jsonrpc2.Inbound {
 		set = &r.Outbound
 	}
 	// get the record for this method
@@ -202,7 +202,7 @@
 
 func getStatusCode(span *export.Span) string {
 	for _, ev := range span.Events() {
-		if status := tag.StatusCode.Get(ev); status != "" {
+		if status := jsonrpc2.StatusCode.Get(ev); status != "" {
 			return status
 		}
 	}
diff --git a/gopls/internal/debug/serve.go b/gopls/internal/debug/serve.go
index 62e4168..084f8e2 100644
--- a/gopls/internal/debug/serve.go
+++ b/gopls/internal/debug/serve.go
@@ -26,6 +26,7 @@
 
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/debug/log"
+	label1 "golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/internal/event"
@@ -36,7 +37,6 @@
 	"golang.org/x/tools/internal/event/export/prometheus"
 	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 type contextKeyType int
@@ -440,7 +440,7 @@
 	if strings.HasSuffix(i.debugAddress, ":0") {
 		stdlog.Printf("debug server listening at http://localhost:%d", port)
 	}
-	event.Log(ctx, "Debug serving", tag.Port.Of(port))
+	event.Log(ctx, "Debug serving", label1.Port.Of(port))
 	go func() {
 		mux := http.NewServeMux()
 		mux.HandleFunc("/", render(MainTmpl, func(*http.Request) interface{} { return i }))
@@ -561,27 +561,27 @@
 			if s := cache.KeyCreateSession.Get(ev); s != nil {
 				i.State.addClient(s)
 			}
-			if sid := tag.NewServer.Get(ev); sid != "" {
+			if sid := label1.NewServer.Get(ev); sid != "" {
 				i.State.updateServer(&Server{
 					ID:           sid,
-					Logfile:      tag.Logfile.Get(ev),
-					DebugAddress: tag.DebugAddress.Get(ev),
-					GoplsPath:    tag.GoplsPath.Get(ev),
-					ClientID:     tag.ClientID.Get(ev),
+					Logfile:      label1.Logfile.Get(ev),
+					DebugAddress: label1.DebugAddress.Get(ev),
+					GoplsPath:    label1.GoplsPath.Get(ev),
+					ClientID:     label1.ClientID.Get(ev),
 				})
 			}
 			if s := cache.KeyShutdownSession.Get(ev); s != nil {
 				i.State.dropClient(s)
 			}
-			if sid := tag.EndServer.Get(ev); sid != "" {
+			if sid := label1.EndServer.Get(ev); sid != "" {
 				i.State.dropServer(sid)
 			}
 			if s := cache.KeyUpdateSession.Get(ev); s != nil {
 				if c := i.State.Client(s.ID()); c != nil {
-					c.DebugAddress = tag.DebugAddress.Get(ev)
-					c.Logfile = tag.Logfile.Get(ev)
-					c.ServerID = tag.ServerID.Get(ev)
-					c.GoplsPath = tag.GoplsPath.Get(ev)
+					c.DebugAddress = label1.DebugAddress.Get(ev)
+					c.Logfile = label1.Logfile.Get(ev)
+					c.ServerID = label1.ServerID.Get(ev)
+					c.GoplsPath = label1.GoplsPath.Get(ev)
 				}
 			}
 		}
diff --git a/gopls/internal/golang/call_hierarchy.go b/gopls/internal/golang/call_hierarchy.go
index 7e88df1..5331e6e 100644
--- a/gopls/internal/golang/call_hierarchy.go
+++ b/gopls/internal/golang/call_hierarchy.go
@@ -21,7 +21,6 @@
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file.
@@ -83,7 +82,7 @@
 	for _, ref := range refs {
 		callItem, err := enclosingNodeCallItem(ctx, snapshot, ref.pkgPath, ref.location)
 		if err != nil {
-			event.Error(ctx, "error getting enclosing node", err, tag.Method.Of(string(ref.pkgPath)))
+			event.Error(ctx, fmt.Sprintf("error getting enclosing node for %q", ref.pkgPath), err)
 			continue
 		}
 		loc := protocol.Location{
diff --git a/gopls/internal/golang/codeaction.go b/gopls/internal/golang/codeaction.go
index 99bfa3d..eb4e28b 100644
--- a/gopls/internal/golang/codeaction.go
+++ b/gopls/internal/golang/codeaction.go
@@ -17,13 +17,13 @@
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/gopls/internal/util/slices"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/imports"
 )
 
@@ -48,7 +48,7 @@
 		if wantQuickFixes || want[protocol.SourceOrganizeImports] {
 			importEdits, importEditsPerFix, err := allImportsFixes(ctx, snapshot, pgf)
 			if err != nil {
-				event.Error(ctx, "imports fixes", err, tag.File.Of(fh.URI().Path()))
+				event.Error(ctx, "imports fixes", err, label.File.Of(fh.URI().Path()))
 				importEdits = nil
 				importEditsPerFix = nil
 			}
diff --git a/gopls/internal/golang/types_format.go b/gopls/internal/golang/types_format.go
index 8cc98a9..aab73e3 100644
--- a/gopls/internal/golang/types_format.go
+++ b/gopls/internal/golang/types_format.go
@@ -20,7 +20,6 @@
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/gopls/internal/util/bug"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/tokeninternal"
 	"golang.org/x/tools/internal/typeparams"
 )
@@ -154,7 +153,7 @@
 		cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4}
 		b := &bytes.Buffer{}
 		if err := cfg.Fprint(b, fset, p.Type); err != nil {
-			event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type))
+			event.Error(ctx, fmt.Sprintf("error printing type %s", types.ExprString(p.Type)), err)
 			continue
 		}
 		typ := replacer.Replace(b.String())
diff --git a/gopls/internal/label/keys.go b/gopls/internal/label/keys.go
new file mode 100644
index 0000000..1ef3b17
--- /dev/null
+++ b/gopls/internal/label/keys.go
@@ -0,0 +1,37 @@
+// 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 label provides common labels used to annotate gopls log messages
+// and events.
+package label
+
+import "golang.org/x/tools/internal/event/keys"
+
+var (
+	File      = keys.NewString("file", "")
+	Directory = keys.New("directory", "")
+	URI       = keys.New("URI", "")
+	Package   = keys.NewString("package", "") // sorted comma-separated list of Package IDs
+	Query     = keys.New("query", "")
+	ViewID    = keys.NewString("view_id", "")
+	Snapshot  = keys.NewUInt64("snapshot", "")
+	Operation = keys.NewString("operation", "")
+	Duration  = keys.New("duration", "Elapsed time")
+
+	Position     = keys.New("position", "")
+	PackageCount = keys.NewInt("packages", "")
+	Files        = keys.New("files", "")
+	Port         = keys.NewInt("port", "")
+
+	NewServer = keys.NewString("new_server", "A new server was added")
+	EndServer = keys.NewString("end_server", "A server was shut down")
+
+	ServerID     = keys.NewString("server", "The server ID an event is related to")
+	Logfile      = keys.NewString("logfile", "")
+	DebugAddress = keys.NewString("debug_address", "")
+	GoplsPath    = keys.NewString("gopls_path", "")
+	ClientID     = keys.NewString("client_id", "")
+
+	Level = keys.NewInt("level", "The logging level")
+)
diff --git a/gopls/internal/lsprpc/lsprpc.go b/gopls/internal/lsprpc/lsprpc.go
index 0497612..ceb47aa 100644
--- a/gopls/internal/lsprpc/lsprpc.go
+++ b/gopls/internal/lsprpc/lsprpc.go
@@ -21,12 +21,12 @@
 
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/debug"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/gopls/internal/server"
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/jsonrpc2"
 )
 
@@ -259,11 +259,11 @@
 		event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", goplsPath, hresp.GoplsPath))
 	}
 	event.Log(ctx, "New server",
-		tag.NewServer.Of(f.serverID),
-		tag.Logfile.Of(hresp.Logfile),
-		tag.DebugAddress.Of(hresp.DebugAddr),
-		tag.GoplsPath.Of(hresp.GoplsPath),
-		tag.ClientID.Of(hresp.SessionID),
+		label.NewServer.Of(f.serverID),
+		label.Logfile.Of(hresp.Logfile),
+		label.DebugAddress.Of(hresp.DebugAddr),
+		label.GoplsPath.Of(hresp.GoplsPath),
+		label.ClientID.Of(hresp.SessionID),
 	)
 }
 
@@ -473,10 +473,10 @@
 			}
 			event.Log(ctx, "Handshake session update",
 				cache.KeyUpdateSession.Of(session),
-				tag.DebugAddress.Of(req.DebugAddr),
-				tag.Logfile.Of(req.Logfile),
-				tag.ServerID.Of(req.ServerID),
-				tag.GoplsPath.Of(req.GoplsPath),
+				label.DebugAddress.Of(req.DebugAddr),
+				label.Logfile.Of(req.Logfile),
+				label.ServerID.Of(req.ServerID),
+				label.GoplsPath.Of(req.GoplsPath),
 			)
 			resp := handshakeResponse{
 				SessionID: session.ID(),
diff --git a/gopls/internal/progress/progress.go b/gopls/internal/progress/progress.go
index 0bb17b3..e35c0fe 100644
--- a/gopls/internal/progress/progress.go
+++ b/gopls/internal/progress/progress.go
@@ -16,9 +16,9 @@
 	"strings"
 	"sync"
 
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/xcontext"
 )
 
@@ -267,7 +267,7 @@
 }
 
 func (ew *eventWriter) Write(p []byte) (n int, err error) {
-	event.Log(ew.ctx, string(p), tag.Operation.Of(ew.operation))
+	event.Log(ew.ctx, string(p), label.Operation.Of(ew.operation))
 	return len(p), nil
 }
 
diff --git a/gopls/internal/server/code_lens.go b/gopls/internal/server/code_lens.go
index 7e6506c..cd37fe7 100644
--- a/gopls/internal/server/code_lens.go
+++ b/gopls/internal/server/code_lens.go
@@ -11,15 +11,15 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/mod"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/protocol/command"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) CodeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.codeLens", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.codeLens", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/completion.go b/gopls/internal/server/completion.go
index 5429739..0c759b9 100644
--- a/gopls/internal/server/completion.go
+++ b/gopls/internal/server/completion.go
@@ -12,13 +12,13 @@
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
 	"golang.org/x/tools/gopls/internal/golang/completion"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/gopls/internal/telemetry"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/gopls/internal/work"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Completion(ctx context.Context, params *protocol.CompletionParams) (_ *protocol.CompletionList, rerr error) {
@@ -27,7 +27,7 @@
 		recordLatency(ctx, rerr)
 	}()
 
-	ctx, done := event.Start(ctx, "lsp.Server.completion", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.completion", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
@@ -58,7 +58,7 @@
 		return cl, nil
 	}
 	if err != nil {
-		event.Error(ctx, "no completions found", err, tag.Position.Of(params.Position))
+		event.Error(ctx, "no completions found", err, label.Position.Of(params.Position))
 	}
 	if candidates == nil {
 		complEmpty.Inc()
diff --git a/gopls/internal/server/definition.go b/gopls/internal/server/definition.go
index 7a0eb25..7b4df3c 100644
--- a/gopls/internal/server/definition.go
+++ b/gopls/internal/server/definition.go
@@ -10,11 +10,11 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/telemetry"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Definition(ctx context.Context, params *protocol.DefinitionParams) (_ []protocol.Location, rerr error) {
@@ -23,7 +23,7 @@
 		recordLatency(ctx, rerr)
 	}()
 
-	ctx, done := event.Start(ctx, "lsp.Server.definition", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.definition", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	// TODO(rfindley): definition requests should be multiplexed across all views.
@@ -43,7 +43,7 @@
 }
 
 func (s *server) TypeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) ([]protocol.Location, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.typeDefinition", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.typeDefinition", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	// TODO(rfindley): type definition requests should be multiplexed across all views.
diff --git a/gopls/internal/server/diagnostics.go b/gopls/internal/server/diagnostics.go
index aa509fd..af2cf83 100644
--- a/gopls/internal/server/diagnostics.go
+++ b/gopls/internal/server/diagnostics.go
@@ -21,6 +21,7 @@
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/mod"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
@@ -29,7 +30,6 @@
 	"golang.org/x/tools/gopls/internal/work"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/keys"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // fileDiagnostics holds the current state of published diagnostics for a file.
@@ -511,7 +511,7 @@
 		// if err is non-nil (though as of today it's OK).
 		analysisDiags, err = golang.Analyze(ctx, snapshot, toAnalyze, s.progress)
 		if err != nil {
-			event.Error(ctx, "warning: analyzing package", err, append(snapshot.Labels(), tag.Package.Of(keys.Join(maps.Keys(toDiagnose))))...)
+			event.Error(ctx, "warning: analyzing package", err, append(snapshot.Labels(), label.Package.Of(keys.Join(maps.Keys(toDiagnose))))...)
 			return
 		}
 	}()
@@ -557,7 +557,7 @@
 	for _, mp := range toGCDetail {
 		gcReports, err := golang.GCOptimizationDetails(ctx, snapshot, mp)
 		if err != nil {
-			event.Error(ctx, "warning: gc details", err, append(snapshot.Labels(), tag.Package.Of(string(mp.ID)))...)
+			event.Error(ctx, "warning: gc details", err, append(snapshot.Labels(), label.Package.Of(string(mp.ID)))...)
 			continue
 		}
 		for uri, diags := range gcReports {
@@ -758,7 +758,7 @@
 			if ctx.Err() != nil {
 				return
 			} else {
-				event.Error(ctx, "updateDiagnostics: failed to deliver diagnostics", err, tag.URI.Of(uri))
+				event.Error(ctx, "updateDiagnostics: failed to deliver diagnostics", err, label.URI.Of(uri))
 			}
 		}
 	}
@@ -774,7 +774,7 @@
 					if ctx.Err() != nil {
 						return
 					} else {
-						event.Error(ctx, "updateDiagnostics: failed to deliver diagnostics", err, tag.URI.Of(uri))
+						event.Error(ctx, "updateDiagnostics: failed to deliver diagnostics", err, label.URI.Of(uri))
 					}
 				}
 			}
diff --git a/gopls/internal/server/folding_range.go b/gopls/internal/server/folding_range.go
index cb9d0cb..0ad00e5 100644
--- a/gopls/internal/server/folding_range.go
+++ b/gopls/internal/server/folding_range.go
@@ -9,13 +9,13 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) FoldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.foldingRange", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.foldingRange", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/format.go b/gopls/internal/server/format.go
index 0e6cfdc..1e6344d 100644
--- a/gopls/internal/server/format.go
+++ b/gopls/internal/server/format.go
@@ -9,15 +9,15 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/mod"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/work"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.formatting", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.formatting", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/highlight.go b/gopls/internal/server/highlight.go
index 45eeba7..f60f01e 100644
--- a/gopls/internal/server/highlight.go
+++ b/gopls/internal/server/highlight.go
@@ -9,14 +9,14 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) DocumentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.documentHighlight", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.documentHighlight", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/hover.go b/gopls/internal/server/hover.go
index 1ceede2..c359825 100644
--- a/gopls/internal/server/hover.go
+++ b/gopls/internal/server/hover.go
@@ -9,13 +9,13 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/mod"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/telemetry"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/gopls/internal/work"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Hover(ctx context.Context, params *protocol.HoverParams) (_ *protocol.Hover, rerr error) {
@@ -24,7 +24,7 @@
 		recordLatency(ctx, rerr)
 	}()
 
-	ctx, done := event.Start(ctx, "lsp.Server.hover", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.hover", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/implementation.go b/gopls/internal/server/implementation.go
index b462eac..9e61ebc 100644
--- a/gopls/internal/server/implementation.go
+++ b/gopls/internal/server/implementation.go
@@ -9,10 +9,10 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/telemetry"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Implementation(ctx context.Context, params *protocol.ImplementationParams) (_ []protocol.Location, rerr error) {
@@ -21,7 +21,7 @@
 		recordLatency(ctx, rerr)
 	}()
 
-	ctx, done := event.Start(ctx, "lsp.Server.implementation", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.implementation", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/inlay_hint.go b/gopls/internal/server/inlay_hint.go
index 88ec783..fca8bcb 100644
--- a/gopls/internal/server/inlay_hint.go
+++ b/gopls/internal/server/inlay_hint.go
@@ -9,14 +9,14 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/mod"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) InlayHint(ctx context.Context, params *protocol.InlayHintParams) ([]protocol.InlayHint, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.inlayHint", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.inlayHint", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/link.go b/gopls/internal/server/link.go
index 7b997a6..13097d8 100644
--- a/gopls/internal/server/link.go
+++ b/gopls/internal/server/link.go
@@ -21,10 +21,10 @@
 	"golang.org/x/tools/gopls/internal/cache/parsego"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"mvdan.cc/xurls/v2"
 )
 
@@ -46,7 +46,7 @@
 	}
 	// Don't return errors for document links.
 	if err != nil {
-		event.Error(ctx, "failed to compute document links", err, tag.URI.Of(fh.URI()))
+		event.Error(ctx, "failed to compute document links", err, label.URI.Of(fh.URI()))
 		return nil, nil // empty result
 	}
 	return links, nil // may be empty (for other file types)
diff --git a/gopls/internal/server/references.go b/gopls/internal/server/references.go
index cc02d6f..f501969 100644
--- a/gopls/internal/server/references.go
+++ b/gopls/internal/server/references.go
@@ -9,11 +9,11 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/telemetry"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) References(ctx context.Context, params *protocol.ReferenceParams) (_ []protocol.Location, rerr error) {
@@ -22,7 +22,7 @@
 		recordLatency(ctx, rerr)
 	}()
 
-	ctx, done := event.Start(ctx, "lsp.Server.references", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.references", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/rename.go b/gopls/internal/server/rename.go
index 946cf50..fa90c97 100644
--- a/gopls/internal/server/rename.go
+++ b/gopls/internal/server/rename.go
@@ -11,13 +11,13 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) Rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.rename", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.rename", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
@@ -70,7 +70,7 @@
 // TODO(rfindley): why wouldn't we want to show an error to the user, if the
 // user initiated a rename request at the cursor?
 func (s *server) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (*protocol.PrepareRenamePlaceholder, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.prepareRename", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.prepareRename", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/semantic.go b/gopls/internal/server/semantic.go
index 8192152..ca3df78 100644
--- a/gopls/internal/server/semantic.go
+++ b/gopls/internal/server/semantic.go
@@ -9,10 +9,10 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) SemanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
@@ -24,7 +24,7 @@
 }
 
 func (s *server) semanticTokens(ctx context.Context, td protocol.TextDocumentIdentifier, rng *protocol.Range) (*protocol.SemanticTokens, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.semanticTokens", tag.URI.Of(td.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.semanticTokens", label.URI.Of(td.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, td.URI)
diff --git a/gopls/internal/server/signature_help.go b/gopls/internal/server/signature_help.go
index 009b9d0..addcfe1 100644
--- a/gopls/internal/server/signature_help.go
+++ b/gopls/internal/server/signature_help.go
@@ -9,13 +9,13 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) SignatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.signatureHelp", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.signatureHelp", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
@@ -35,7 +35,7 @@
 		// that recently.
 		//
 		// It's unclear whether we still need to avoid returning this error result.
-		event.Error(ctx, "signature help failed", err, tag.Position.Of(params.Position))
+		event.Error(ctx, "signature help failed", err, label.Position.Of(params.Position))
 		return nil, nil
 	}
 	if info == nil {
diff --git a/gopls/internal/server/symbols.go b/gopls/internal/server/symbols.go
index 3442318..e35b2c7 100644
--- a/gopls/internal/server/symbols.go
+++ b/gopls/internal/server/symbols.go
@@ -9,14 +9,14 @@
 
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/template"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 func (s *server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]any, error) {
-	ctx, done := event.Start(ctx, "lsp.Server.documentSymbol", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.documentSymbol", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI)
diff --git a/gopls/internal/server/text_synchronization.go b/gopls/internal/server/text_synchronization.go
index 242dd7d..9ecd4f1 100644
--- a/gopls/internal/server/text_synchronization.go
+++ b/gopls/internal/server/text_synchronization.go
@@ -16,9 +16,9 @@
 	"golang.org/x/tools/gopls/internal/cache"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/golang"
+	"golang.org/x/tools/gopls/internal/label"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/internal/event"
-	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/xcontext"
 )
@@ -92,7 +92,7 @@
 }
 
 func (s *server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
-	ctx, done := event.Start(ctx, "lsp.Server.didOpen", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.didOpen", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	uri := params.TextDocument.URI
@@ -121,7 +121,7 @@
 }
 
 func (s *server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
-	ctx, done := event.Start(ctx, "lsp.Server.didChange", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.didChange", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	uri := params.TextDocument.URI
@@ -190,7 +190,7 @@
 }
 
 func (s *server) DidSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error {
-	ctx, done := event.Start(ctx, "lsp.Server.didSave", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.didSave", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	c := file.Modification{
@@ -204,7 +204,7 @@
 }
 
 func (s *server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
-	ctx, done := event.Start(ctx, "lsp.Server.didClose", tag.URI.Of(params.TextDocument.URI))
+	ctx, done := event.Start(ctx, "lsp.Server.didClose", label.URI.Of(params.TextDocument.URI))
 	defer done()
 
 	return s.didModifyFiles(ctx, []file.Modification{
diff --git a/internal/event/export/tag.go b/internal/event/export/labels.go
similarity index 100%
rename from internal/event/export/tag.go
rename to internal/event/export/labels.go
diff --git a/internal/event/tag/tag.go b/internal/event/tag/tag.go
deleted file mode 100644
index 581b26c..0000000
--- a/internal/event/tag/tag.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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 provides the labels used for telemetry throughout gopls.
-package tag
-
-import (
-	"golang.org/x/tools/internal/event/keys"
-)
-
-var (
-	// create the label keys we use
-	Method        = keys.NewString("method", "")
-	StatusCode    = keys.NewString("status.code", "")
-	StatusMessage = keys.NewString("status.message", "")
-	RPCID         = keys.NewString("id", "")
-	RPCDirection  = keys.NewString("direction", "")
-	File          = keys.NewString("file", "")
-	Directory     = keys.New("directory", "")
-	URI           = keys.New("URI", "")
-	Package       = keys.NewString("package", "") // sorted comma-separated list of Package IDs
-	PackagePath   = keys.NewString("package_path", "")
-	Query         = keys.New("query", "")
-	Snapshot      = keys.NewUInt64("snapshot", "")
-	Operation     = keys.NewString("operation", "")
-
-	Position     = keys.New("position", "")
-	Category     = keys.NewString("category", "")
-	PackageCount = keys.NewInt("packages", "")
-	Files        = keys.New("files", "")
-	Port         = keys.NewInt("port", "")
-	Type         = keys.New("type", "")
-	HoverKind    = keys.NewString("hoverkind", "")
-
-	NewServer = keys.NewString("new_server", "A new server was added")
-	EndServer = keys.NewString("end_server", "A server was shut down")
-
-	ServerID     = keys.NewString("server", "The server ID an event is related to")
-	Logfile      = keys.NewString("logfile", "")
-	DebugAddress = keys.NewString("debug_address", "")
-	GoplsPath    = keys.NewString("gopls_path", "")
-	ClientID     = keys.NewString("client_id", "")
-
-	Level = keys.NewInt("level", "The logging level")
-)
-
-var (
-	// create the stats we measure
-	Started       = keys.NewInt64("started", "Count of started RPCs.")
-	ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.")            //, unit.Bytes)
-	SentBytes     = keys.NewInt64("sent_bytes", "Bytes sent.")                    //, unit.Bytes)
-	Latency       = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds)
-)
-
-const (
-	Inbound  = "in"
-	Outbound = "out"
-)
diff --git a/internal/gocommand/invoke.go b/internal/gocommand/invoke.go
index f7de3c8..eb7a828 100644
--- a/internal/gocommand/invoke.go
+++ b/internal/gocommand/invoke.go
@@ -25,7 +25,6 @@
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // An Runner will run go command invocations and serialize
@@ -55,11 +54,14 @@
 // 1.14: go: updating go.mod: existing contents have changed since last read
 var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
 
-// verb is an event label for the go command verb.
-var verb = keys.NewString("verb", "go command verb")
+// event keys for go command invocations
+var (
+	verb      = keys.NewString("verb", "go command verb")
+	directory = keys.NewString("directory", "")
+)
 
 func invLabels(inv Invocation) []label.Label {
-	return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)}
+	return []label.Label{verb.Of(inv.Verb), directory.Of(inv.WorkingDir)}
 }
 
 // Run is a convenience wrapper around RunRaw.
diff --git a/internal/jsonrpc2/conn.go b/internal/jsonrpc2/conn.go
index 529cfa5..1d76ef9 100644
--- a/internal/jsonrpc2/conn.go
+++ b/internal/jsonrpc2/conn.go
@@ -13,7 +13,6 @@
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
 )
 
 // Conn is the common interface to jsonrpc clients and servers.
@@ -83,17 +82,17 @@
 		return fmt.Errorf("marshaling notify parameters: %v", err)
 	}
 	ctx, done := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
+		Method.Of(method),
+		RPCDirection.Of(Outbound),
 	)
 	defer func() {
 		recordStatus(ctx, err)
 		done()
 	}()
 
-	event.Metric(ctx, tag.Started.Of(1))
+	event.Metric(ctx, Started.Of(1))
 	n, err := c.write(ctx, notify)
-	event.Metric(ctx, tag.SentBytes.Of(n))
+	event.Metric(ctx, SentBytes.Of(n))
 	return err
 }
 
@@ -105,15 +104,15 @@
 		return id, fmt.Errorf("marshaling call parameters: %v", err)
 	}
 	ctx, done := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
-		tag.RPCID.Of(fmt.Sprintf("%q", id)),
+		Method.Of(method),
+		RPCDirection.Of(Outbound),
+		RPCID.Of(fmt.Sprintf("%q", id)),
 	)
 	defer func() {
 		recordStatus(ctx, err)
 		done()
 	}()
-	event.Metric(ctx, tag.Started.Of(1))
+	event.Metric(ctx, Started.Of(1))
 	// We have to add ourselves to the pending map before we send, otherwise we
 	// are racing the response. Also add a buffer to rchan, so that if we get a
 	// wire response between the time this call is cancelled and id is deleted
@@ -129,7 +128,7 @@
 	}()
 	// now we are ready to send
 	n, err := c.write(ctx, call)
-	event.Metric(ctx, tag.SentBytes.Of(n))
+	event.Metric(ctx, SentBytes.Of(n))
 	if err != nil {
 		// sending failed, we will never get a response, so don't leave it pending
 		return id, err
@@ -169,7 +168,7 @@
 			return err
 		}
 		n, err := c.write(ctx, response)
-		event.Metric(ctx, tag.SentBytes.Of(n))
+		event.Metric(ctx, SentBytes.Of(n))
 		if err != nil {
 			// TODO(iancottrell): if a stream write fails, we really need to shut down
 			// the whole stream
@@ -202,19 +201,19 @@
 		switch msg := msg.(type) {
 		case Request:
 			labels := []label.Label{
-				tag.Method.Of(msg.Method()),
-				tag.RPCDirection.Of(tag.Inbound),
+				Method.Of(msg.Method()),
+				RPCDirection.Of(Inbound),
 				{}, // reserved for ID if present
 			}
 			if call, ok := msg.(*Call); ok {
-				labels[len(labels)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID()))
+				labels[len(labels)-1] = RPCID.Of(fmt.Sprintf("%q", call.ID()))
 			} else {
 				labels = labels[:len(labels)-1]
 			}
 			reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...)
 			event.Metric(reqCtx,
-				tag.Started.Of(1),
-				tag.ReceivedBytes.Of(n))
+				Started.Of(1),
+				ReceivedBytes.Of(n))
 			if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil {
 				// delivery failed, not much we can do
 				event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
@@ -255,8 +254,8 @@
 
 func recordStatus(ctx context.Context, err error) {
 	if err != nil {
-		event.Label(ctx, tag.StatusCode.Of("ERROR"))
+		event.Label(ctx, StatusCode.Of("ERROR"))
 	} else {
-		event.Label(ctx, tag.StatusCode.Of("OK"))
+		event.Label(ctx, StatusCode.Of("OK"))
 	}
 }
diff --git a/internal/jsonrpc2/labels.go b/internal/jsonrpc2/labels.go
new file mode 100644
index 0000000..6da7c64
--- /dev/null
+++ b/internal/jsonrpc2/labels.go
@@ -0,0 +1,24 @@
+// Copyright 2024 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 jsonrpc2
+
+import "golang.org/x/tools/internal/event/keys"
+
+// These keys are used for creating labels to instrument jsonrpc2 events.
+var (
+	Method        = keys.NewString("method", "")
+	RPCID         = keys.NewString("id", "")
+	RPCDirection  = keys.NewString("direction", "")
+	Started       = keys.NewInt64("started", "Count of started RPCs.")
+	SentBytes     = keys.NewInt64("sent_bytes", "Bytes sent.")         //, unit.Bytes)
+	ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes)
+	StatusCode    = keys.NewString("status.code", "")
+	Latency       = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds)
+)
+
+const (
+	Inbound  = "in"
+	Outbound = "out"
+)
diff --git a/internal/jsonrpc2_v2/conn.go b/internal/jsonrpc2_v2/conn.go
index 04d1445..df885bf 100644
--- a/internal/jsonrpc2_v2/conn.go
+++ b/internal/jsonrpc2_v2/conn.go
@@ -17,7 +17,7 @@
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/keys"
 	"golang.org/x/tools/internal/event/label"
-	"golang.org/x/tools/internal/event/tag"
+	"golang.org/x/tools/internal/jsonrpc2"
 )
 
 // Binder builds a connection configuration.
@@ -262,8 +262,8 @@
 // be handed to the method invoked.
 func (c *Connection) Notify(ctx context.Context, method string, params interface{}) (err error) {
 	ctx, done := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
+		jsonrpc2.Method.Of(method),
+		jsonrpc2.RPCDirection.Of(jsonrpc2.Outbound),
 	)
 	attempted := false
 
@@ -300,7 +300,7 @@
 		return fmt.Errorf("marshaling notify parameters: %v", err)
 	}
 
-	event.Metric(ctx, tag.Started.Of(1))
+	event.Metric(ctx, jsonrpc2.Started.Of(1))
 	return c.write(ctx, notify)
 }
 
@@ -313,9 +313,9 @@
 	// Generate a new request identifier.
 	id := Int64ID(atomic.AddInt64(&c.seq, 1))
 	ctx, endSpan := event.Start(ctx, method,
-		tag.Method.Of(method),
-		tag.RPCDirection.Of(tag.Outbound),
-		tag.RPCID.Of(fmt.Sprintf("%q", id)),
+		jsonrpc2.Method.Of(method),
+		jsonrpc2.RPCDirection.Of(jsonrpc2.Outbound),
+		jsonrpc2.RPCID.Of(fmt.Sprintf("%q", id)),
 	)
 
 	ac := &AsyncCall{
@@ -349,7 +349,7 @@
 		return ac
 	}
 
-	event.Metric(ctx, tag.Started.Of(1))
+	event.Metric(ctx, jsonrpc2.Started.Of(1))
 	if err := c.write(ctx, call); err != nil {
 		// Sending failed. We will never get a response, so deliver a fake one if it
 		// wasn't already retired by the connection breaking.
@@ -539,16 +539,16 @@
 func (c *Connection) acceptRequest(ctx context.Context, msg *Request, msgBytes int64, preempter Preempter) {
 	// Add a span to the context for this request.
 	labels := append(make([]label.Label, 0, 3), // Make space for the ID if present.
-		tag.Method.Of(msg.Method),
-		tag.RPCDirection.Of(tag.Inbound),
+		jsonrpc2.Method.Of(msg.Method),
+		jsonrpc2.RPCDirection.Of(jsonrpc2.Inbound),
 	)
 	if msg.IsCall() {
-		labels = append(labels, tag.RPCID.Of(fmt.Sprintf("%q", msg.ID)))
+		labels = append(labels, jsonrpc2.RPCID.Of(fmt.Sprintf("%q", msg.ID)))
 	}
 	ctx, endSpan := event.Start(ctx, msg.Method, labels...)
 	event.Metric(ctx,
-		tag.Started.Of(1),
-		tag.ReceivedBytes.Of(msgBytes))
+		jsonrpc2.Started.Of(1),
+		jsonrpc2.ReceivedBytes.Of(msgBytes))
 
 	// In theory notifications cannot be cancelled, but we build them a cancel
 	// context anyway.
@@ -754,7 +754,7 @@
 	writer := <-c.writer
 	defer func() { c.writer <- writer }()
 	n, err := writer.Write(ctx, msg)
-	event.Metric(ctx, tag.SentBytes.Of(n))
+	event.Metric(ctx, jsonrpc2.SentBytes.Of(n))
 
 	if err != nil && ctx.Err() == nil {
 		// The call to Write failed, and since ctx.Err() is nil we can't attribute
@@ -794,9 +794,9 @@
 // labelStatus labels the status of the event in ctx based on whether err is nil.
 func labelStatus(ctx context.Context, err error) {
 	if err == nil {
-		event.Label(ctx, tag.StatusCode.Of("OK"))
+		event.Label(ctx, jsonrpc2.StatusCode.Of("OK"))
 	} else {
-		event.Label(ctx, tag.StatusCode.Of("ERROR"))
+		event.Label(ctx, jsonrpc2.StatusCode.Of("ERROR"))
 	}
 }