Skip to content

Commit 3398ffb

Browse files
committed
all: instrument build, vet, and run time
This change adds a latency distribution for the build&vet stage, the vet stage, and the run stage of a snippet handled by the compile handler. For golang/go#44822 Change-Id: Icedce87492afadd6041efb05e6f0ed3cd12a01ba Reviewed-on: https://go-review.googlesource.com/c/playground/+/302770 Trust: Alexander Rakoczy <alex@golang.org> Run-TryBot: Alexander Rakoczy <alex@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Carlos Amedee <carlos@golang.org>
1 parent d1c0810 commit 3398ffb

File tree

6 files changed

+160
-7
lines changed

6 files changed

+160
-7
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
golang.org/x/mod v0.2.0
1616
golang.org/x/tools v0.0.0-20200420001825-978e26b7c37c
1717
google.golang.org/api v0.20.0
18+
google.golang.org/appengine v1.6.5 // indirect
1819
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672
1920
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
2021
gopkg.in/yaml.v2 v2.2.7 // indirect

internal/metrics/service.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package metrics
88

99
import (
10+
"context"
1011
"errors"
1112
"fmt"
1213
"net/http"
@@ -17,6 +18,7 @@ import (
1718
"contrib.go.opencensus.io/exporter/prometheus"
1819
"contrib.go.opencensus.io/exporter/stackdriver"
1920
"go.opencensus.io/stats/view"
21+
"google.golang.org/appengine"
2022
mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
2123
)
2224

@@ -138,6 +140,30 @@ func GCEResource(jobName string) (*MonitoredResource, error) {
138140
}), nil
139141
}
140142

143+
// GAEResource returns a *MonitoredResource with fields populated and
144+
// for StackDriver.
145+
//
146+
// The resource will be in StackDrvier's gae_instance type.
147+
func GAEResource(ctx context.Context) (*MonitoredResource, error) {
148+
if !appengine.IsAppEngine() {
149+
return nil, fmt.Errorf("not running on appengine")
150+
}
151+
projID, err := metadata.ProjectID()
152+
if err != nil {
153+
return nil, err
154+
}
155+
return (*MonitoredResource)(&mrpb.MonitoredResource{
156+
Type: "gae_instance",
157+
Labels: map[string]string{
158+
"project_id": projID,
159+
"module_id": appengine.ModuleName(ctx),
160+
"version_id": appengine.VersionID(ctx),
161+
"instance_id": appengine.InstanceID(),
162+
"location": appengine.Datacenter(ctx),
163+
},
164+
}), nil
165+
}
166+
141167
// instanceGroupName fetches the instanceGroupName from the instance
142168
// metadata.
143169
//

