all: add basic trace to gddo
This uses Stackdriver trace API to trace search, package requests
as well as background tasks. It also traces outbound requests to
VCS's and remote_api calls.
Change-Id: Ice82c1fbd267eefa4bad9d6674dc39ef10a1c874
Reviewed-on: https://go-review.googlesource.com/68472
Reviewed-by: Ross Light <light@google.com>
diff --git a/database/database.go b/database/database.go
index 4f112fb..b41392f 100644
--- a/database/database.go
+++ b/database/database.go
@@ -46,6 +46,7 @@
"time"
"unicode/utf8"
+ "cloud.google.com/go/trace"
"github.com/garyburd/redigo/redis"
"github.com/golang/snappy"
"golang.org/x/oauth2/google"
@@ -128,6 +129,7 @@
if err != nil {
return nil, err
}
+ client.Transport = trace.Transport{Base: client.Transport}
return remote_api.NewClient(host, client)
}
diff --git a/gddo-server/background.go b/gddo-server/background.go
index 47387ea..5167a67 100644
--- a/gddo-server/background.go
+++ b/gddo-server/background.go
@@ -11,10 +11,16 @@
"log"
"time"
+ "cloud.google.com/go/trace"
+
"github.com/golang/gddo/gosrc"
)
func (s *server) doCrawl(ctx context.Context) error {
+ span := s.traceClient.NewSpan("Crawl")
+ defer span.Finish()
+ ctx = trace.NewContext(ctx, span)
+
// Look for new package to crawl.
importPath, hasSubdirs, err := s.db.PopNewCrawl()
if err != nil {
@@ -49,6 +55,10 @@
}
func (s *server) readGitHubUpdates(ctx context.Context) error {
+ span := s.traceClient.NewSpan("GitHubUpdates")
+ defer span.Finish()
+ ctx = trace.NewContext(ctx, span)
+
const key = "gitHubUpdates"
var last string
if err := s.db.GetGob(key, &last); err != nil {
diff --git a/gddo-server/client.go b/gddo-server/client.go
index 1455ead..af557a6 100644
--- a/gddo-server/client.go
+++ b/gddo-server/client.go
@@ -13,6 +13,7 @@
"net"
"net/http"
+ "cloud.google.com/go/trace"
"github.com/gregjones/httpcache"
"github.com/gregjones/httpcache/memcache"
"github.com/spf13/viper"
@@ -42,7 +43,7 @@
}
return &http.Client{
// Wrap the cached transport with GitHub authentication.
- Transport: rt,
+ Transport: trace.Transport{Base: rt},
Timeout: requestTimeout,
}
}
diff --git a/gddo-server/config.go b/gddo-server/config.go
index 7c8e084..a1d15a8 100644
--- a/gddo-server/config.go
+++ b/gddo-server/config.go
@@ -50,6 +50,10 @@
ConfigDialTimeout = "dial_timeout"
ConfigRequestTimeout = "request_timeout"
ConfigMemcacheAddr = "memcache_addr"
+
+ // Trace Config
+ ConfigTraceSamplerFraction = "trace_fraction"
+ ConfigTraceSamplerMaxQPS = "trace_max_qps"
)
func loadConfig(ctx context.Context, args []string) (*viper.Viper, error) {
@@ -126,7 +130,6 @@
flags.StringP("config", "c", "", "path to motd config file")
flags.String(ConfigProject, "", "Google Cloud Platform project used for Google services")
- // TODO(stephenmw): flags.Bool("enable-admin-pages", false, "When true, enables /admin pages")
flags.Float64(ConfigRobotThreshold, 100, "Request counter threshold for robots.")
flags.String(ConfigAssetsDir, filepath.Join(defaultBase("github.com/golang/gddo/gddo-server"), "assets"), "Base directory for templates and static files.")
flags.Duration(ConfigGetTimeout, 8*time.Second, "Time to wait for package update from the VCS.")
@@ -146,6 +149,8 @@
flags.Bool(ConfigDBLog, false, "Log database commands")
flags.String(ConfigMemcacheAddr, "", "Address in the format host:port gddo uses to point to the memcache backend.")
flags.String(ConfigGAERemoteAPI, "", "Remoteapi endpoint for App Engine Search. Defaults to serviceproxy-dot-${project}.appspot.com.")
+ flags.Float64(ConfigTraceSamplerFraction, 0.1, "Fraction of the requests sampled by the trace API.")
+ flags.Float64(ConfigTraceSamplerMaxQPS, 5, "Max number of requests sampled every second by the trace API.")
return flags
}
diff --git a/gddo-server/main.go b/gddo-server/main.go
index e864409..0edaf8f 100644
--- a/gddo-server/main.go
+++ b/gddo-server/main.go
@@ -29,6 +29,7 @@
"time"
"cloud.google.com/go/logging"
+ "cloud.google.com/go/trace"
"github.com/spf13/viper"
"github.com/golang/gddo/database"
@@ -834,11 +835,12 @@
}
type server struct {
- v *viper.Viper
- db *database.Database
- httpClient *http.Client
- gceLogger *GCELogger
- templates templateMap
+ v *viper.Viper
+ db *database.Database
+ httpClient *http.Client
+ gceLogger *GCELogger
+ templates templateMap
+ traceClient *trace.Client
statusPNG http.Handler
statusSVG http.Handler
@@ -852,6 +854,18 @@
httpClient: newHTTPClient(v),
}
+ var err error
+ if proj := s.v.GetString(ConfigProject); proj != "" {
+ if s.traceClient, err = trace.NewClient(ctx, proj); err != nil {
+ return nil, err
+ }
+ sp, err := trace.NewLimitedSampler(s.v.GetFloat64(ConfigTraceSamplerFraction), s.v.GetFloat64(ConfigTraceSamplerMaxQPS))
+ if err != nil {
+ return nil, err
+ }
+ s.traceClient.SetSamplingPolicy(sp)
+ }
+
assets := v.GetString(ConfigAssetsDir)
staticServer := httputil.StaticServer{
Dir: assets,
@@ -919,17 +933,21 @@
mux.Handle("/BingSiteAuth.xml", staticServer.FileHandler("BingSiteAuth.xml"))
mux.Handle("/C", http.RedirectHandler("http://golang.org/doc/articles/c_go_cgo.html", http.StatusMovedPermanently))
mux.Handle("/code.jquery.com/", http.NotFoundHandler())
- mux.Handle("/_ah/health", http.HandlerFunc(serveHealthCheck))
- mux.Handle("/_ah/", http.NotFoundHandler())
mux.Handle("/", handler(s.serveHome))
+ ahMux := http.NewServeMux()
+ ahMux.HandleFunc("/_ah/health", serveHealthCheck)
+
+ mainMux := http.NewServeMux()
+ mainMux.Handle("/_ah/", ahMux)
+ mainMux.Handle("/", s.traceClient.HTTPHandler(mux))
+
s.root = rootHandler{
- {"api.", httpsRedirectHandler{apiMux}},
+ {"api.", httpsRedirectHandler{s.traceClient.HTTPHandler(apiMux)}},
{"talks.godoc.org", otherDomainHandler{"https", "go-talks.appspot.com"}},
- {"", httpsRedirectHandler{mux}},
+ {"", httpsRedirectHandler{mainMux}},
}
- var err error
cacheBusters := &httputil.CacheBusters{Handler: mux}
s.templates, err = parseTemplates(assets, cacheBusters, v)
if err != nil {
@@ -971,6 +989,10 @@
doc.SetDefaultGOOS(v.GetString(ConfigDefaultGOOS))
s, err := newServer(ctx, v)
+ if err != nil {
+ log.Fatal("error creating server:", err)
+ }
+
go func() {
for range time.Tick(s.v.GetDuration(ConfigCrawlInterval)) {
if err := s.doCrawl(ctx); err != nil {
diff --git a/gosrc/client.go b/gosrc/client.go
index 2f4663f..e6a4bf9 100644
--- a/gosrc/client.go
+++ b/gosrc/client.go
@@ -38,6 +38,8 @@
return nil, err
}
+ // Take the trace ID to group all outbound requests together.
+ req = req.WithContext(ctx)
for k, vs := range c.header {
req.Header[k] = vs
}