internal/frontend: track metrics for deps.dev URL fetch

Create a metric for what happens when we fetch a URL from
deps.dev: success, cancellation, timeout or other error.
This will let us determine if the problems we see in the log
are common.

Change-Id: Ife282a31c9ba5347b4c9ce8e91a480badbac6aa9
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/405541
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/cmd/frontend/main.go b/cmd/frontend/main.go
index 897a5b0..d197198 100644
--- a/cmd/frontend/main.go
+++ b/cmd/frontend/main.go
@@ -151,6 +151,7 @@
 		middleware.CacheErrorCount,
 		middleware.CacheLatency,
 		middleware.QuotaResultCount,
+		frontend.DepsDevResultCount,
 	)
 	if err := dcensus.Init(cfg, views...); err != nil {
 		log.Fatal(ctx, err)
diff --git a/internal/frontend/depsdev.go b/internal/frontend/depsdev.go
index b2a9fbb..99f05d2 100644
--- a/internal/frontend/depsdev.go
+++ b/internal/frontend/depsdev.go
@@ -13,7 +13,9 @@
 	"time"
 
 	"go.opencensus.io/plugin/ochttp"
-
+	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
+	"go.opencensus.io/tag"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/log"
 )
@@ -37,10 +39,16 @@
 	url := make(chan string, 1)
 	go func() {
 		u, err := fetchDepsDevURL(ctx, um.ModulePath, um.Version)
-		if err == context.Canceled {
+		switch {
+		case errors.Is(err, context.Canceled):
 			log.Warningf(ctx, "fetching url from deps.dev: %v", err)
-		} else if err != nil {
+			recordDepsDevMetric(ctx, "canceled")
+		case errors.Is(err, context.DeadlineExceeded):
+			log.Warningf(ctx, "fetching url from deps.dev: %v", err)
+			recordDepsDevMetric(ctx, "timeout")
+		case err != nil:
 			log.Errorf(ctx, "fetching url from deps.dev: %v", err)
+			recordDepsDevMetric(ctx, "error")
 		}
 		url <- u
 	}()
@@ -86,3 +94,29 @@
 	}
 	return depsDevBase + "/go/" + url.PathEscape(r.Name) + "/" + url.PathEscape(r.Version), nil
 }
+
+var (
+	keyDepsDevResult = tag.MustNewKey("depsdev.result")
+
+	depsDevResults = stats.Int64(
+		"go-discovery/depsdev_result_count",
+		"The result of fetching a deps.dev URL.",
+		stats.UnitDimensionless,
+	)
+
+	// DepsDevResultCount is a counter of results from fetching a URL from
+	// deps.dev: either OK, canceled, timeout, or error.
+	DepsDevResultCount = &view.View{
+		Name:        "go-discovery/depsdev/result_count",
+		Measure:     depsDevResults,
+		Aggregation: view.Count(),
+		Description: "deps.dev URL fetch results",
+		TagKeys:     []tag.Key{keyDepsDevResult},
+	}
+)
+
+func recordDepsDevMetric(ctx context.Context, kind string) {
+	stats.RecordWithTags(ctx, []tag.Mutator{
+		tag.Upsert(keyDepsDevResult, kind),
+	}, depsDevResults.M(1))
+}