metrics.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"go.opencensus.io/stats"
9+
"go.opencensus.io/stats/view"
10+
"go.opencensus.io/tag"
11+
)
12+
13+
var (
14+
BuildLatencyDistribution = view.Distribution(1, 5, 10, 15, 20, 25, 50, 75, 100, 125, 150, 200, 250, 300, 400, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 7000, 8000, 9000, 10000, 20000, 30000)
15+
kGoBuildSuccess = tag.MustNewKey("go-playground/frontend/go_build_success")
16+
kGoRunSuccess = tag.MustNewKey("go-playground/frontend/go_run_success")
17+
kGoVetSuccess = tag.MustNewKey("go-playground/frontend/go_vet_success")
18+
mGoBuildLatency = stats.Float64("go-playground/frontend/go_build_latency", "", stats.UnitMilliseconds)
19+
mGoRunLatency = stats.Float64("go-playground/frontend/go_run_latency", "", stats.UnitMilliseconds)
20+
mGoVetLatency = stats.Float64("go-playground/frontend/go_vet_latency", "", stats.UnitMilliseconds)
21+
22+
goBuildCount = &view.View{
23+
Name: "go-playground/frontend/go_build_count",
24+
Description: "Number of snippets built",
25+
Measure: mGoBuildLatency,
26+
TagKeys: []tag.Key{kGoBuildSuccess},
27+
Aggregation: view.Count(),
28+
}
29+
goBuildLatency = &view.View{
30+
Name: "go-playground/frontend/go_build_latency",
31+
Description: "Latency distribution of building snippets",
32+
Measure: mGoBuildLatency,
33+
Aggregation: BuildLatencyDistribution,
34+
}
35+
goRunCount = &view.View{
36+
Name: "go-playground/frontend/go_run_count",
37+
Description: "Number of snippets run",
38+
Measure: mGoRunLatency,
39+
TagKeys: []tag.Key{kGoRunSuccess},
40+
Aggregation: view.Count(),
41+
}
42+
goRunLatency = &view.View{
43+
Name: "go-playground/frontend/go_run_latency",
44+
Description: "Latency distribution of running snippets",
45+
Measure: mGoRunLatency,
46+
Aggregation: BuildLatencyDistribution,
47+
}
48+
goVetCount = &view.View{
49+
Name: "go-playground/frontend/go_vet_count",
50+
Description: "Number of vet runs",
51+
Measure: mGoVetLatency,
52+
TagKeys: []tag.Key{kGoVetSuccess},
53+
Aggregation: view.Count(),
54+
}
55+
goVetLatency = &view.View{
56+
Name: "go-playground/sandbox/go_vet_latency",
57+
Description: "Latency distribution of vet runs",
58+
Measure: mGoVetLatency,
59+
Aggregation: BuildLatencyDistribution,
60+
}
61+
)
62+
63+
// views should contain all measurements. All *view.View added to this
64+
// slice will be registered and exported to the metric service.
65+
var views = []*view.View{
66+
goBuildCount,
67+
goBuildLatency,
68+
goRunCount,
69+
goRunLatency,
70+
goVetCount,
71+
goVetLatency,
72+
}

