diff --git a/CHANGELOG.md b/CHANGELOG.md index f32624de7732..f1aac524dc4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,22 @@ All notable changes to Sourcegraph are documented in this file. ### Added - Users and site administrators can now view a log of their actions/events in the user settings. +- Distributed tracing is a powerful tool for investigating performance issues. The following changes have been made with the goal of making it easier to use distributed tracing with Sourcegraph: + + - The site configuration field `"observability.tracing": { "sampling": "..." }` allows a site admin to control which requests generate tracing data. + - `"all"` will trace all requests. + - `"selective"` (recommended) will trace all requests initiated from an end-user URL with `?trace=1`. Non-end-user-initiated requests can set a HTTP header `X-Sourcegraph-Should-Trace: true`. This is the recommended setting, as `"all"` can generate large amounts of tracing data that may cause network and memory resource contention in the Sourcegraph instance. + - `"none"` (default) turns off tracing. + - Jaeger is now the officially supported distributed tracer. The following is the recommended site configuration to connect Sourcegraph to a Jaeger agent (which must be deployed on the same host and listening on the default ports): + + ``` + "observability.tracing": { + "sampling": "selective" + } + ``` + + - The site configuration field, `useJaeger`, is deprecated in favor of `observability.tracing`. + - Support for configuring Lightstep as a distributed tracer is deprecated and will be removed in a subsequent release. Instances that use Lightstep with Sourcegraph are encouraged to migrate to Jaeger (directions for running Jaeger alongside Sourcegraph are included in the installation instructions). ### Changed diff --git a/cmd/frontend/backend/mocks.go b/cmd/frontend/backend/mocks.go index a7437ed23348..8975994647eb 100644 --- a/cmd/frontend/backend/mocks.go +++ b/cmd/frontend/backend/mocks.go @@ -3,10 +3,9 @@ package backend import ( "context" - opentracing "github.com/opentracing/opentracing-go" - "github.com/sourcegraph/sourcegraph/cmd/frontend/db" "github.com/sourcegraph/sourcegraph/internal/actor" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" ) @@ -24,7 +23,7 @@ func testContext() context.Context { ctx := context.Background() ctx = actor.WithActor(ctx, &actor.Actor{UID: 1}) - _, ctx = opentracing.StartSpanFromContext(ctx, "dummy") + _, ctx = ot.StartSpanFromContext(ctx, "dummy") return ctx } diff --git a/cmd/frontend/backend/trace.go b/cmd/frontend/backend/trace.go index dd827216d749..9ca46dbe865d 100644 --- a/cmd/frontend/backend/trace.go +++ b/cmd/frontend/backend/trace.go @@ -7,11 +7,11 @@ import ( "time" "github.com/inconshreveable/log15" - opentracing "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/internal/actor" tracepkg "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var metricLabels = []string{"method", "success"} @@ -38,7 +38,7 @@ func init() { func trace(ctx context.Context, server, method string, arg interface{}, err *error) (context.Context, func()) { requestGauge.WithLabelValues(server + "." + method).Inc() - span, ctx := opentracing.StartSpanFromContext(ctx, server+"."+method) + span, ctx := ot.StartSpanFromContext(ctx, server+"."+method) span.SetTag("Server", server) span.SetTag("Method", method) span.SetTag("Argument", fmt.Sprintf("%#v", arg)) diff --git a/cmd/frontend/graphqlbackend/codemod.go b/cmd/frontend/graphqlbackend/codemod.go index 74724b8ac13b..48b05c24a0d3 100644 --- a/cmd/frontend/graphqlbackend/codemod.go +++ b/cmd/frontend/graphqlbackend/codemod.go @@ -13,7 +13,6 @@ import ( "github.com/inconshreveable/log15" "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" "github.com/sourcegraph/go-diff/diff" @@ -24,6 +23,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/search" "github.com/sourcegraph/sourcegraph/internal/search/query" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" "golang.org/x/net/context/ctxhttp" ) @@ -259,7 +259,7 @@ func callCodemodInRepo(ctx context.Context, repoRevs *search.RepositoryRevisions } req = req.WithContext(ctx) - req, ht := nethttp.TraceRequest(opentracing.GlobalTracer(), req, + req, ht := nethttp.TraceRequest(ot.GetTracer(ctx), req, nethttp.OperationName("Codemod client"), nethttp.ClientTrace(false)) defer ht.Finish() diff --git a/cmd/frontend/graphqlbackend/externallink/repository.go b/cmd/frontend/graphqlbackend/externallink/repository.go index 8ad465f26258..1d977b50c90e 100644 --- a/cmd/frontend/graphqlbackend/externallink/repository.go +++ b/cmd/frontend/graphqlbackend/externallink/repository.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/inconshreveable/log15" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/cmd/frontend/backend" @@ -18,6 +17,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/errcode" "github.com/sourcegraph/sourcegraph/internal/repoupdater" "github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" ) @@ -104,7 +104,7 @@ func Commit(ctx context.Context, repo *types.Repo, commitID api.CommitID) (links // It logs errors to the trace but does not return errors, because external links are not worth // failing any request for. func linksForRepository(ctx context.Context, repo *types.Repo) (phabRepo *types.PhabricatorRepo, link *protocol.RepoLinks, serviceType string) { - span, ctx := opentracing.StartSpanFromContext(ctx, "externallink.linksForRepository") + span, ctx := ot.StartSpanFromContext(ctx, "externallink.linksForRepository") defer span.Finish() span.SetTag("Repo", repo.Name) span.SetTag("ExternalRepo", repo.ExternalRepo) diff --git a/cmd/frontend/graphqlbackend/graphqlbackend.go b/cmd/frontend/graphqlbackend/graphqlbackend.go index 4ff13b999073..b3a424de6c5c 100644 --- a/cmd/frontend/graphqlbackend/graphqlbackend.go +++ b/cmd/frontend/graphqlbackend/graphqlbackend.go @@ -20,6 +20,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/errcode" sgtrace "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var graphqlFieldHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{ @@ -48,6 +49,10 @@ type prometheusTracer struct { } func (prometheusTracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, trace.TraceQueryFinishFunc) { + if !ot.ShouldTrace(ctx) { + return ctx, trace.TraceQueryFinishFunc(func([]*gqlerrors.QueryError) {}) + } + traceCtx, finish := trace.OpenTracingTracer{}.TraceQuery(ctx, queryString, operationName, variables, varTypes) // Note: We don't care about the error here, we just extract the username if @@ -85,6 +90,10 @@ VARIABLES } func (prometheusTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, trace.TraceFieldFinishFunc) { + if !ot.ShouldTrace(ctx) { + return ctx, trace.TraceFieldFinishFunc(func(*gqlerrors.QueryError) {}) + } + traceCtx, finish := trace.OpenTracingTracer{}.TraceField(ctx, label, typeName, fieldName, trivial, args) start := time.Now() return traceCtx, func(err *gqlerrors.QueryError) { diff --git a/cmd/frontend/graphqlbackend/search_results.go b/cmd/frontend/graphqlbackend/search_results.go index 262180994488..5db7dd05ce4d 100644 --- a/cmd/frontend/graphqlbackend/search_results.go +++ b/cmd/frontend/graphqlbackend/search_results.go @@ -39,6 +39,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/search" "github.com/sourcegraph/sourcegraph/internal/search/query" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" ) @@ -360,7 +361,7 @@ func (sf *searchFilterResolver) Kind() string { // blameFileMatch blames the specified file match to produce the time at which // the first line match inside of it was authored. func (sr *SearchResultsResolver) blameFileMatch(ctx context.Context, fm *FileMatchResolver) (t time.Time, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "blameFileMatch") + span, ctx := ot.StartSpanFromContext(ctx, "blameFileMatch") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/cmd/frontend/graphqlbackend/search_symbols.go b/cmd/frontend/graphqlbackend/search_symbols.go index 07e0d7f3b84b..af93628a780a 100644 --- a/cmd/frontend/graphqlbackend/search_symbols.go +++ b/cmd/frontend/graphqlbackend/search_symbols.go @@ -12,7 +12,6 @@ import ( "github.com/google/zoekt" "github.com/inconshreveable/log15" "github.com/neelance/parallel" - "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" @@ -28,6 +27,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/search/query" "github.com/sourcegraph/sourcegraph/internal/symbols/protocol" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" ) @@ -252,7 +252,7 @@ func symbolCount(fmrs []*FileMatchResolver) int { } func searchSymbolsInRepo(ctx context.Context, repoRevs *search.RepositoryRevisions, patternInfo *search.TextPatternInfo, query query.QueryInfo, limit int) (res []*FileMatchResolver, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Search symbols in repo") + span, ctx := ot.StartSpanFromContext(ctx, "Search symbols in repo") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/cmd/frontend/graphqlbackend/textsearch.go b/cmd/frontend/graphqlbackend/textsearch.go index 7f5da27c9b11..e40397aa63d5 100644 --- a/cmd/frontend/graphqlbackend/textsearch.go +++ b/cmd/frontend/graphqlbackend/textsearch.go @@ -17,9 +17,9 @@ import ( "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/metrics" + "github.com/sourcegraph/sourcegraph/internal/trace" "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" otlog "github.com/opentracing/opentracing-go/log" "github.com/inconshreveable/log15" @@ -32,7 +32,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/search" "github.com/sourcegraph/sourcegraph/internal/search/query" querytypes "github.com/sourcegraph/sourcegraph/internal/search/query/types" - "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/git" ) @@ -43,8 +43,8 @@ var ( requestCounter = metrics.NewRequestMeter("textsearch", "Total number of requests sent to the textsearch API.") searchHTTPClient = &http.Client{ - // nethttp.Transport will propagate opentracing spans - Transport: &nethttp.Transport{ + // ot.Transport will propagate opentracing spans + Transport: &ot.Transport{ RoundTripper: requestCounter.Transport(&http.Transport{ // Default is 2, but we can send many concurrent requests MaxIdleConnsPerHost: 500, @@ -302,7 +302,7 @@ func textSearchURL(ctx context.Context, url string) ([]*FileMatchResolver, bool, } req = req.WithContext(ctx) - req, ht := nethttp.TraceRequest(opentracing.GlobalTracer(), req, + req, ht := nethttp.TraceRequest(ot.GetTracer(ctx), req, nethttp.OperationName("Searcher Client"), nethttp.ClientTrace(false)) defer ht.Finish() diff --git a/cmd/frontend/internal/app/jscontext/jscontext.go b/cmd/frontend/internal/app/jscontext/jscontext.go index 0722d7edf5fb..667fdb64c9d6 100644 --- a/cmd/frontend/internal/app/jscontext/jscontext.go +++ b/cmd/frontend/internal/app/jscontext/jscontext.go @@ -91,13 +91,6 @@ func NewJSContextFromRequest(req *http.Request) JSContext { headers["x-sourcegraph-client"] = globals.ExternalURL().String() headers["X-Requested-With"] = "Sourcegraph" // required for httpapi to use cookie auth - // -- currently we don't associate XHR calls with the parent page's span -- - // if span := opentracing.SpanFromContext(req.Context()); span != nil { - // if err := opentracing.GlobalTracer().Inject(span.Context(), opentracing.HTTPHeaders, opentracing.TextMapCarrier(headers)); err != nil { - // return JSContext{}, err - // } - // } - // Propagate Cache-Control no-cache and max-age=0 directives // to the requests made by our client-side JavaScript. This is // not a perfect parser, but it catches the important cases. diff --git a/cmd/frontend/internal/app/ui/landing.go b/cmd/frontend/internal/app/ui/landing.go index 599e4aa78f9a..d761a1489e40 100644 --- a/cmd/frontend/internal/app/ui/landing.go +++ b/cmd/frontend/internal/app/ui/landing.go @@ -6,13 +6,13 @@ import ( "github.com/inconshreveable/log15" "github.com/gorilla/mux" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/cmd/frontend/internal/pkg/handlerutil" "github.com/sourcegraph/sourcegraph/internal/errcode" "github.com/sourcegraph/sourcegraph/internal/lazyregexp" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var goSymbolReg = lazyregexp.New("/info/GoPackage/(.+)$") @@ -34,7 +34,7 @@ func serveRepoLanding(w http.ResponseWriter, r *http.Request) error { } func serveDefLanding(w http.ResponseWriter, r *http.Request) (err error) { - span, ctx := opentracing.StartSpanFromContext(r.Context(), "serveDefLanding") + span, ctx := ot.StartSpanFromContext(r.Context(), "serveDefLanding") r = r.WithContext(ctx) defer func() { if err != nil { diff --git a/cmd/frontend/internal/cli/http.go b/cmd/frontend/internal/cli/http.go index 02c83529eace..1ab3c8a00f0b 100644 --- a/cmd/frontend/internal/cli/http.go +++ b/cmd/frontend/internal/cli/http.go @@ -23,6 +23,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/actor" "github.com/sourcegraph/sourcegraph/internal/conf" tracepkg "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/version" ) @@ -73,7 +74,8 @@ func newExternalHTTPHandler(schema *graphql.Schema, githubWebhook, bitbucketServ // 🚨 SECURITY: Auth middleware that must run before other auth middlewares. h = internalauth.OverrideAuthMiddleware(h) h = internalauth.ForbidAllRequestsMiddleware(h) - h = tracepkg.Middleware(h) + h = tracepkg.HTTPTraceMiddleware(h) + h = ot.Middleware(h) h = middleware.SourcegraphComGoGetHandler(h) h = middleware.BlackHole(h) h = secureHeadersMiddleware(h) @@ -165,7 +167,7 @@ func secureHeadersMiddleware(next http.Handler) http.Handler { if r.Method == "OPTIONS" { w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", corsAllowHeader+", X-Sourcegraph-Client, Content-Type, Authorization") + w.Header().Set("Access-Control-Allow-Headers", corsAllowHeader+", X-Sourcegraph-Client, Content-Type, Authorization, X-Sourcegraph-Should-Trace") w.WriteHeader(http.StatusOK) return // do not invoke next handler } diff --git a/cmd/gitserver/main.go b/cmd/gitserver/main.go index a38c14f90c00..9397543108aa 100644 --- a/cmd/gitserver/main.go +++ b/cmd/gitserver/main.go @@ -15,12 +15,11 @@ import ( "github.com/pkg/errors" "github.com/inconshreveable/log15" - "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/cmd/gitserver/server" "github.com/sourcegraph/sourcegraph/internal/debugserver" "github.com/sourcegraph/sourcegraph/internal/env" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/tracer" ) @@ -63,7 +62,7 @@ func main() { } // Create Handler now since it also initializes state - handler := nethttp.Middleware(opentracing.GlobalTracer(), gitserver.Handler()) + handler := ot.Middleware(gitserver.Handler()) go debugserver.Start() diff --git a/cmd/gitserver/server/server.go b/cmd/gitserver/server/server.go index da414853a8c2..71168dd5fb96 100644 --- a/cmd/gitserver/server/server.go +++ b/cmd/gitserver/server/server.go @@ -26,7 +26,6 @@ import ( "time" "github.com/inconshreveable/log15" - "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" @@ -40,6 +39,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/mutablelimiter" "github.com/sourcegraph/sourcegraph/internal/repotrackutil" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // tempDirName is the name used for the temporary directory under ReposDir. @@ -80,7 +80,7 @@ func runCommand(ctx context.Context, cmd *exec.Cmd) (exitCode int, err error) { if runCommandMock != nil { return runCommandMock(ctx, cmd) } - span, _ := opentracing.StartSpanFromContext(ctx, "runCommand") + span, _ := ot.StartSpanFromContext(ctx, "runCommand") span.SetTag("path", cmd.Path) span.SetTag("args", cmd.Args) span.SetTag("dir", cmd.Dir) @@ -1048,7 +1048,7 @@ func init() { var headBranchPattern = lazyregexp.New(`HEAD branch: (.+?)\n`) func (s *Server) doRepoUpdate(ctx context.Context, repo api.RepoName, url string) error { - span, ctx := opentracing.StartSpanFromContext(ctx, "Server.doRepoUpdate") + span, ctx := ot.StartSpanFromContext(ctx, "Server.doRepoUpdate") span.SetTag("repo", repo) span.SetTag("url", url) defer span.Finish() diff --git a/cmd/replacer/main.go b/cmd/replacer/main.go index cb37ca73ba07..0b99836abcb5 100644 --- a/cmd/replacer/main.go +++ b/cmd/replacer/main.go @@ -15,8 +15,6 @@ import ( "time" "github.com/inconshreveable/log15" - "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/cmd/replacer/replace" "github.com/sourcegraph/sourcegraph/internal/api" @@ -24,6 +22,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/store" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/tracer" ) @@ -61,7 +60,7 @@ func main() { Store: &store, Log: log15.Root(), } - handler := nethttp.Middleware(opentracing.GlobalTracer(), service) + handler := ot.Middleware(service) host := "" if env.InsecureDev { diff --git a/cmd/replacer/replace/replace.go b/cmd/replacer/replace/replace.go index 0647e3787b66..caa839b19db0 100644 --- a/cmd/replacer/replace/replace.go +++ b/cmd/replacer/replace/replace.go @@ -25,16 +25,16 @@ import ( "strings" "time" - "golang.org/x/net/trace" + nettrace "golang.org/x/net/trace" "github.com/inconshreveable/log15" - "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/cmd/replacer/protocol" "github.com/sourcegraph/sourcegraph/internal/store" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/gorilla/schema" ) @@ -132,10 +132,10 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (s *Service) replace(ctx context.Context, p *protocol.Request, w http.ResponseWriter, r *http.Request) (deadlineHit bool, err error) { - tr := trace.New("replace", fmt.Sprintf("%s@%s", p.Repo, p.Commit)) + tr := nettrace.New("replace", fmt.Sprintf("%s@%s", p.Repo, p.Commit)) tr.LazyPrintf("%s", p.RewriteSpecification) - span, ctx := opentracing.StartSpanFromContext(ctx, "Replace") + span, ctx := ot.StartSpanFromContext(ctx, "Replace") ext.Component.Set(span, "service") span.SetTag("repo", p.Repo) span.SetTag("url", p.URL) diff --git a/cmd/repo-updater/repoupdater/observability.go b/cmd/repo-updater/repoupdater/observability.go index aff68d703055..54726927e805 100644 --- a/cmd/repo-updater/repoupdater/observability.go +++ b/cmd/repo-updater/repoupdater/observability.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // HandlerMetrics encapsulates the Prometheus metrics of an http.Handler. @@ -53,12 +54,11 @@ func ObservedHandler( tr opentracing.Tracer, ) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { - return nethttp.Middleware(tr, + return ot.MiddlewareWithTracer(tr, &observedHandler{ next: next, log: log, metrics: m, - tracer: tr, }, nethttp.OperationNameFunc(func(r *http.Request) string { return "HTTP " + r.Method + ":" + r.URL.Path @@ -75,7 +75,6 @@ type observedHandler struct { next http.Handler log log15.Logger metrics HandlerMetrics - tracer opentracing.Tracer } func (h *observedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/repo-updater/repoupdater/server_test.go b/cmd/repo-updater/repoupdater/server_test.go index 609d2d48fbef..f8abf7b9ac6d 100644 --- a/cmd/repo-updater/repoupdater/server_test.go +++ b/cmd/repo-updater/repoupdater/server_test.go @@ -37,7 +37,7 @@ func TestServer_handleRepoLookup(t *testing.T) { h := ObservedHandler( log15.Root(), NewHandlerMetrics(), - opentracing.GlobalTracer(), + opentracing.NoopTracer{}, )(s.Handler()) repoLookup := func(t *testing.T, repo api.RepoName) (resp *protocol.RepoLookupResult, statusCode int) { diff --git a/cmd/searcher/main.go b/cmd/searcher/main.go index b73929f3b558..41a4a3c8c902 100644 --- a/cmd/searcher/main.go +++ b/cmd/searcher/main.go @@ -15,8 +15,6 @@ import ( "time" "github.com/inconshreveable/log15" - "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/cmd/searcher/search" "github.com/sourcegraph/sourcegraph/internal/api" @@ -24,6 +22,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/store" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/tracer" ) @@ -59,7 +58,7 @@ func main() { } service.Store.SetMaxConcurrentFetchTar(10) service.Store.Start() - handler := nethttp.Middleware(opentracing.GlobalTracer(), service) + handler := ot.Middleware(service) host := "" if env.InsecureDev { diff --git a/cmd/searcher/search/search.go b/cmd/searcher/search/search.go index 160d1e0ab238..2109a68b5363 100644 --- a/cmd/searcher/search/search.go +++ b/cmd/searcher/search/search.go @@ -21,15 +21,15 @@ import ( "time" "github.com/inconshreveable/log15" - "golang.org/x/net/trace" "github.com/sourcegraph/sourcegraph/cmd/searcher/protocol" "github.com/sourcegraph/sourcegraph/internal/store" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" + nettrace "golang.org/x/net/trace" "github.com/pkg/errors" "github.com/gorilla/schema" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/prometheus/client_golang/prometheus" @@ -131,10 +131,10 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (s *Service) search(ctx context.Context, p *protocol.Request) (matches []protocol.FileMatch, limitHit, deadlineHit bool, err error) { - tr := trace.New("search", fmt.Sprintf("%s@%s", p.Repo, p.Commit)) + tr := nettrace.New("search", fmt.Sprintf("%s@%s", p.Repo, p.Commit)) tr.LazyPrintf("%s", p.Pattern) - span, ctx := opentracing.StartSpanFromContext(ctx, "Search") + span, ctx := ot.StartSpanFromContext(ctx, "Search") ext.Component.Set(span, "service") span.SetTag("repo", p.Repo) span.SetTag("url", p.URL) diff --git a/cmd/searcher/search/search_regex.go b/cmd/searcher/search/search_regex.go index c8fa9743ee8a..4c6bf8dd3175 100644 --- a/cmd/searcher/search/search_regex.go +++ b/cmd/searcher/search/search_regex.go @@ -17,8 +17,8 @@ import ( "github.com/sourcegraph/sourcegraph/cmd/searcher/protocol" "github.com/sourcegraph/sourcegraph/internal/pathmatch" "github.com/sourcegraph/sourcegraph/internal/store" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" ) @@ -295,7 +295,7 @@ func (rg *readerGrep) FindZip(zf *store.ZipFile, f *store.SrcFile) (protocol.Fil // regexSearch concurrently searches files in zr looking for matches using rg. func regexSearch(ctx context.Context, rg *readerGrep, zf *store.ZipFile, fileMatchLimit int, patternMatchesContent, patternMatchesPaths bool) (fm []protocol.FileMatch, limitHit bool, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "RegexSearch") + span, ctx := ot.StartSpanFromContext(ctx, "RegexSearch") ext.Component.Set(span, "regex_search") if rg.re != nil { span.SetTag("re", rg.re.String()) diff --git a/cmd/symbols/internal/symbols/fetch.go b/cmd/symbols/internal/symbols/fetch.go index 1f3851246dc0..0af93712a438 100644 --- a/cmd/symbols/internal/symbols/fetch.go +++ b/cmd/symbols/internal/symbols/fetch.go @@ -7,11 +7,11 @@ import ( "io" "path" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) type parseRequest struct { @@ -25,7 +25,7 @@ func (s *Service) fetchRepositoryArchive(ctx context.Context, repo api.RepoName, fetchQueueSize.Dec() fetching.Inc() - span, ctx := opentracing.StartSpanFromContext(ctx, "Store.fetch") + span, ctx := ot.StartSpanFromContext(ctx, "Store.fetch") ext.Component.Set(span, "store") span.SetTag("repo", repo) span.SetTag("commit", commitID) diff --git a/cmd/symbols/internal/symbols/parse.go b/cmd/symbols/internal/symbols/parse.go index e9b874845530..90293beeeddd 100644 --- a/cmd/symbols/internal/symbols/parse.go +++ b/cmd/symbols/internal/symbols/parse.go @@ -8,7 +8,6 @@ import ( "sync" "github.com/inconshreveable/log15" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" @@ -16,7 +15,8 @@ import ( "github.com/sourcegraph/sourcegraph/cmd/symbols/internal/pkg/ctags" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/symbols/protocol" - "golang.org/x/net/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" + nettrace "golang.org/x/net/trace" ) // startParsers starts the parser process pool. @@ -38,7 +38,7 @@ func (s *Service) startParsers() error { } func (s *Service) parseUncached(ctx context.Context, repo api.RepoName, commitID api.CommitID, callback func(symbol protocol.Symbol) error) (err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "parseUncached") + span, ctx := ot.StartSpanFromContext(ctx, "parseUncached") defer func() { if err != nil { ext.Error.Set(span, true) @@ -49,7 +49,7 @@ func (s *Service) parseUncached(ctx context.Context, repo api.RepoName, commitID span.SetTag("repo", string(repo)) span.SetTag("commit", string(commitID)) - tr := trace.New("parseUncached", string(repo)) + tr := nettrace.New("parseUncached", string(repo)) tr.LazyPrintf("commitID: %s", commitID) totalSymbols := 0 diff --git a/cmd/symbols/internal/symbols/search.go b/cmd/symbols/internal/symbols/search.go index 28dd07900870..ad50c6ef791a 100644 --- a/cmd/symbols/internal/symbols/search.go +++ b/cmd/symbols/internal/symbols/search.go @@ -19,11 +19,11 @@ import ( "github.com/jmoiron/sqlx" "github.com/keegancsmith/sqlf" sqlite3 "github.com/mattn/go-sqlite3" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/sourcegraph/sourcegraph/internal/symbols/protocol" - "golang.org/x/net/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" + nettrace "golang.org/x/net/trace" ) // maxFileSize is the limit on file size in bytes. Only files smaller than this are processed. @@ -69,8 +69,7 @@ func (s *Service) search(ctx context.Context, args protocol.SearchArgs) (result defer cancel() log15.Debug("Symbol search", "repo", args.Repo, "query", args.Query) - - span, ctx := opentracing.StartSpanFromContext(ctx, "search") + span, ctx := ot.StartSpanFromContext(ctx, "search") span.SetTag("repo", args.Repo) span.SetTag("commitID", args.CommitID) span.SetTag("query", args.Query) @@ -83,7 +82,7 @@ func (s *Service) search(ctx context.Context, args protocol.SearchArgs) (result span.Finish() }() - tr := trace.New("symbols.search", fmt.Sprintf("args:%+v", args)) + tr := nettrace.New("symbols.search", fmt.Sprintf("args:%+v", args)) defer func() { if err != nil { tr.LazyPrintf("error: %v", err) @@ -152,7 +151,7 @@ func isLiteralEquality(expr string) (ok bool, lit string, err error) { } func filterSymbols(ctx context.Context, db *sqlx.DB, args protocol.SearchArgs) (res []protocol.Symbol, err error) { - span, _ := opentracing.StartSpanFromContext(ctx, "filterSymbols") + span, _ := ot.StartSpanFromContext(ctx, "filterSymbols") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/cmd/symbols/main.go b/cmd/symbols/main.go index ec8684166f32..487869ac1ddb 100644 --- a/cmd/symbols/main.go +++ b/cmd/symbols/main.go @@ -16,8 +16,6 @@ import ( "time" "github.com/inconshreveable/log15" - "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/cmd/symbols/internal/pkg/ctags" @@ -26,6 +24,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/debugserver" "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/tracer" ) @@ -73,7 +72,7 @@ func main() { if err := service.Start(); err != nil { log.Fatalln("Start:", err) } - handler := nethttp.Middleware(opentracing.GlobalTracer(), service.Handler()) + handler := ot.Middleware(service.Handler()) host := "" if env.InsecureDev { diff --git a/enterprise/internal/codeintel/lsifserver/client/client.go b/enterprise/internal/codeintel/lsifserver/client/client.go index 74cbed199599..cbf51ad4da77 100644 --- a/enterprise/internal/codeintel/lsifserver/client/client.go +++ b/enterprise/internal/codeintel/lsifserver/client/client.go @@ -6,9 +6,9 @@ import ( "strings" "sync" - "github.com/opentracing-contrib/go-stdlib/nethttp" "github.com/sourcegraph/sourcegraph/internal/endpoint" "github.com/sourcegraph/sourcegraph/internal/env" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var ( @@ -20,8 +20,8 @@ var ( DefaultClient = &Client{ endpoint: LSIFURLs(), HTTPClient: &http.Client{ - // nethttp.Transport will propagate opentracing spans - Transport: &nethttp.Transport{}, + // ot.Transport will propagate opentracing spans + Transport: &ot.Transport{}, }, } ) diff --git a/enterprise/internal/codeintel/lsifserver/client/request.go b/enterprise/internal/codeintel/lsifserver/client/request.go index 000905b164ff..5cf0103f0d86 100644 --- a/enterprise/internal/codeintel/lsifserver/client/request.go +++ b/enterprise/internal/codeintel/lsifserver/client/request.go @@ -11,10 +11,10 @@ import ( "strconv" "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/linkheader" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) type lsifRequest struct { @@ -55,7 +55,7 @@ func (c *Client) do(ctx context.Context, lsifRequest *lsifRequest, payload inter return nil, err } - span, ctx := opentracing.StartSpanFromContext(ctx, "lsifserver.client.do") + span, ctx := ot.StartSpanFromContext(ctx, "lsifserver.client.do") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/go.mod b/go.mod index 262bdeac5429..004293e53d64 100644 --- a/go.mod +++ b/go.mod @@ -125,7 +125,7 @@ require ( github.com/sourcegraph/go-jsonschema v0.0.0-20191222043427-cdbee60427af github.com/sourcegraph/go-langserver v2.0.1-0.20181108233942-4a51fa2e1238+incompatible github.com/sourcegraph/go-lsp v0.0.0-20200117082640-b19bb38222e2 - github.com/sourcegraph/gosyntect v0.0.0-20191222043511-084e9c124954 + github.com/sourcegraph/gosyntect v0.0.0-20200331033347-c35e64c39373 github.com/sourcegraph/jsonx v0.0.0-20190114210550-ba8cb36a8614 github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect github.com/sqs/httpgzip v0.0.0-20180622165210-91da61ed4dff @@ -143,6 +143,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeonx/timeago v1.0.0-rc4 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect + go.uber.org/atomic v1.6.0 go.uber.org/automaxprocs v1.3.0 golang.org/x/arch v0.0.0-20200312215426-ff8b605520f4 // indirect golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 diff --git a/go.sum b/go.sum index c058bda1928e..6272d7d6a8bc 100644 --- a/go.sum +++ b/go.sum @@ -851,8 +851,8 @@ github.com/sourcegraph/goreman v0.1.2-0.20180928223752-6e9a2beb830d h1:FxF0pen6r github.com/sourcegraph/goreman v0.1.2-0.20180928223752-6e9a2beb830d/go.mod h1:8HCyYaC38XwX0AOu0+fuY02Y5Z7CkITW0oVJavbna4Q= github.com/sourcegraph/gosaml2 v0.3.2-0.20200109173551-5cfddeb48b17 h1:9kV7BLsB6gGUXFr7GqNb6VKlUz1WQvh8kkMb1uKEk9Y= github.com/sourcegraph/gosaml2 v0.3.2-0.20200109173551-5cfddeb48b17/go.mod h1:ZqB/uu1WtCDmlwK8c+TO8+QSfDkJsWx9LYjQBgGxxtk= -github.com/sourcegraph/gosyntect v0.0.0-20191222043511-084e9c124954 h1:TUosEuSmcqv7rdCg5H25vstEiqzu2VZA8WZ1dVJX4lA= -github.com/sourcegraph/gosyntect v0.0.0-20191222043511-084e9c124954/go.mod h1:WiNJKgKTnR3psOIGzVZQjLqZjJZuoL3F8tCh25Uk8dU= +github.com/sourcegraph/gosyntect v0.0.0-20200331033347-c35e64c39373 h1:0POmXzeymYlitNlTePPtUdEIKi1ac8jhiq1oK5xQtos= +github.com/sourcegraph/gosyntect v0.0.0-20200331033347-c35e64c39373/go.mod h1:WiNJKgKTnR3psOIGzVZQjLqZjJZuoL3F8tCh25Uk8dU= github.com/sourcegraph/jsonx v0.0.0-20190114210550-ba8cb36a8614 h1:MrlKMpoGse4bCneDoK/c+ZbPGqOP5Hme5ulatc8smbQ= github.com/sourcegraph/jsonx v0.0.0-20190114210550-ba8cb36a8614/go.mod h1:7jkSQ2sdxwXMaIDxKJotTt+hwKnT9b/wbJFU7/ObUEY= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= @@ -1002,6 +1002,8 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0H go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/automaxprocs v1.3.0 h1:II28aZoGdaglS5vVNnspf28lnZpXScxtIozx1lAjdb0= go.uber.org/automaxprocs v1.3.0/go.mod h1:9CWT6lKIep8U41DDaPiH6eFscnTyjfTANNQNx6LrIcA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1211,6 +1213,7 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911151314-feee8acb394c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/internal/conf/parse.go b/internal/conf/parse.go index 58e4b12eca1e..43bd7f02e9d2 100644 --- a/internal/conf/parse.go +++ b/internal/conf/parse.go @@ -53,13 +53,10 @@ var requireRestart = []string{ "searchScopes", "extensions", "disablePublicRepoRedirects", - "lightstepAccessToken", - "lightstepProject", "auth.userOrgMap", "auth.providers", "externalURL", "update.channel", - "useJaeger", } // NeedRestartToApply determines if a restart is needed to apply the changes diff --git a/internal/db/dbutil/dbutil.go b/internal/db/dbutil/dbutil.go index c314da676377..4d9469557d16 100644 --- a/internal/db/dbutil/dbutil.go +++ b/internal/db/dbutil/dbutil.go @@ -19,9 +19,9 @@ import ( bindata "github.com/golang-migrate/migrate/v4/source/go_bindata" multierror "github.com/hashicorp/go-multierror" "github.com/inconshreveable/log15" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/migrations" ) @@ -38,7 +38,7 @@ func Transaction(ctx context.Context, db *sql.DB, f func(tx *sql.Tx) error) (err err = tx.Commit() } - span, ctx := opentracing.StartSpanFromContext(ctx, "Transaction") + span, ctx := ot.StartSpanFromContext(ctx, "Transaction") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/internal/diskcache/cache.go b/internal/diskcache/cache.go index 0aadbae54c7d..a24d5fa471a4 100644 --- a/internal/diskcache/cache.go +++ b/internal/diskcache/cache.go @@ -13,9 +13,9 @@ import ( "strings" "time" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // Store is an on disk cache, with items cached via calls to Open. @@ -77,7 +77,7 @@ func (s *Store) Open(ctx context.Context, key string, fetcher Fetcher) (file *Fi // OpenWithPath will open a file from the local cache with key. If missing, fetcher // will fill the cache first. Open also performs single-flighting for fetcher. func (s *Store) OpenWithPath(ctx context.Context, key string, fetcher FetcherWithPath) (file *File, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Cached Fetch") + span, ctx := ot.StartSpanFromContext(ctx, "Cached Fetch") if s.Component != "" { ext.Component.Set(span, s.Component) } diff --git a/internal/extsvc/bitbucketcloud/client.go b/internal/extsvc/bitbucketcloud/client.go index 904fe07afec3..1d9a056b967e 100644 --- a/internal/extsvc/bitbucketcloud/client.go +++ b/internal/extsvc/bitbucketcloud/client.go @@ -14,10 +14,10 @@ import ( "github.com/inconshreveable/log15" "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/httpcli" "github.com/sourcegraph/sourcegraph/internal/metrics" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "golang.org/x/time/rate" ) @@ -157,7 +157,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, result interface{}) req.URL = c.URL.ResolveReference(req.URL) req.Header.Set("Content-Type", "application/json; charset=utf-8") - req, ht := nethttp.TraceRequest(opentracing.GlobalTracer(), + req, ht := nethttp.TraceRequest(ot.GetTracer(ctx), req.WithContext(ctx), nethttp.OperationName("Bitbucket Cloud"), nethttp.ClientTrace(false)) diff --git a/internal/extsvc/bitbucketserver/client.go b/internal/extsvc/bitbucketserver/client.go index 560663ea761a..4951557f8ba9 100644 --- a/internal/extsvc/bitbucketserver/client.go +++ b/internal/extsvc/bitbucketserver/client.go @@ -21,11 +21,11 @@ import ( "github.com/gomodule/oauth1/oauth" "github.com/inconshreveable/log15" "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/segmentio/fasthash/fnv1" "github.com/sourcegraph/sourcegraph/internal/httpcli" "github.com/sourcegraph/sourcegraph/internal/metrics" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/schema" "golang.org/x/time/rate" ) @@ -780,7 +780,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, result interface{}) req.URL = c.URL.ResolveReference(req.URL) req.Header.Set("Content-Type", "application/json; charset=utf-8") - req, ht := nethttp.TraceRequest(opentracing.GlobalTracer(), + req, ht := nethttp.TraceRequest(ot.GetTracer(ctx), req.WithContext(ctx), nethttp.OperationName("Bitbucket Server"), nethttp.ClientTrace(false)) diff --git a/internal/extsvc/github/client.go b/internal/extsvc/github/client.go index 93e441aea09d..7a2d547e03fa 100644 --- a/internal/extsvc/github/client.go +++ b/internal/extsvc/github/client.go @@ -18,13 +18,13 @@ import ( "sync" "time" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/httpcli" "github.com/sourcegraph/sourcegraph/internal/metrics" "github.com/sourcegraph/sourcegraph/internal/ratelimit" "github.com/sourcegraph/sourcegraph/internal/rcache" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var ( @@ -203,7 +203,7 @@ func (c *Client) do(ctx context.Context, token string, req *http.Request, result var resp *http.Response - span, ctx := opentracing.StartSpanFromContext(ctx, "GitHub") + span, ctx := ot.StartSpanFromContext(ctx, "GitHub") span.SetTag("URL", req.URL.String()) defer func() { if err != nil { diff --git a/internal/extsvc/gitlab/client.go b/internal/extsvc/gitlab/client.go index 3280884f579b..d5f9d2802142 100644 --- a/internal/extsvc/gitlab/client.go +++ b/internal/extsvc/gitlab/client.go @@ -15,13 +15,13 @@ import ( "time" "github.com/inconshreveable/log15" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/httpcli" "github.com/sourcegraph/sourcegraph/internal/metrics" "github.com/sourcegraph/sourcegraph/internal/ratelimit" "github.com/sourcegraph/sourcegraph/internal/rcache" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var ( @@ -215,7 +215,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, result interface{}) var resp *http.Response - span, ctx := opentracing.StartSpanFromContext(ctx, "GitLab") + span, ctx := ot.StartSpanFromContext(ctx, "GitLab") span.SetTag("URL", req.URL.String()) defer func() { if err != nil { diff --git a/internal/gitserver/client.go b/internal/gitserver/client.go index bf208f061224..beb8f2c41b86 100644 --- a/internal/gitserver/client.go +++ b/internal/gitserver/client.go @@ -22,7 +22,6 @@ import ( "github.com/inconshreveable/log15" "github.com/neelance/parallel" "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" @@ -33,14 +32,15 @@ import ( "github.com/sourcegraph/sourcegraph/internal/gitserver/protocol" "github.com/sourcegraph/sourcegraph/internal/httpcli" "github.com/sourcegraph/sourcegraph/internal/metrics" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs" ) var requestMeter = metrics.NewRequestMeter("gitserver", "Total number of requests sent to gitserver.") // defaultTransport is the default transport used in the default client and the -// default reverse proxy. nethttp.Transport will propagate opentracing spans. -var defaultTransport = &nethttp.Transport{ +// default reverse proxy. ot.Transport will propagate opentracing spans. +var defaultTransport = &ot.Transport{ RoundTripper: requestMeter.Transport(&http.Transport{ // Default is 2, but we can send many concurrent requests MaxIdleConnsPerHost: 500, @@ -164,7 +164,7 @@ func (c *Client) ArchiveURL(ctx context.Context, repo Repo, opt ArchiveOptions) // Archive produces an archive from a Git repository. func (c *Client) Archive(ctx context.Context, repo Repo, opt ArchiveOptions) (_ io.ReadCloser, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Archive") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Archive") span.SetTag("Repo", repo.Name) span.SetTag("Treeish", opt.Treeish) defer func() { @@ -224,7 +224,7 @@ func (e badRequestError) BadRequest() bool { return true } func (c *Cmd) sendExec(ctx context.Context) (_ io.ReadCloser, _ http.Header, errRes error) { repoName := protocol.NormalizeRepo(c.Repo.Name) - span, ctx := opentracing.StartSpanFromContext(ctx, "Client.sendExec") + span, ctx := ot.StartSpanFromContext(ctx, "Client.sendExec") defer func() { if errRes != nil { ext.Error.Set(span, true) @@ -761,7 +761,7 @@ func (c *Client) httpPost(ctx context.Context, repo api.RepoName, op string, pay // do performs a request to a gitserver, sharding based on the given // repo name (the repo name is otherwise not used). func (c *Client) do(ctx context.Context, repo api.RepoName, method, op string, payload interface{}) (resp *http.Response, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Client.do") + span, ctx := ot.StartSpanFromContext(ctx, "Client.do") defer func() { span.LogKV("repo", string(repo), "method", method, "op", op) if err != nil { diff --git a/internal/gitserver/proxy.go b/internal/gitserver/proxy.go index 0a5b3e134033..97032fdc8c32 100644 --- a/internal/gitserver/proxy.go +++ b/internal/gitserver/proxy.go @@ -5,8 +5,8 @@ import ( "net/http/httputil" "github.com/neelance/parallel" - opentracing "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/internal/api" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // DefaultReverseProxy is the default ReverseProxy. It uses the same transport and HTTP @@ -34,7 +34,7 @@ type ReverseProxy struct { // to gitserver. The director must rewrite the request to the correct gitserver address, which // should be obtained via a gitserver client's AddrForRepo method. func (p *ReverseProxy) ServeHTTP(repo api.RepoName, method, op string, director func(req *http.Request), res http.ResponseWriter, req *http.Request) { - span, _ := opentracing.StartSpanFromContext(req.Context(), "ReverseProxy.ServeHTTP") + span, _ := ot.StartSpanFromContext(req.Context(), "ReverseProxy.ServeHTTP") defer func() { span.LogKV("repo", string(repo), "method", method, "op", op) span.Finish() diff --git a/internal/highlight/highlight.go b/internal/highlight/highlight.go index d15c78b636cb..ec580515dd06 100644 --- a/internal/highlight/highlight.go +++ b/internal/highlight/highlight.go @@ -17,6 +17,7 @@ import ( "github.com/sourcegraph/gosyntect" "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "golang.org/x/net/html" "golang.org/x/net/html/atom" ) @@ -158,6 +159,7 @@ func Code(ctx context.Context, p Params) (h template.HTML, aborted bool, err err Filepath: p.Filepath, Theme: themechoice, StabilizeTimeout: stabilizeTimeout, + Tracer: ot.GetTracer(ctx), }) if ctx.Err() == context.DeadlineExceeded { diff --git a/internal/httpcli/client.go b/internal/httpcli/client.go index 0daeb6b1192f..0954f2ef878e 100644 --- a/internal/httpcli/client.go +++ b/internal/httpcli/client.go @@ -8,9 +8,9 @@ import ( "github.com/gregjones/httpcache" "github.com/hashicorp/go-multierror" - "github.com/opentracing-contrib/go-stdlib/nethttp" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/httputil" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // A Doer captures the Do method of an http.Client. It faciliates decorating @@ -241,7 +241,7 @@ func TracedTransportOpt(cli *http.Client) error { cli.Transport = http.DefaultTransport } - cli.Transport = &nethttp.Transport{RoundTripper: cli.Transport} + cli.Transport = &ot.Transport{RoundTripper: cli.Transport} return nil } diff --git a/internal/repoupdater/client.go b/internal/repoupdater/client.go index 4d01f93c8a11..05b88061d151 100644 --- a/internal/repoupdater/client.go +++ b/internal/repoupdater/client.go @@ -11,7 +11,6 @@ import ( "net/url" "github.com/opentracing-contrib/go-stdlib/nethttp" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" @@ -19,6 +18,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/metrics" "github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) var repoupdaterURL = env.Get("REPO_UPDATER_URL", "http://repo-updater:3182", "repo-updater server URL") @@ -42,8 +42,8 @@ var ( var DefaultClient = &Client{ URL: repoupdaterURL, HTTPClient: &http.Client{ - // nethttp.Transport will propagate opentracing spans - Transport: &nethttp.Transport{ + // ot.Transport will propagate opentracing spans and whether or not to trace + Transport: &ot.Transport{ RoundTripper: requestMeter.Transport(&http.Transport{ // Default is 2, but we can send many concurrent requests MaxIdleConnsPerHost: 500, @@ -89,7 +89,7 @@ func (c *Client) RepoLookup(ctx context.Context, args protocol.RepoLookupArgs) ( return MockRepoLookup(args) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Client.RepoLookup") + span, ctx := ot.StartSpanFromContext(ctx, "Client.RepoLookup") defer func() { if result != nil { span.SetTag("found", result.Repo != nil) @@ -350,7 +350,7 @@ func (c *Client) httpGet(ctx context.Context, method string) (*http.Response, er } func (c *Client) do(ctx context.Context, req *http.Request) (_ *http.Response, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Client.do") + span, ctx := ot.StartSpanFromContext(ctx, "Client.do") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/internal/store/store.go b/internal/store/store.go index 3ebfa3cdb88c..4ad149ebc744 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -16,14 +16,15 @@ import ( "sync" "time" + opentracing "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/diskcache" "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/metrics" "github.com/sourcegraph/sourcegraph/internal/mutablelimiter" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -113,7 +114,7 @@ func (s *Store) Start() { // PrepareZip returns the path to a local zip archive of repo at commit. // It will first consult the local cache, otherwise will fetch from the network. func (s *Store) PrepareZip(ctx context.Context, repo gitserver.Repo, commit api.CommitID) (path string, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Store.prepareZip") + span, ctx := ot.StartSpanFromContext(ctx, "Store.prepareZip") ext.Component.Set(span, "store") defer func() { if err != nil { @@ -191,7 +192,7 @@ func (s *Store) fetch(ctx context.Context, repo gitserver.Repo, commit api.Commi ctx, cancel := context.WithTimeout(ctx, 2*time.Minute) fetching.Inc() - span, ctx := opentracing.StartSpanFromContext(ctx, "Store.fetch") + span, ctx := ot.StartSpanFromContext(ctx, "Store.fetch") ext.Component.Set(span, "store") span.SetTag("repo", repo.Name) span.SetTag("repoURL", repo.URL) diff --git a/internal/symbols/client.go b/internal/symbols/client.go index 5dbb66335f6b..2346c0b7d45d 100644 --- a/internal/symbols/client.go +++ b/internal/symbols/client.go @@ -12,7 +12,6 @@ import ( "github.com/neelance/parallel" "github.com/opentracing-contrib/go-stdlib/nethttp" - "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" otlog "github.com/opentracing/opentracing-go/log" "github.com/pkg/errors" @@ -21,6 +20,7 @@ import ( "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/search" "github.com/sourcegraph/sourcegraph/internal/symbols/protocol" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "golang.org/x/net/context/ctxhttp" ) @@ -31,8 +31,8 @@ var symbolsURL = env.Get("SYMBOLS_URL", "k8s+http://symbols:3184", "symbols serv var DefaultClient = &Client{ URL: symbolsURL, HTTPClient: &http.Client{ - // nethttp.Transport will propagate opentracing spans - Transport: &nethttp.Transport{ + // ot.Transport will propagate opentracing spans + Transport: &ot.Transport{ RoundTripper: &http.Transport{ // Default is 2, but we can send many concurrent requests MaxIdleConnsPerHost: 500, @@ -75,7 +75,7 @@ func (c *Client) url(key key) (string, error) { // Search performs a symbol search on the symbols service. func (c *Client) Search(ctx context.Context, args search.SymbolsParameters) (result *protocol.SearchResult, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "symbols.Client.Search") + span, ctx := ot.StartSpanFromContext(ctx, "symbols.Client.Search") defer func() { if err != nil { ext.Error.Set(span, true) @@ -103,7 +103,7 @@ func (c *Client) Search(ctx context.Context, args search.SymbolsParameters) (res } func (c *Client) httpPost(ctx context.Context, method string, key key, payload interface{}) (resp *http.Response, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "symbols.Client.httpPost") + span, ctx := ot.StartSpanFromContext(ctx, "symbols.Client.httpPost") defer func() { if err != nil { ext.Error.Set(span, true) diff --git a/internal/trace/httptrace.go b/internal/trace/httptrace.go index 5d8d7a7eac49..589013036d54 100644 --- a/internal/trace/httptrace.go +++ b/internal/trace/httptrace.go @@ -15,12 +15,13 @@ import ( "github.com/felixge/httpsnoop" raven "github.com/getsentry/raven-go" "github.com/gorilla/mux" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/repotrackutil" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/version" ) @@ -126,11 +127,11 @@ func WithRequestOrigin(ctx context.Context, name string) context.Context { // // 🚨 SECURITY: This handler is served to all clients, even on private servers to clients who have // not authenticated. It must not reveal any sensitive information. -func Middleware(next http.Handler) http.Handler { +func HTTPTraceMiddleware(next http.Handler) http.Handler { return raven.Recoverer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() - wireContext, err := opentracing.GlobalTracer().Extract( + wireContext, err := ot.GetTracer(ctx).Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) if err != nil && err != opentracing.ErrSpanContextNotFound { @@ -138,7 +139,7 @@ func Middleware(next http.Handler) http.Handler { } // start new span - span := opentracing.StartSpan("", ext.RPCServerOption(wireContext)) + span, ctx := ot.StartSpanFromContext(ctx, "", ext.RPCServerOption(wireContext)) ext.HTTPUrl.Set(span, r.URL.String()) ext.HTTPMethod.Set(span, r.Method) span.SetTag("http.referer", r.Header.Get("referer")) diff --git a/internal/trace/ot/ot.go b/internal/trace/ot/ot.go new file mode 100644 index 000000000000..e309b63bf5cf --- /dev/null +++ b/internal/trace/ot/ot.go @@ -0,0 +1,137 @@ +// Package ot wraps github.com/opentracing/opentracing-go and +// github.com./opentracing-contrib/go-stdlib with selective tracing behavior that is toggled on and +// off with the presence of a context item (uses context.Context). This context item is propagated +// across API boundaries through a HTTP header (X-Sourcegraph-Should-Trace). +package ot + +import ( + "context" + "net/http" + "strconv" + + "github.com/opentracing-contrib/go-stdlib/nethttp" + "github.com/opentracing/opentracing-go" + "go.uber.org/atomic" +) + +type tracePolicy string + +const ( + // TraceNone turns off tracing. + TraceNone tracePolicy = "none" + + // TraceSelective turns on tracing only for requests with the X-Sourcegraph-Should-Trace header + // set to a truthy value. + TraceSelective tracePolicy = "selective" + + // Comprehensive turns on tracing for all requests. + TraceAll tracePolicy = "all" +) + +var trPolicy = atomic.NewString(string(TraceNone)) + +func SetTracePolicy(newTracePolicy tracePolicy) { + trPolicy.Store(string(newTracePolicy)) +} + +func GetTracePolicy() tracePolicy { + return tracePolicy(trPolicy.Load()) +} + +// Middleware wraps the handler with the following: +// +// - If the HTTP header, X-Sourcegraph-Should-Trace, is set to a truthy value, set the +// shouldTraceKey context.Context value to true +// - github.com/opentracing-contrib/go-stdlib/nethttp.Middleware, which creates a new span to track +// the request handler from the global tracer. +func Middleware(h http.Handler, opts ...nethttp.MWOption) http.Handler { + return MiddlewareWithTracer(opentracing.GlobalTracer(), h) +} + +// MiddlewareWithTracer is like Middleware, but uses the specified tracer instead of the global +// tracer. +func MiddlewareWithTracer(tr opentracing.Tracer, h http.Handler, opts ...nethttp.MWOption) http.Handler { + nethttpMiddleware := nethttp.Middleware(tr, h, append([]nethttp.MWOption{ + nethttp.MWSpanFilter(func(r *http.Request) bool { + return ShouldTrace(r.Context()) + }), + }, opts...)...) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch GetTracePolicy() { + case TraceSelective: + traceHeaderIsTrue, _ := strconv.ParseBool(r.Header.Get(traceHeader)) + nethttpMiddleware.ServeHTTP(w, r.WithContext(WithShouldTrace(r.Context(), traceHeaderIsTrue))) + return + case TraceAll: + nethttpMiddleware.ServeHTTP(w, r.WithContext(WithShouldTrace(r.Context(), true))) + return + default: + nethttpMiddleware.ServeHTTP(w, r.WithContext(WithShouldTrace(r.Context(), false))) + return + } + }) +} + +const traceHeader = "X-Sourcegraph-Should-Trace" + +// Transport wraps an underlying HTTP RoundTripper, injecting the X-Sourcegraph-Should-Trace header +// into outgoing requests whenever the shouldTraceKey context value is true. +type Transport struct { + http.RoundTripper +} + +func (r *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set(traceHeader, strconv.FormatBool(ShouldTrace(req.Context()))) + t := nethttp.Transport{RoundTripper: r.RoundTripper} + return t.RoundTrip(req) +} + +type key int + +const ( + shouldTraceKey key = iota +) + +// ShouldTrace returns true if the shouldTraceKey context value is true. +func ShouldTrace(ctx context.Context) bool { + v, ok := ctx.Value(shouldTraceKey).(bool) + if !ok { + return false + } + return v +} + +// WithShouldTrace sets the shouldTraceKey context value. +func WithShouldTrace(ctx context.Context, shouldTrace bool) context.Context { + return context.WithValue(ctx, shouldTraceKey, shouldTrace) +} + +// GetTracer returns the tracer to use for the given context. If ShouldTrace returns true, it +// returns the global tracer. Otherwise, it returns the NoopTracer. +func GetTracer(ctx context.Context) opentracing.Tracer { + return getTracer(ctx, opentracing.GlobalTracer()) +} + +// getTracer is like GetTracer, but accepts a tracer as an argument. If ShouldTrace returns false, +// it returns the NoopTracer. If it returns true and the passed-in tracer is not nil, it returns the +// passed-in tracer. Otherwise, it returns the global tracer. +func getTracer(ctx context.Context, tracer opentracing.Tracer) opentracing.Tracer { + if !ShouldTrace(ctx) { + return opentracing.NoopTracer{} + } + if tracer == nil { + return opentracing.GlobalTracer() + } + return tracer +} + +// StartSpanFromContext starts a span using the tracer returned by GetTracer. +func StartSpanFromContext(ctx context.Context, operationName string, opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context) { + return StartSpanFromContextWithTracer(ctx, opentracing.GlobalTracer(), operationName, opts...) +} + +// StartSpanFromContext starts a span using the tracer returned by invoking getTracer with the +// passed-in tracer. +func StartSpanFromContextWithTracer(ctx context.Context, tracer opentracing.Tracer, operationName string, opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context) { + return opentracing.StartSpanFromContextWithTracer(ctx, getTracer(ctx, tracer), operationName, opts...) +} diff --git a/internal/trace/traceutil.go b/internal/trace/traceutil.go index f48c51cf1ebf..20114300bd8c 100644 --- a/internal/trace/traceutil.go +++ b/internal/trace/traceutil.go @@ -10,17 +10,20 @@ import ( opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/log" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" nettrace "golang.org/x/net/trace" ) -// SpanURL returns the URL to the tracing UI for the given span. The span must be non-nil. -var SpanURL = func(span opentracing.Span) string { +var NoopSpanURL = func(span opentracing.Span) string { return "#tracer-not-enabled" } +// SpanURL returns the URL to the tracing UI for the given span. The span must be non-nil. +var SpanURL = NoopSpanURL + // New returns a new Trace with the specified family and title. func New(ctx context.Context, family, title string) (*Trace, context.Context) { - tr := Tracer{Tracer: opentracing.GlobalTracer()} + tr := Tracer{Tracer: ot.GetTracer(ctx)} return tr.New(ctx, family, title) } @@ -33,7 +36,7 @@ type Tracer struct { // New returns a new Trace with the specified family and title. func (t Tracer) New(ctx context.Context, family, title string) (*Trace, context.Context) { - span, ctx := opentracing.StartSpanFromContextWithTracer( + span, ctx := ot.StartSpanFromContextWithTracer( ctx, t.Tracer, family, diff --git a/internal/tracer/tracer.go b/internal/tracer/tracer.go index 345e66a52056..af76cffe17f3 100644 --- a/internal/tracer/tracer.go +++ b/internal/tracer/tracer.go @@ -1,22 +1,29 @@ +// Package tracer initializes distributed tracing and log15 behavior. It also updates distributed +// tracing behavior in response to changes in site configuration. When the Init function of this +// package is invoked, opentracing.SetGlobalTracer is called (and subsequently called again after +// every Sourcegraph site configuration change). Importing programs should not invoke +// opentracing.SetGlobalTracer anywhere else. package tracer import ( "bytes" "fmt" - "log" + "io" "os" "reflect" "strconv" + "sync" "time" "github.com/fatih/color" "github.com/inconshreveable/log15" + "github.com/lightstep/lightstep-tracer-go" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/trace" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "go.uber.org/automaxprocs/maxprocs" - lightstep "github.com/lightstep/lightstep-tracer-go" opentracing "github.com/opentracing/opentracing-go" jaeger "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" @@ -127,33 +134,8 @@ func Init(options ...Option) { handler = log15.LvlFilterHandler(lvl, handler) } log15.Root().SetHandler(log15.LvlFilterHandler(lvl, handler)) - if conf.Get().UseJaeger { - log15.Info("Distributed tracing enabled", "tracer", "jaeger") - cfg, err := jaegercfg.FromEnv() - if err != nil { - log.Printf("Could not initialize jaeger tracer from env: %s", err.Error()) - return - } - if reflect.DeepEqual(cfg.Sampler, &jaegercfg.SamplerConfig{}) { - // Default sampler configuration for when it is not specified via - // JAEGER_SAMPLER_* env vars. In most cases, this is sufficient - // enough to connect Sourcegraph to Jaeger without any env vars. - cfg.Sampler.Type = jaeger.SamplerTypeConst - cfg.Sampler.Param = 1 - } - _, err = cfg.InitGlobalTracer( - opts.serviceName, - jaegercfg.Logger(jaegerlog.StdLogger), - jaegercfg.Metrics(jaegermetrics.NullFactory), - ) - if err != nil { - log.Printf("Could not initialize jaeger tracer: %s", err.Error()) - return - } - trace.SpanURL = jaegerSpanURL - return - } + // Legacy Lightstep support lightstepAccessToken := conf.Get().LightstepAccessToken if lightstepAccessToken != "" { log15.Info("Distributed tracing enabled", "tracer", "Lightstep") @@ -177,9 +159,152 @@ func Init(options ...Option) { defaultHandler(e) } }) + + // If Lightstep is used, don't invoke initTracer, as that will conflict with the Lightstep + // configuration. + return + } + + initTracer(opts) +} + +// initTracer is a helper that should be called exactly once (from Init). +func initTracer(opts *Options) { + globalTracer := newSwitchableTracer() + opentracing.SetGlobalTracer(globalTracer) + var ( + jaegerEnabledMu sync.Mutex + jaegerEnabled = false + ) + + // Watch loop + conf.Watch(func() { + opentracing.SetGlobalTracer(globalTracer) + siteConfig := conf.Get() + + // Set sampling strategy + samplingStrategy := ot.TraceNone + shouldLog := false + if tracingConfig := siteConfig.ObservabilityTracing; tracingConfig != nil { + switch tracingConfig.Sampling { + case "all": + samplingStrategy = ot.TraceAll + case "selective": + samplingStrategy = ot.TraceSelective + } + shouldLog = tracingConfig.Debug + } else if siteConfig.UseJaeger { + samplingStrategy = ot.TraceAll + } + if tracePolicy := ot.GetTracePolicy(); tracePolicy != samplingStrategy { + log15.Info("opentracing: TracePolicy", "oldValue", tracePolicy, "newValue", samplingStrategy) + } + ot.SetTracePolicy(samplingStrategy) + + // Determine whether Jaeger should be enabled + _, lastShouldLog := globalTracer.get() + jaegerShouldBeEnabled := samplingStrategy == ot.TraceAll || samplingStrategy == ot.TraceSelective + + // Set global tracer (Jaeger or No-op) + jaegerEnabledMu.Lock() + defer jaegerEnabledMu.Unlock() + if jaegerEnabled != jaegerShouldBeEnabled { + log15.Info("opentracing: Jaeger enablement change", "old", jaegerEnabled, "newValue", jaegerShouldBeEnabled) + } + if jaegerShouldBeEnabled && (!jaegerEnabled || lastShouldLog != shouldLog) { + cfg, err := jaegercfg.FromEnv() + cfg.ServiceName = opts.serviceName + if err != nil { + log15.Warn("Could not initialize jaeger tracer from env", "error", err.Error()) + return + } + if reflect.DeepEqual(cfg.Sampler, &jaegercfg.SamplerConfig{}) { + // Default sampler configuration for when it is not specified via + // JAEGER_SAMPLER_* env vars. In most cases, this is sufficient + // enough to connect Sourcegraph to Jaeger without any env vars. + cfg.Sampler.Type = jaeger.SamplerTypeConst + cfg.Sampler.Param = 1 + } + tracer, closer, err := cfg.NewTracer( + jaegercfg.Logger(jaegerlog.StdLogger), + jaegercfg.Metrics(jaegermetrics.NullFactory), + ) + if err != nil { + log15.Warn("Could not initialize jaeger tracer", "error", err.Error()) + return + } + globalTracer.set(tracer, closer, shouldLog) + trace.SpanURL = jaegerSpanURL + jaegerEnabled = true + } else if !jaegerShouldBeEnabled && jaegerEnabled { + globalTracer.set(opentracing.NoopTracer{}, nil, shouldLog) + trace.SpanURL = trace.NoopSpanURL + jaegerEnabled = false + } + }) +} + +// switchableTracer implements opentracing.Tracer. The underlying tracer used is switchable (set via +// the `set` method). +type switchableTracer struct { + mu sync.RWMutex + tracer opentracing.Tracer + tracerCloser io.Closer + log bool +} + +func newSwitchableTracer() *switchableTracer { + return &switchableTracer{tracer: opentracing.NoopTracer{}} +} + +func (t *switchableTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { + t.mu.RLock() + defer t.mu.RUnlock() + if t.log { + log15.Info("opentracing: StartSpan", "operationName", operationName, "tracer", fmt.Sprintf("%T", t.tracer)) + } + return t.tracer.StartSpan(operationName, opts...) +} + +func (t *switchableTracer) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error { + t.mu.RLock() + defer t.mu.RUnlock() + if t.log { + log15.Info("opentracing: Inject", "tracer", fmt.Sprintf("%T", t.tracer)) + } + return t.tracer.Inject(sm, format, carrier) +} + +func (t *switchableTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) { + t.mu.RLock() + defer t.mu.RUnlock() + if t.log { + log15.Info("opentracing: Extract", "tracer", fmt.Sprintf("%T", t.tracer)) + } + return t.tracer.Extract(format, carrier) +} + +func (t *switchableTracer) set(tracer opentracing.Tracer, tracerCloser io.Closer, log bool) { + t.mu.Lock() + defer t.mu.Unlock() + if tc := t.tracerCloser; tc != nil { + // Close the old tracerCloser outside the critical zone + go tc.Close() } + + t.tracerCloser = tracerCloser + t.tracer = tracer + t.log = log } +func (t *switchableTracer) get() (tracer opentracing.Tracer, log bool) { + t.mu.RLock() + defer t.mu.RUnlock() + return t.tracer, t.log +} + +const tracingNotEnabledURL = "#tracing_not_enabled_for_this_request_add_?trace=1_to_url_to_enable" + func lightStepSpanURL(span opentracing.Span) string { spanCtx := span.Context().(lightstep.SpanContext) t := span.(interface { @@ -189,6 +314,12 @@ func lightStepSpanURL(span opentracing.Span) string { } func jaegerSpanURL(span opentracing.Span) string { - spanCtx := span.Context().(jaeger.SpanContext) + if span == nil { + return tracingNotEnabledURL + } + spanCtx, ok := span.Context().(jaeger.SpanContext) + if !ok { + return tracingNotEnabledURL + } return spanCtx.TraceID().String() } diff --git a/internal/vcs/git/blame.go b/internal/vcs/git/blame.go index e523028c34ff..c67cccb696e9 100644 --- a/internal/vcs/git/blame.go +++ b/internal/vcs/git/blame.go @@ -8,10 +8,10 @@ import ( "strings" "time" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // BlameOptions configures a blame. @@ -36,7 +36,7 @@ type Hunk struct { // BlameFile returns Git blame information about a file. func BlameFile(ctx context.Context, repo gitserver.Repo, path string, opt *BlameOptions) ([]*Hunk, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: BlameFile") + span, ctx := ot.StartSpanFromContext(ctx, "Git: BlameFile") span.SetTag("repo", repo.Name) span.SetTag("path", path) span.SetTag("opt", opt) diff --git a/internal/vcs/git/blob.go b/internal/vcs/git/blob.go index 4bbb30389084..86468022b927 100644 --- a/internal/vcs/git/blob.go +++ b/internal/vcs/git/blob.go @@ -8,10 +8,10 @@ import ( "os" "strings" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/util" ) @@ -22,7 +22,7 @@ func ReadFile(ctx context.Context, repo gitserver.Repo, commit api.CommitID, nam return Mocks.ReadFile(commit, name) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ReadFile") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ReadFile") span.SetTag("Name", name) defer span.Finish() @@ -45,7 +45,7 @@ func NewFileReader(ctx context.Context, repo gitserver.Repo, commit api.CommitID return Mocks.NewFileReader(commit, name) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: GetFileReader") + span, ctx := ot.StartSpanFromContext(ctx, "Git: GetFileReader") span.SetTag("Name", name) defer span.Finish() diff --git a/internal/vcs/git/commits.go b/internal/vcs/git/commits.go index 0bf21d06f282..5fed92c5f40e 100644 --- a/internal/vcs/git/commits.go +++ b/internal/vcs/git/commits.go @@ -8,11 +8,11 @@ import ( "strings" "time" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" "github.com/sourcegraph/sourcegraph/internal/lazyregexp" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) type Commit struct { @@ -84,7 +84,7 @@ func getCommit(ctx context.Context, repo gitserver.Repo, remoteURLFunc func() (s // needed. The Git remote URL is only required if the gitserver doesn't already contain a clone of // the repository or if the commit must be fetched from the remote. func GetCommit(ctx context.Context, repo gitserver.Repo, remoteURLFunc func() (string, error), id api.CommitID) (*Commit, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: GetCommit") + span, ctx := ot.StartSpanFromContext(ctx, "Git: GetCommit") span.SetTag("Commit", id) defer span.Finish() @@ -93,7 +93,7 @@ func GetCommit(ctx context.Context, repo gitserver.Repo, remoteURLFunc func() (s // Commits returns all commits matching the options. func Commits(ctx context.Context, repo gitserver.Repo, opt CommitsOptions) ([]*Commit, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Commits") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Commits") span.SetTag("Opt", opt) defer span.Finish() @@ -107,7 +107,7 @@ func Commits(ctx context.Context, repo gitserver.Repo, opt CommitsOptions) ([]*C // HasCommitAfter indicates the staleness of a repository. It returns a boolean indicating if a repository // contains a commit past a specified date. func HasCommitAfter(ctx context.Context, repo gitserver.Repo, date string, revspec string) (bool, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: HasCommitAfter") + span, ctx := ot.StartSpanFromContext(ctx, "Git: HasCommitAfter") span.SetTag("Date", date) span.SetTag("RevSpec", revspec) defer span.Finish() @@ -225,7 +225,7 @@ func commitLogArgs(initialArgs []string, opt CommitsOptions) (args []string, err // CommitCount returns the number of commits that would be returned by Commits. func CommitCount(ctx context.Context, repo gitserver.Repo, opt CommitsOptions) (uint, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: CommitCount") + span, ctx := ot.StartSpanFromContext(ctx, "Git: CommitCount") span.SetTag("Opt", opt) defer span.Finish() diff --git a/internal/vcs/git/exec.go b/internal/vcs/git/exec.go index 2ee01aed1bfb..8500df20f0f5 100644 --- a/internal/vcs/git/exec.go +++ b/internal/vcs/git/exec.go @@ -9,9 +9,9 @@ import ( "net/url" "strings" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs" ) @@ -34,7 +34,7 @@ func ExecSafe(ctx context.Context, repo gitserver.Repo, params []string) (stdout return Mocks.ExecSafe(params) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ExecSafe") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ExecSafe") defer span.Finish() if len(params) == 0 { @@ -58,7 +58,7 @@ func ExecSafe(ctx context.Context, repo gitserver.Repo, params []string) (stdout // ExecReader executes an arbitrary `git` command (`git [args...]`) and returns a reader connected // to its stdout. func ExecReader(ctx context.Context, repo gitserver.Repo, args []string) (io.ReadCloser, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ExecReader") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ExecReader") span.SetTag("args", args) defer span.Finish() diff --git a/internal/vcs/git/merge_base.go b/internal/vcs/git/merge_base.go index f5a547aef496..91abc72e00e2 100644 --- a/internal/vcs/git/merge_base.go +++ b/internal/vcs/git/merge_base.go @@ -5,15 +5,15 @@ import ( "context" "fmt" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // MergeBase returns the merge base commit for the specified commits. func MergeBase(ctx context.Context, repo gitserver.Repo, a, b api.CommitID) (api.CommitID, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: MergeBase") + span, ctx := ot.StartSpanFromContext(ctx, "Git: MergeBase") span.SetTag("A", a) span.SetTag("B", b) defer span.Finish() diff --git a/internal/vcs/git/object.go b/internal/vcs/git/object.go index d2ece77b2990..7cbb8378e6a9 100644 --- a/internal/vcs/git/object.go +++ b/internal/vcs/git/object.go @@ -6,9 +6,9 @@ import ( "encoding/hex" "fmt" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // OID is a Git OID (40-char hex-encoded). @@ -33,7 +33,7 @@ func GetObject(ctx context.Context, repo gitserver.Repo, objectName string) (oid return Mocks.GetObject(objectName) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: GetObject") + span, ctx := ot.StartSpanFromContext(ctx, "Git: GetObject") span.SetTag("objectName", objectName) defer span.Finish() diff --git a/internal/vcs/git/refs.go b/internal/vcs/git/refs.go index 438529a31d1c..eec0b2e093f3 100644 --- a/internal/vcs/git/refs.go +++ b/internal/vcs/git/refs.go @@ -10,12 +10,12 @@ import ( "time" "github.com/avelino/slugify" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/rainycape/unidecode" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs" ) @@ -145,7 +145,7 @@ func (f branchFilter) add(list []string) { // ListBranches returns a list of all branches in the repository. func ListBranches(ctx context.Context, repo gitserver.Repo, opt BranchesOptions) ([]*Branch, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Branches") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Branches") span.SetTag("Opt", opt) defer span.Finish() @@ -216,7 +216,7 @@ func branches(ctx context.Context, repo gitserver.Repo, args ...string) ([]strin // GetBehindAhead returns the behind/ahead commit counts information for right vs. left (both Git // revspecs). func GetBehindAhead(ctx context.Context, repo gitserver.Repo, left, right string) (*BehindAhead, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: BehindAhead") + span, ctx := ot.StartSpanFromContext(ctx, "Git: BehindAhead") defer span.Finish() if err := checkSpecArgSafety(left); err != nil { @@ -246,7 +246,7 @@ func GetBehindAhead(ctx context.Context, repo gitserver.Repo, left, right string // ListTags returns a list of all tags in the repository. func ListTags(ctx context.Context, repo gitserver.Repo) ([]*Tag, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Tags") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Tags") defer span.Finish() // Support both lightweight tags and tag objects. For creatordate, use an %(if) to prefer the @@ -294,7 +294,7 @@ func (p byteSlices) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // ListRefs returns a list of all refs in the repository. func ListRefs(ctx context.Context, repo gitserver.Repo) ([]Ref, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ListRefs") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ListRefs") defer span.Finish() return showRef(ctx, repo) } diff --git a/internal/vcs/git/revisions.go b/internal/vcs/git/revisions.go index 9d6429ee4df8..bf983a20a2f2 100644 --- a/internal/vcs/git/revisions.go +++ b/internal/vcs/git/revisions.go @@ -5,10 +5,10 @@ import ( "context" "fmt" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs" ) @@ -62,7 +62,7 @@ func ResolveRevision(ctx context.Context, repo gitserver.Repo, remoteURLFunc fun return Mocks.ResolveRevision(spec, opt) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ResolveRevision") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ResolveRevision") span.SetTag("Spec", spec) span.SetTag("Opt", fmt.Sprintf("%+v", opt)) defer span.Finish() diff --git a/internal/vcs/git/shortlog.go b/internal/vcs/git/shortlog.go index 43ee1be3b730..1089711fbdf1 100644 --- a/internal/vcs/git/shortlog.go +++ b/internal/vcs/git/shortlog.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - opentracing "github.com/opentracing/opentracing-go" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // ShortLogOptions contains options for (Repository).ShortLog. @@ -32,7 +32,7 @@ func (p *PersonCount) String() string { // ShortLog returns the per-author commit statistics of the repo. func ShortLog(ctx context.Context, repo gitserver.Repo, opt ShortLogOptions) ([]*PersonCount, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ShortLog") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ShortLog") span.SetTag("Opt", opt) defer span.Finish() diff --git a/internal/vcs/git/tree.go b/internal/vcs/git/tree.go index 1d093c8e1c2b..d1e9be245b9b 100644 --- a/internal/vcs/git/tree.go +++ b/internal/vcs/git/tree.go @@ -15,17 +15,17 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/format/config" "github.com/golang/groupcache/lru" - opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" "github.com/sourcegraph/sourcegraph/internal/vcs/util" ) // Lstat returns a FileInfo describing the named file at commit. If the file is a symbolic link, the // returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. func Lstat(ctx context.Context, repo gitserver.Repo, commit api.CommitID, path string) (os.FileInfo, error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Lstat") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Lstat") span.SetTag("Commit", commit) span.SetTag("Path", path) defer span.Finish() @@ -62,7 +62,7 @@ func Stat(ctx context.Context, repo gitserver.Repo, commit api.CommitID, path st return Mocks.Stat(commit, path) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: Stat") + span, ctx := ot.StartSpanFromContext(ctx, "Git: Stat") span.SetTag("Commit", commit) span.SetTag("Path", path) defer span.Finish() @@ -103,7 +103,7 @@ func ReadDir(ctx context.Context, repo gitserver.Repo, commit api.CommitID, path return Mocks.ReadDir(commit, path, recurse) } - span, ctx := opentracing.StartSpanFromContext(ctx, "Git: ReadDir") + span, ctx := ot.StartSpanFromContext(ctx, "Git: ReadDir") span.SetTag("Commit", commit) span.SetTag("Path", path) span.SetTag("Recurse", recurse) diff --git a/internal/vfsutil/gitserver.go b/internal/vfsutil/gitserver.go index 9e4a0ab82175..8725c8d8c7da 100644 --- a/internal/vfsutil/gitserver.go +++ b/internal/vfsutil/gitserver.go @@ -7,12 +7,12 @@ import ( "os" "strings" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sourcegraph/sourcegraph/internal/api" "github.com/sourcegraph/sourcegraph/internal/gitserver" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" ) // NewGitServer returns a VFS to repo at commit. It is backed by an archive @@ -76,7 +76,7 @@ func (opts *ArchiveOpts) cacheKey() string { // GitServerFetchArchive fetches an archive of a repositories contents from gitserver. func GitServerFetchArchive(ctx context.Context, opts ArchiveOpts) (archive *os.File, cacheEvicter Evicter, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "Archive Fetch") + span, ctx := ot.StartSpanFromContext(ctx, "Archive Fetch") ext.Component.Set(span, "gitserver") span.SetTag("repo", opts.Repo) span.SetTag("commit", opts.Commit) diff --git a/internal/vfsutil/zip.go b/internal/vfsutil/zip.go index c3169738578f..abd632ced93d 100644 --- a/internal/vfsutil/zip.go +++ b/internal/vfsutil/zip.go @@ -8,8 +8,8 @@ import ( "golang.org/x/net/context/ctxhttp" "github.com/pkg/errors" + "github.com/sourcegraph/sourcegraph/internal/trace/ot" - opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" ) @@ -17,7 +17,7 @@ import ( // on disk) and returns a new VFS backed by that zip archive. func NewZipVFS(url string, onFetchStart, onFetchFailed func(), evictOnClose bool) (*ArchiveFS, error) { fetch := func(ctx context.Context) (ar *archiveReader, err error) { - span, ctx := opentracing.StartSpanFromContext(ctx, "zip Fetch") + span, ctx := ot.StartSpanFromContext(ctx, "zip Fetch") ext.Component.Set(span, "zipvfs") span.SetTag("url", url) defer func() { diff --git a/schema/schema.go b/schema/schema.go index 789989fd2a13..b22b4992b6f5 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -668,6 +668,14 @@ type OAuthIdentity struct { Type string `json:"type"` } +// ObservabilityTracing description: Controls the settings for distributed tracing. +type ObservabilityTracing struct { + // Debug description: Turns on debug logging of opentracing client requests. This can be useful for debugging connectivity issues between the tracing client and the Jaeger agent, the performance overhead of tracing, and other issues related to the use of distributed tracing. + Debug bool `json:"debug,omitempty"` + // Sampling description: Determines the requests for which distributed traces are recorded. "none" (default) turns off tracing entirely. "selective" sends traces whenever `?trace=1` is present in the URL. "all" sends traces on every request. Note that this only affects the behavior of the distributed tracing client. The Jaeger instance must be running for traces to be collected (as described in the Sourcegraph installation instructions). Additional downsampling can be configured in Jaeger, itself (https://www.jaegertracing.io/docs/1.17/sampling) + Sampling string `json:"sampling,omitempty"` +} + // OpenIDConnectAuthProvider description: Configures the OpenID Connect authentication provider for SSO. type OpenIDConnectAuthProvider struct { // ClientID description: The client ID for the OpenID Connect client for this site. @@ -959,9 +967,9 @@ type SiteConfiguration struct { HtmlHeadTop string `json:"htmlHeadTop,omitempty"` // LicenseKey description: The license key associated with a Sourcegraph product subscription, which is necessary to activate Sourcegraph Enterprise functionality. To obtain this value, contact Sourcegraph to purchase a subscription. To escape the value into a JSON string, you may want to use a tool like https://json-escape-text.now.sh. LicenseKey string `json:"licenseKey,omitempty"` - // LightstepAccessToken description: Access token for sending traces to LightStep. + // LightstepAccessToken description: DEPRECATED. Use Jaeger (`"observability.tracing": { "sampling": "selective" }`), instead. LightstepAccessToken string `json:"lightstepAccessToken,omitempty"` - // LightstepProject description: The project ID on LightStep that corresponds to the `lightstepAccessToken`, only for generating links to traces. For example, if `lightstepProject` is `mycompany-prod`, all HTTP responses from Sourcegraph will include an X-Trace header with the URL to the trace on LightStep, of the form `https://app.lightstep.com/mycompany-prod/trace?span_guid=...&at_micros=...`. + // LightstepProject description: DEPRECATED. Use Jaeger (`"observability.tracing": { "sampling": "selective" }`), instead. LightstepProject string `json:"lightstepProject,omitempty"` // Log description: Configuration for logging and alerting, including to external services. Log *Log `json:"log,omitempty"` @@ -969,6 +977,8 @@ type SiteConfiguration struct { LsifEnforceAuth bool `json:"lsifEnforceAuth,omitempty"` // MaxReposToSearch description: The maximum number of repositories to search across. The user is prompted to narrow their query if exceeded. Any value less than or equal to zero means unlimited. MaxReposToSearch int `json:"maxReposToSearch,omitempty"` + // ObservabilityTracing description: Controls the settings for distributed tracing. + ObservabilityTracing *ObservabilityTracing `json:"observability.tracing,omitempty"` // ParentSourcegraph description: URL to fetch unreachable repository details from. Defaults to "https://sourcegraph.com" ParentSourcegraph *ParentSourcegraph `json:"parentSourcegraph,omitempty"` // PermissionsBackgroundSync description: Sync code host repository and user permissions in the background. @@ -985,19 +995,7 @@ type SiteConfiguration struct { SearchLargeFiles []string `json:"search.largeFiles,omitempty"` // UpdateChannel description: The channel on which to automatically check for Sourcegraph updates. UpdateChannel string `json:"update.channel,omitempty"` - // UseJaeger description: Use local Jaeger instance for tracing. Kubernetes cluster deployments only. - // - // After enabling Jaeger and updating your Kubernetes cluster, `kubectl get pods` - // should display pods prefixed with `jaeger-cassandra`, - // `jaeger-collector`, and `jaeger-query`. `jaeger-collector` will start - // crashing until you initialize the Cassandra DB. To do so, do the - // following: - // - // 1. Install [`cqlsh`](https://pypi.python.org/pypi/cqlsh). - // 1. `kubectl port-forward $(kubectl get pods | grep jaeger-cassandra | awk '{ print $1 }') 9042` - // 1. `git clone https://github.com/uber/jaeger && cd jaeger && MODE=test ./plugin/storage/cassandra/schema/create.sh | cqlsh` - // 1. `kubectl port-forward $(kubectl get pods | grep jaeger-query | awk '{ print $1 }') 16686` - // 1. Go to http://localhost:16686 to view the Jaeger dashboard. + // UseJaeger description: DEPRECATED. Use `"observability.tracing": { "sampling": "all" }`, instead. Enables Jaeger tracing. UseJaeger bool `json:"useJaeger,omitempty"` } diff --git a/schema/site.schema.json b/schema/site.schema.json index cb61db94994f..9d83a0411ef8 100644 --- a/schema/site.schema.json +++ b/schema/site.schema.json @@ -569,21 +569,38 @@ "examples": ["https://sourcegraph.example.com"] }, "lightstepAccessToken": { - "description": "Access token for sending traces to LightStep.", + "description": "DEPRECATED. Use Jaeger (`\"observability.tracing\": { \"sampling\": \"selective\" }`), instead.", "type": "string", "group": "Misc." }, "lightstepProject": { - "description": "The project ID on LightStep that corresponds to the `lightstepAccessToken`, only for generating links to traces. For example, if `lightstepProject` is `mycompany-prod`, all HTTP responses from Sourcegraph will include an X-Trace header with the URL to the trace on LightStep, of the form `https://app.lightstep.com/mycompany-prod/trace?span_guid=...&at_micros=...`.", + "description": "DEPRECATED. Use Jaeger (`\"observability.tracing\": { \"sampling\": \"selective\" }`), instead.", "type": "string", "examples": ["myproject"], "group": "Misc." }, "useJaeger": { - "description": "Use local Jaeger instance for tracing. Kubernetes cluster deployments only.\n\nAfter enabling Jaeger and updating your Kubernetes cluster, `kubectl get pods`\nshould display pods prefixed with `jaeger-cassandra`,\n`jaeger-collector`, and `jaeger-query`. `jaeger-collector` will start\ncrashing until you initialize the Cassandra DB. To do so, do the\nfollowing:\n\n1. Install [`cqlsh`](https://pypi.python.org/pypi/cqlsh).\n1. `kubectl port-forward $(kubectl get pods | grep jaeger-cassandra | awk '{ print $1 }') 9042`\n1. `git clone https://github.com/uber/jaeger && cd jaeger && MODE=test ./plugin/storage/cassandra/schema/create.sh | cqlsh`\n1. `kubectl port-forward $(kubectl get pods | grep jaeger-query | awk '{ print $1 }') 16686`\n1. Go to http://localhost:16686 to view the Jaeger dashboard.", + "description": "DEPRECATED. Use `\"observability.tracing\": { \"sampling\": \"all\" }`, instead. Enables Jaeger tracing.", "type": "boolean", "group": "Misc." }, + "observability.tracing": { + "description": "Controls the settings for distributed tracing.", + "type": "object", + "properties": { + "sampling": { + "description": "Determines the requests for which distributed traces are recorded. \"none\" (default) turns off tracing entirely. \"selective\" sends traces whenever `?trace=1` is present in the URL. \"all\" sends traces on every request. Note that this only affects the behavior of the distributed tracing client. The Jaeger instance must be running for traces to be collected (as described in the Sourcegraph installation instructions). Additional downsampling can be configured in Jaeger, itself (https://www.jaegertracing.io/docs/1.17/sampling)", + "type": "string", + "enum": ["selective", "all", "none"], + "default": "selective" + }, + "debug": { + "description": "Turns on debug logging of opentracing client requests. This can be useful for debugging connectivity issues between the tracing client and the Jaeger agent, the performance overhead of tracing, and other issues related to the use of distributed tracing.", + "type": "boolean", + "default": false + } + } + }, "htmlHeadTop": { "description": "HTML to inject at the top of the `` element on each page, for analytics scripts", "type": "string", diff --git a/schema/site_stringdata.go b/schema/site_stringdata.go index b337a9227303..69b3fa2c0b7e 100644 --- a/schema/site_stringdata.go +++ b/schema/site_stringdata.go @@ -574,21 +574,38 @@ const SiteSchemaJSON = `{ "examples": ["https://sourcegraph.example.com"] }, "lightstepAccessToken": { - "description": "Access token for sending traces to LightStep.", + "description": "DEPRECATED. Use Jaeger (` + "`" + `\"observability.tracing\": { \"sampling\": \"selective\" }` + "`" + `), instead.", "type": "string", "group": "Misc." }, "lightstepProject": { - "description": "The project ID on LightStep that corresponds to the ` + "`" + `lightstepAccessToken` + "`" + `, only for generating links to traces. For example, if ` + "`" + `lightstepProject` + "`" + ` is ` + "`" + `mycompany-prod` + "`" + `, all HTTP responses from Sourcegraph will include an X-Trace header with the URL to the trace on LightStep, of the form ` + "`" + `https://app.lightstep.com/mycompany-prod/trace?span_guid=...&at_micros=...` + "`" + `.", + "description": "DEPRECATED. Use Jaeger (` + "`" + `\"observability.tracing\": { \"sampling\": \"selective\" }` + "`" + `), instead.", "type": "string", "examples": ["myproject"], "group": "Misc." }, "useJaeger": { - "description": "Use local Jaeger instance for tracing. Kubernetes cluster deployments only.\n\nAfter enabling Jaeger and updating your Kubernetes cluster, ` + "`" + `kubectl get pods` + "`" + `\nshould display pods prefixed with ` + "`" + `jaeger-cassandra` + "`" + `,\n` + "`" + `jaeger-collector` + "`" + `, and ` + "`" + `jaeger-query` + "`" + `. ` + "`" + `jaeger-collector` + "`" + ` will start\ncrashing until you initialize the Cassandra DB. To do so, do the\nfollowing:\n\n1. Install [` + "`" + `cqlsh` + "`" + `](https://pypi.python.org/pypi/cqlsh).\n1. ` + "`" + `kubectl port-forward $(kubectl get pods | grep jaeger-cassandra | awk '{ print $1 }') 9042` + "`" + `\n1. ` + "`" + `git clone https://github.com/uber/jaeger && cd jaeger && MODE=test ./plugin/storage/cassandra/schema/create.sh | cqlsh` + "`" + `\n1. ` + "`" + `kubectl port-forward $(kubectl get pods | grep jaeger-query | awk '{ print $1 }') 16686` + "`" + `\n1. Go to http://localhost:16686 to view the Jaeger dashboard.", + "description": "DEPRECATED. Use ` + "`" + `\"observability.tracing\": { \"sampling\": \"all\" }` + "`" + `, instead. Enables Jaeger tracing.", "type": "boolean", "group": "Misc." }, + "observability.tracing": { + "description": "Controls the settings for distributed tracing.", + "type": "object", + "properties": { + "sampling": { + "description": "Determines the requests for which distributed traces are recorded. \"none\" (default) turns off tracing entirely. \"selective\" sends traces whenever ` + "`" + `?trace=1` + "`" + ` is present in the URL. \"all\" sends traces on every request. Note that this only affects the behavior of the distributed tracing client. The Jaeger instance must be running for traces to be collected (as described in the Sourcegraph installation instructions). Additional downsampling can be configured in Jaeger, itself (https://www.jaegertracing.io/docs/1.17/sampling)", + "type": "string", + "enum": ["selective", "all", "none"], + "default": "selective" + }, + "debug": { + "description": "Turns on debug logging of opentracing client requests. This can be useful for debugging connectivity issues between the tracing client and the Jaeger agent, the performance overhead of tracing, and other issues related to the use of distributed tracing.", + "type": "boolean", + "default": false + } + } + }, "htmlHeadTop": { "description": "HTML to inject at the top of the ` + "`" + `` + "`" + ` element on each page, for analytics scripts", "type": "string", diff --git a/web/src/backend/graphql.tsx b/web/src/backend/graphql.tsx index 1403d33c768c..e59c7b5306c9 100644 --- a/web/src/backend/graphql.tsx +++ b/web/src/backend/graphql.tsx @@ -6,6 +6,7 @@ const getHeaders = (): { [header: string]: string } => ({ ...window.context.xhrHeaders, Accept: 'application/json', 'Content-Type': 'application/json', + 'X-Sourcegraph-Should-Trace': new URLSearchParams(window.location.search).get('trace') || 'false', }) /**