sandbox.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import (
3636

3737
"cloud.google.com/go/compute/metadata"
3838
"github.com/bradfitz/gomemcache/memcache"
39+
"go.opencensus.io/stats"
40+
"go.opencensus.io/tag"
3941
"golang.org/x/playground/internal"
4042
"golang.org/x/playground/internal/gcpdial"
4143
"golang.org/x/playground/sandbox/sandboxtypes"
@@ -415,13 +417,25 @@ func (b *buildResult) cleanup() error {
415417
// sandboxBuild builds a Go program and returns a build result that includes the build context.
416418
//
417419
// An error is returned if a non-user-correctable error has occurred.
418-
func sandboxBuild(ctx context.Context, tmpDir string, in []byte, vet bool) (*buildResult, error) {
420+
func sandboxBuild(ctx context.Context, tmpDir string, in []byte, vet bool) (br *buildResult, err error) {
421+
start := time.Now()
422+
defer func() {
423+
status := "success"
424+
if err != nil {
425+
status = "error"
426+
}
427+
// Ignore error. The only error can be invalid tag key or value
428+
// length, which we know are safe.
429+
stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(kGoBuildSuccess, status)},
430+
mGoBuildLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
431+
}()
432+
419433
files, err := splitFiles(in)
420434
if err != nil {
421435
return &buildResult{errorMessage: err.Error()}, nil
422436
}
423437

424-
br := new(buildResult)
438+
br = new(buildResult)
425439
defer br.cleanup()
426440
var buildPkgArg = "."
427441
if files.Num() == 1 && len(files.Data(progName)) > 0 {
@@ -515,7 +529,7 @@ func sandboxBuild(ctx context.Context, tmpDir string, in []byte, vet bool) (*bui
515529
}
516530
if vet {
517531
// TODO: do this concurrently with the execution to reduce latency.
518-
br.vetOut, err = vetCheckInDir(tmpDir, br.goPath)
532+
br.vetOut, err = vetCheckInDir(ctx, tmpDir, br.goPath)
519533
if err != nil {
520534
return nil, fmt.Errorf("running vet: %v", err)
521535
}
@@ -524,8 +538,18 @@ func sandboxBuild(ctx context.Context, tmpDir string, in []byte, vet bool) (*bui
524538
}
525539

526540
// sandboxRun runs a Go binary in a sandbox environment.
527-
func sandboxRun(ctx context.Context, exePath string, testParam string) (sandboxtypes.Response, error) {
528-
var execRes sandboxtypes.Response
541+
func sandboxRun(ctx context.Context, exePath string, testParam string) (execRes sandboxtypes.Response, err error) {
542+
start := time.Now()
543+
defer func() {
544+
status := "success"
545+
if err != nil {
546+
status = "error"
547+
}
548+
// Ignore error. The only error can be invalid tag key or value
549+
// length, which we know are safe.
550+
stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(kGoBuildSuccess, status)},
551+
mGoRunLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
552+
}()
529553
exeBytes, err := ioutil.ReadFile(exePath)
530554
if err != nil {
531555
return execRes, err

server.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package main
66

77
import (
8+
"context"
89
"fmt"
910
"net/http"
1011
"os"
1112
"strings"
1213
"time"
1314

15+
"cloud.google.com/go/compute/metadata"
16+
"golang.org/x/playground/internal/metrics"
1417
"golang.org/x/tools/godoc/static"
1518
)
1619

@@ -43,6 +46,17 @@ func newServer(options ...func(s *server) error) (*server, error) {
4346
s.modtime = fi.ModTime()
4447
}
4548
}
49+
gr, err := metrics.GAEResource(context.Background())
50+
if err != nil {
51+
s.log.Printf("metrics.GaeService() = _, %q", err)
52+
}
53+
ms, err := metrics.NewService(gr, views)
54+
if err != nil {
55+
s.log.Printf("Failed to initialize metrics: metrics.NewService() = _, %q. (not on GCP?)", err)
56+
}
57+
if ms != nil && !metadata.OnGCE() {
58+
s.mux.Handle("/statusz", ms)
59+
}
4660
s.init()
4761
return s, nil
4862
}

vet.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
"os/exec"
1313
"path/filepath"
1414
"strings"
15+
"time"
16+
17+
"go.opencensus.io/stats"
18+
"go.opencensus.io/tag"
1519
)
1620

1721
// vetCheck runs the "vet" tool on the source code in req.Body.
@@ -33,7 +37,7 @@ func vetCheck(ctx context.Context, req *request) (*response, error) {
3337
if err := ioutil.WriteFile(in, []byte(req.Body), 0400); err != nil {
3438
return nil, fmt.Errorf("error creating temp file %q: %v", in, err)
3539
}
36-
vetOutput, err := vetCheckInDir(tmpDir, os.Getenv("GOPATH"))
40+
vetOutput, err := vetCheckInDir(ctx, tmpDir, os.Getenv("GOPATH"))
3741
if err != nil {
3842
// This is about errors running vet, not vet returning output.
3943
return nil, err
@@ -46,7 +50,19 @@ func vetCheck(ctx context.Context, req *request) (*response, error) {
4650
// go vet was able to run, not whether vet reported problem. The
4751
// returned value is ("", nil) if vet successfully found nothing,
4852
// and (non-empty, nil) if vet ran and found issues.
49-
func vetCheckInDir(dir, goPath string) (output string, execErr error) {
53+
func vetCheckInDir(ctx context.Context, dir, goPath string) (output string, execErr error) {
54+
start := time.Now()
55+
defer func() {
56+
status := "success"
57+
if execErr != nil {
58+
status = "error"
59+
}
60+
// Ignore error. The only error can be invalid tag key or value
61+
// length, which we know are safe.
62+
stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(kGoVetSuccess, status)},
63+
mGoVetLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
64+
}()
65+
5066
cmd := exec.Command("go", "vet")
5167
cmd.Dir = dir
5268
// Linux go binary is not built with CGO_ENABLED=0.

0 commit comments

Comments
 (0)