You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

846 lines
26 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // This package is OBSOLETE. See https://godoc.org/go.opencensus.io/trace; and use
  15. // OpenCensus Stackdriver exporter, https://godoc.org/contrib.go.opencensus.io/exporter/stackdriver.
  16. //
  17. // Package trace is a Google Stackdriver Trace library.
  18. //
  19. // This package is still experimental and subject to change.
  20. // See https://cloud.google.com/trace/api/#data_model for a discussion of traces
  21. // and spans.
  22. //
  23. // To initialize a client that connects to the Stackdriver Trace server, use the
  24. // NewClient function. Generally you will want to do this on program
  25. // initialization.
  26. //
  27. // import "cloud.google.com/go/trace"
  28. // ...
  29. // traceClient, err = trace.NewClient(ctx, projectID)
  30. //
  31. // Calling SpanFromRequest will create a new trace span for an incoming HTTP
  32. // request. If the request contains a trace context header, it is used to
  33. // determine the trace ID. Otherwise, a new trace ID is created.
  34. //
  35. // func handler(w http.ResponseWriter, r *http.Request) {
  36. // span := traceClient.SpanFromRequest(r)
  37. // defer span.Finish()
  38. // ...
  39. // }
  40. //
  41. // SpanFromRequest and NewSpan returns nil if the *Client is nil, so you can disable
  42. // tracing by not initializing your *Client variable. All of the exported
  43. // functions on *Span do nothing when the *Span is nil.
  44. //
  45. // If you need to start traces that don't correspond to an incoming HTTP request,
  46. // you can use NewSpan to create a root-level span.
  47. //
  48. // span := traceClient.NewSpan("span name")
  49. // defer span.Finish()
  50. //
  51. // Although a trace span object is created for every request, only a subset of
  52. // traces are uploaded to the server, for efficiency. By default, the requests
  53. // that are traced are those with the tracing bit set in the options field of
  54. // the trace context header. Ideally, you should override this behaviour by
  55. // calling SetSamplingPolicy. NewLimitedSampler returns an implementation of
  56. // SamplingPolicy which traces requests that have the tracing bit set, and also
  57. // randomly traces a specified fraction of requests. Additionally, it sets a
  58. // limit on the number of requests traced per second. The following example
  59. // traces one in every thousand requests, up to a limit of 5 per second.
  60. //
  61. // p, err := trace.NewLimitedSampler(0.001, 5)
  62. // traceClient.SetSamplingPolicy(p)
  63. //
  64. // You can create a new span as a child of an existing span with NewChild.
  65. //
  66. // childSpan := span.NewChild(name)
  67. // ...
  68. // childSpan.Finish()
  69. //
  70. // When sending an HTTP request to another server, NewRemoteChild will create
  71. // a span to represent the time the current program waits for the request to
  72. // complete, and attach a header to the outgoing request so that the trace will
  73. // be propagated to the destination server.
  74. //
  75. // childSpan := span.NewRemoteChild(&httpRequest)
  76. // ...
  77. // childSpan.Finish()
  78. //
  79. // Alternatively, if you have access to the X-Cloud-Trace-Context header value
  80. // but not the underlying HTTP request (this can happen if you are using a
  81. // different transport or messaging protocol, such as gRPC), you can use
  82. // SpanFromHeader instead of SpanFromRequest. In that case, you will need to
  83. // specify the span name explicility, since it cannot be constructed from the
  84. // HTTP request's URL and method.
  85. //
  86. // func handler(r *somepkg.Request) {
  87. // span := traceClient.SpanFromHeader("span name", r.TraceContext())
  88. // defer span.Finish()
  89. // ...
  90. // }
  91. //
  92. // Spans can contain a map from keys to values that have useful information
  93. // about the span. The elements of this map are called labels. Some labels,
  94. // whose keys all begin with the string "trace.cloud.google.com/", are set
  95. // automatically in the following ways:
  96. //
  97. // - SpanFromRequest sets some labels to data about the incoming request.
  98. //
  99. // - NewRemoteChild sets some labels to data about the outgoing request.
  100. //
  101. // - Finish sets a label to a stack trace, if the stack trace option is enabled
  102. // in the incoming trace header.
  103. //
  104. // - The WithResponse option sets some labels to data about a response.
  105. // You can also set labels using SetLabel. If a label is given a value
  106. // automatically and by SetLabel, the automatically-set value is used.
  107. //
  108. // span.SetLabel(key, value)
  109. //
  110. // The WithResponse option can be used when Finish is called.
  111. //
  112. // childSpan := span.NewRemoteChild(outgoingReq)
  113. // resp, err := http.DefaultClient.Do(outgoingReq)
  114. // ...
  115. // childSpan.Finish(trace.WithResponse(resp))
  116. //
  117. // When a span created by SpanFromRequest or SpanFromHeader is finished, the
  118. // finished spans in the corresponding trace -- the span itself and its
  119. // descendants -- are uploaded to the Stackdriver Trace server using the
  120. // *Client that created the span. Finish returns immediately, and uploading
  121. // occurs asynchronously. You can use the FinishWait function instead to wait
  122. // until uploading has finished.
  123. //
  124. // err := span.FinishWait()
  125. //
  126. // Using contexts to pass *trace.Span objects through your program will often
  127. // be a better approach than passing them around explicitly. This allows trace
  128. // spans, and other request-scoped or part-of-request-scoped values, to be
  129. // easily passed through API boundaries. Various Google Cloud libraries will
  130. // retrieve trace spans from contexts and automatically create child spans for
  131. // API requests.
  132. // See https://blog.golang.org/context for more discussion of contexts.
  133. // A derived context containing a trace span can be created using NewContext.
  134. //
  135. // span := traceClient.SpanFromRequest(r)
  136. // ctx = trace.NewContext(ctx, span)
  137. //
  138. // The span can be retrieved from a context elsewhere in the program using
  139. // FromContext.
  140. //
  141. // func foo(ctx context.Context) {
  142. // span := trace.FromContext(ctx).NewChild("in foo")
  143. // defer span.Finish()
  144. // ...
  145. // }
  146. //
  147. package trace // import "cloud.google.com/go/trace"
  148. import (
  149. "crypto/rand"
  150. "encoding/binary"
  151. "encoding/json"
  152. "fmt"
  153. "log"
  154. "net/http"
  155. "runtime"
  156. "strconv"
  157. "strings"
  158. "sync"
  159. "sync/atomic"
  160. "time"
  161. "golang.org/x/net/context"
  162. api "google.golang.org/api/cloudtrace/v1"
  163. "google.golang.org/api/gensupport"
  164. "google.golang.org/api/option"
  165. "google.golang.org/api/support/bundler"
  166. htransport "google.golang.org/api/transport/http"
  167. )
  168. const (
  169. httpHeader = `X-Cloud-Trace-Context`
  170. userAgent = `gcloud-golang-trace/20160501`
  171. cloudPlatformScope = `https://www.googleapis.com/auth/cloud-platform`
  172. spanKindClient = `RPC_CLIENT`
  173. spanKindServer = `RPC_SERVER`
  174. spanKindUnspecified = `SPAN_KIND_UNSPECIFIED`
  175. maxStackFrames = 20
  176. labelAgent = `trace.cloud.google.com/agent`
  177. )
  178. // Stackdriver Trace API predefined labels.
  179. const (
  180. LabelComponent = `trace.cloud.google.com/component`
  181. LabelErrorMessage = `trace.cloud.google.com/error/message`
  182. LabelErrorName = `trace.cloud.google.com/error/name`
  183. LabelHTTPClientCity = `trace.cloud.google.com/http/client_city`
  184. LabelHTTPClientCountry = `trace.cloud.google.com/http/client_country`
  185. LabelHTTPClientProtocol = `trace.cloud.google.com/http/client_protocol`
  186. LabelHTTPClientRegion = `trace.cloud.google.com/http/client_region`
  187. LabelHTTPHost = `trace.cloud.google.com/http/host`
  188. LabelHTTPMethod = `trace.cloud.google.com/http/method`
  189. LabelHTTPRedirectedURL = `trace.cloud.google.com/http/redirected_url`
  190. LabelHTTPRequestSize = `trace.cloud.google.com/http/request/size`
  191. LabelHTTPResponseSize = `trace.cloud.google.com/http/response/size`
  192. LabelHTTPStatusCode = `trace.cloud.google.com/http/status_code`
  193. LabelHTTPURL = `trace.cloud.google.com/http/url`
  194. LabelHTTPUserAgent = `trace.cloud.google.com/http/user_agent`
  195. LabelPID = `trace.cloud.google.com/pid`
  196. LabelSamplingPolicy = `trace.cloud.google.com/sampling_policy`
  197. LabelSamplingWeight = `trace.cloud.google.com/sampling_weight`
  198. LabelStackTrace = `trace.cloud.google.com/stacktrace`
  199. LabelTID = `trace.cloud.google.com/tid`
  200. )
  201. const (
  202. // ScopeTraceAppend grants permissions to write trace data for a project.
  203. ScopeTraceAppend = "https://www.googleapis.com/auth/trace.append"
  204. // ScopeCloudPlatform grants permissions to view and manage your data
  205. // across Google Cloud Platform services.
  206. ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform"
  207. )
  208. type contextKey struct{}
  209. type stackLabelValue struct {
  210. Frames []stackFrame `json:"stack_frame"`
  211. }
  212. type stackFrame struct {
  213. Class string `json:"class_name,omitempty"`
  214. Method string `json:"method_name"`
  215. Filename string `json:"file_name"`
  216. Line int64 `json:"line_number"`
  217. }
  218. var (
  219. spanIDCounter uint64
  220. spanIDIncrement uint64
  221. )
  222. func init() {
  223. // Set spanIDCounter and spanIDIncrement to random values. nextSpanID will
  224. // return an arithmetic progression using these values, skipping zero. We set
  225. // the LSB of spanIDIncrement to 1, so that the cycle length is 2^64.
  226. binary.Read(rand.Reader, binary.LittleEndian, &spanIDCounter)
  227. binary.Read(rand.Reader, binary.LittleEndian, &spanIDIncrement)
  228. spanIDIncrement |= 1
  229. // Attach hook for autogenerated Google API calls. This will automatically
  230. // create trace spans for API calls if there is a trace in the context.
  231. gensupport.RegisterHook(requestHook)
  232. }
  233. func requestHook(ctx context.Context, req *http.Request) func(resp *http.Response) {
  234. span := FromContext(ctx)
  235. if span == nil || req == nil {
  236. return nil
  237. }
  238. span = span.NewRemoteChild(req)
  239. return func(resp *http.Response) {
  240. if resp != nil {
  241. span.Finish(WithResponse(resp))
  242. } else {
  243. span.Finish()
  244. }
  245. }
  246. }
  247. // nextSpanID returns a new span ID. It will never return zero.
  248. func nextSpanID() uint64 {
  249. var id uint64
  250. for id == 0 {
  251. id = atomic.AddUint64(&spanIDCounter, spanIDIncrement)
  252. }
  253. return id
  254. }
  255. // nextTraceID returns a new trace ID.
  256. func nextTraceID() string {
  257. id1 := nextSpanID()
  258. id2 := nextSpanID()
  259. return fmt.Sprintf("%016x%016x", id1, id2)
  260. }
  261. // Client is a client for uploading traces to the Google Stackdriver Trace service.
  262. // A nil Client will no-op for all of its methods.
  263. type Client struct {
  264. service *api.Service
  265. projectID string
  266. policy SamplingPolicy
  267. bundler *bundler.Bundler
  268. }
  269. // NewClient creates a new Google Stackdriver Trace client.
  270. func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) {
  271. o := []option.ClientOption{
  272. option.WithScopes(cloudPlatformScope),
  273. option.WithUserAgent(userAgent),
  274. }
  275. o = append(o, opts...)
  276. hc, basePath, err := htransport.NewClient(ctx, o...)
  277. if err != nil {
  278. return nil, fmt.Errorf("creating HTTP client for Google Stackdriver Trace API: %v", err)
  279. }
  280. apiService, err := api.New(hc)
  281. if err != nil {
  282. return nil, fmt.Errorf("creating Google Stackdriver Trace API client: %v", err)
  283. }
  284. if basePath != "" {
  285. // An option set a basepath, so override api.New's default.
  286. apiService.BasePath = basePath
  287. }
  288. c := &Client{
  289. service: apiService,
  290. projectID: projectID,
  291. }
  292. bundler := bundler.NewBundler((*api.Trace)(nil), func(bundle interface{}) {
  293. traces := bundle.([]*api.Trace)
  294. err := c.upload(traces)
  295. if err != nil {
  296. log.Printf("failed to upload %d traces to the Cloud Trace server: %v", len(traces), err)
  297. }
  298. })
  299. bundler.DelayThreshold = 2 * time.Second
  300. bundler.BundleCountThreshold = 100
  301. // We're not measuring bytes here, we're counting traces and spans as one "byte" each.
  302. bundler.BundleByteThreshold = 1000
  303. bundler.BundleByteLimit = 1000
  304. bundler.BufferedByteLimit = 10000
  305. c.bundler = bundler
  306. return c, nil
  307. }
  308. // SetSamplingPolicy sets the SamplingPolicy that determines how often traces
  309. // are initiated by this client.
  310. func (c *Client) SetSamplingPolicy(p SamplingPolicy) {
  311. if c != nil {
  312. c.policy = p
  313. }
  314. }
  315. // SpanFromHeader returns a new trace span based on a provided request header
  316. // value or nil iff the client is nil.
  317. //
  318. // The trace information and identifiers will be read from the header value.
  319. // Otherwise, a new trace ID is made and the parent span ID is zero.
  320. // For the exact format of the header value, see
  321. // https://cloud.google.com/trace/docs/support#how_do_i_force_a_request_to_be_traced
  322. //
  323. // The name of the new span is provided as an argument.
  324. //
  325. // If a non-nil sampling policy has been set in the client, it can override
  326. // the options set in the header and choose whether to trace the request.
  327. //
  328. // If the header doesn't have existing tracing information, then a *Span is
  329. // returned anyway, but it will not be uploaded to the server, just as when
  330. // calling SpanFromRequest on an untraced request.
  331. //
  332. // Most users using HTTP should use SpanFromRequest, rather than
  333. // SpanFromHeader, since it provides additional functionality for HTTP
  334. // requests. In particular, it will set various pieces of request information
  335. // as labels on the *Span, which is not available from the header alone.
  336. func (c *Client) SpanFromHeader(name string, header string) *Span {
  337. if c == nil {
  338. return nil
  339. }
  340. traceID, parentSpanID, options, _, ok := traceInfoFromHeader(header)
  341. if !ok {
  342. traceID = nextTraceID()
  343. }
  344. t := &trace{
  345. traceID: traceID,
  346. client: c,
  347. globalOptions: options,
  348. localOptions: options,
  349. }
  350. span := startNewChild(name, t, parentSpanID)
  351. span.span.Kind = spanKindServer
  352. span.rootSpan = true
  353. configureSpanFromPolicy(span, c.policy, ok)
  354. return span
  355. }
  356. // SpanFromRequest returns a new trace span for an HTTP request or nil
  357. // iff the client is nil.
  358. //
  359. // If the incoming HTTP request contains a trace context header, the trace ID,
  360. // parent span ID, and tracing options will be read from that header.
  361. // Otherwise, a new trace ID is made and the parent span ID is zero.
  362. //
  363. // If a non-nil sampling policy has been set in the client, it can override the
  364. // options set in the header and choose whether to trace the request.
  365. //
  366. // If the request is not being traced, then a *Span is returned anyway, but it
  367. // will not be uploaded to the server -- it is only useful for propagating
  368. // trace context to child requests and for getting the TraceID. All its
  369. // methods can still be called -- the Finish, FinishWait, and SetLabel methods
  370. // do nothing. NewChild does nothing, and returns the same *Span. TraceID
  371. // works as usual.
  372. func (c *Client) SpanFromRequest(r *http.Request) *Span {
  373. if c == nil {
  374. return nil
  375. }
  376. traceID, parentSpanID, options, _, ok := traceInfoFromHeader(r.Header.Get(httpHeader))
  377. if !ok {
  378. traceID = nextTraceID()
  379. }
  380. t := &trace{
  381. traceID: traceID,
  382. client: c,
  383. globalOptions: options,
  384. localOptions: options,
  385. }
  386. span := startNewChildWithRequest(r, t, parentSpanID)
  387. span.span.Kind = spanKindServer
  388. span.rootSpan = true
  389. configureSpanFromPolicy(span, c.policy, ok)
  390. return span
  391. }
  392. // NewSpan returns a new trace span with the given name or nil iff the
  393. // client is nil.
  394. //
  395. // A new trace and span ID is generated to trace the span.
  396. // Returned span need to be finished by calling Finish or FinishWait.
  397. func (c *Client) NewSpan(name string) *Span {
  398. if c == nil {
  399. return nil
  400. }
  401. t := &trace{
  402. traceID: nextTraceID(),
  403. client: c,
  404. localOptions: optionTrace,
  405. globalOptions: optionTrace,
  406. }
  407. span := startNewChild(name, t, 0)
  408. span.span.Kind = spanKindUnspecified
  409. span.rootSpan = true
  410. configureSpanFromPolicy(span, c.policy, false)
  411. return span
  412. }
  413. func configureSpanFromPolicy(s *Span, p SamplingPolicy, ok bool) {
  414. if p == nil {
  415. return
  416. }
  417. d := p.Sample(Parameters{HasTraceHeader: ok})
  418. if d.Trace {
  419. // Turn on tracing locally, and in child requests.
  420. s.trace.localOptions |= optionTrace
  421. s.trace.globalOptions |= optionTrace
  422. } else {
  423. // Turn off tracing locally.
  424. s.trace.localOptions = 0
  425. return
  426. }
  427. if d.Sample {
  428. // This trace is in the random sample, so set the labels.
  429. s.SetLabel(LabelSamplingPolicy, d.Policy)
  430. s.SetLabel(LabelSamplingWeight, fmt.Sprint(d.Weight))
  431. }
  432. }
  433. // NewContext returns a derived context containing the span.
  434. func NewContext(ctx context.Context, s *Span) context.Context {
  435. if s == nil {
  436. return ctx
  437. }
  438. return context.WithValue(ctx, contextKey{}, s)
  439. }
  440. // FromContext returns the span contained in the context, or nil.
  441. func FromContext(ctx context.Context) *Span {
  442. s, _ := ctx.Value(contextKey{}).(*Span)
  443. return s
  444. }
  445. func traceInfoFromHeader(h string) (traceID string, spanID uint64, options optionFlags, optionsOk bool, ok bool) {
  446. // See https://cloud.google.com/trace/docs/faq for the header format.
  447. // Return if the header is empty or missing, or if the header is unreasonably
  448. // large, to avoid making unnecessary copies of a large string.
  449. if h == "" || len(h) > 200 {
  450. return "", 0, 0, false, false
  451. }
  452. // Parse the trace id field.
  453. slash := strings.Index(h, `/`)
  454. if slash == -1 {
  455. return "", 0, 0, false, false
  456. }
  457. traceID, h = h[:slash], h[slash+1:]
  458. // Parse the span id field.
  459. spanstr := h
  460. semicolon := strings.Index(h, `;`)
  461. if semicolon != -1 {
  462. spanstr, h = h[:semicolon], h[semicolon+1:]
  463. }
  464. spanID, err := strconv.ParseUint(spanstr, 10, 64)
  465. if err != nil {
  466. return "", 0, 0, false, false
  467. }
  468. // Parse the options field, options field is optional.
  469. if !strings.HasPrefix(h, "o=") {
  470. return traceID, spanID, 0, false, true
  471. }
  472. o, err := strconv.ParseUint(h[2:], 10, 64)
  473. if err != nil {
  474. return "", 0, 0, false, false
  475. }
  476. options = optionFlags(o)
  477. return traceID, spanID, options, true, true
  478. }
  479. type optionFlags uint32
  480. const (
  481. optionTrace optionFlags = 1 << iota
  482. optionStack
  483. )
  484. type trace struct {
  485. mu sync.Mutex
  486. client *Client
  487. traceID string
  488. globalOptions optionFlags // options that will be passed to any child requests
  489. localOptions optionFlags // options applied in this server
  490. spans []*Span // finished spans for this trace.
  491. }
  492. // finish appends s to t.spans. If s is the root span, uploads the trace to the
  493. // server.
  494. func (t *trace) finish(s *Span, wait bool, opts ...FinishOption) error {
  495. for _, o := range opts {
  496. o.modifySpan(s)
  497. }
  498. s.end = time.Now()
  499. t.mu.Lock()
  500. t.spans = append(t.spans, s)
  501. spans := t.spans
  502. t.mu.Unlock()
  503. if s.rootSpan {
  504. if wait {
  505. return t.client.upload([]*api.Trace{t.constructTrace(spans)})
  506. }
  507. go func() {
  508. tr := t.constructTrace(spans)
  509. err := t.client.bundler.Add(tr, 1+len(spans))
  510. if err == bundler.ErrOversizedItem {
  511. err = t.client.upload([]*api.Trace{tr})
  512. }
  513. if err != nil {
  514. log.Println("error uploading trace:", err)
  515. }
  516. }()
  517. }
  518. return nil
  519. }
  520. func (t *trace) constructTrace(spans []*Span) *api.Trace {
  521. apiSpans := make([]*api.TraceSpan, len(spans))
  522. for i, sp := range spans {
  523. sp.span.StartTime = sp.start.In(time.UTC).Format(time.RFC3339Nano)
  524. sp.span.EndTime = sp.end.In(time.UTC).Format(time.RFC3339Nano)
  525. if t.localOptions&optionStack != 0 {
  526. sp.setStackLabel()
  527. }
  528. if sp.host != "" {
  529. sp.SetLabel(LabelHTTPHost, sp.host)
  530. }
  531. if sp.url != "" {
  532. sp.SetLabel(LabelHTTPURL, sp.url)
  533. }
  534. if sp.method != "" {
  535. sp.SetLabel(LabelHTTPMethod, sp.method)
  536. }
  537. if sp.statusCode != 0 {
  538. sp.SetLabel(LabelHTTPStatusCode, strconv.Itoa(sp.statusCode))
  539. }
  540. sp.SetLabel(labelAgent, userAgent)
  541. apiSpans[i] = &sp.span
  542. }
  543. return &api.Trace{
  544. ProjectId: t.client.projectID,
  545. TraceId: t.traceID,
  546. Spans: apiSpans,
  547. }
  548. }
  549. func (c *Client) upload(traces []*api.Trace) error {
  550. _, err := c.service.Projects.PatchTraces(c.projectID, &api.Traces{Traces: traces}).Do()
  551. return err
  552. }
  553. // Span contains information about one span of a trace.
  554. type Span struct {
  555. trace *trace
  556. spanMu sync.Mutex // guards span.Labels
  557. span api.TraceSpan
  558. start time.Time
  559. end time.Time
  560. rootSpan bool
  561. stack [maxStackFrames]uintptr
  562. host string
  563. method string
  564. url string
  565. statusCode int
  566. }
  567. // Traced reports whether the current span is sampled to be traced.
  568. func (s *Span) Traced() bool {
  569. if s == nil {
  570. return false
  571. }
  572. return s.trace.localOptions&optionTrace != 0
  573. }
  574. // NewChild creates a new span with the given name as a child of s.
  575. // If s is nil, does nothing and returns nil.
  576. func (s *Span) NewChild(name string) *Span {
  577. if s == nil {
  578. return nil
  579. }
  580. if !s.Traced() {
  581. // TODO(jbd): Document this behavior in godoc here and elsewhere.
  582. return s
  583. }
  584. return startNewChild(name, s.trace, s.span.SpanId)
  585. }
  586. // NewRemoteChild creates a new span as a child of s.
  587. //
  588. // Some labels in the span are set from the outgoing *http.Request r.
  589. //
  590. // A header is set in r so that the trace context is propagated to the
  591. // destination. The parent span ID in that header is set as follows:
  592. // - If the request is being traced, then the ID of s is used.
  593. // - If the request is not being traced, but there was a trace context header
  594. // in the incoming request for this trace (the request passed to
  595. // SpanFromRequest), the parent span ID in that header is used.
  596. // - Otherwise, the parent span ID is zero.
  597. // The tracing bit in the options is set if tracing is enabled, or if it was
  598. // set in the incoming request.
  599. //
  600. // If s is nil, does nothing and returns nil.
  601. func (s *Span) NewRemoteChild(r *http.Request) *Span {
  602. if s == nil {
  603. return nil
  604. }
  605. if !s.Traced() {
  606. r.Header[httpHeader] = []string{spanHeader(s.trace.traceID, s.span.ParentSpanId, s.trace.globalOptions)}
  607. return s
  608. }
  609. newSpan := startNewChildWithRequest(r, s.trace, s.span.SpanId)
  610. r.Header[httpHeader] = []string{spanHeader(s.trace.traceID, newSpan.span.SpanId, s.trace.globalOptions)}
  611. return newSpan
  612. }
  613. // Header returns the value of the X-Cloud-Trace-Context header that
  614. // should be used to propagate the span. This is the inverse of
  615. // SpanFromHeader.
  616. //
  617. // Most users should use NewRemoteChild unless they have specific
  618. // propagation needs or want to control the naming of their span.
  619. // Header() does not create a new span.
  620. func (s *Span) Header() string {
  621. if s == nil {
  622. return ""
  623. }
  624. return spanHeader(s.trace.traceID, s.span.SpanId, s.trace.globalOptions)
  625. }
  626. func startNewChildWithRequest(r *http.Request, trace *trace, parentSpanID uint64) *Span {
  627. name := r.URL.Host + r.URL.Path // drop scheme and query params
  628. newSpan := startNewChild(name, trace, parentSpanID)
  629. if r.Host == "" {
  630. newSpan.host = r.URL.Host
  631. } else {
  632. newSpan.host = r.Host
  633. }
  634. newSpan.method = r.Method
  635. newSpan.url = r.URL.String()
  636. return newSpan
  637. }
  638. func startNewChild(name string, trace *trace, parentSpanID uint64) *Span {
  639. spanID := nextSpanID()
  640. for spanID == parentSpanID {
  641. spanID = nextSpanID()
  642. }
  643. newSpan := &Span{
  644. trace: trace,
  645. span: api.TraceSpan{
  646. Kind: spanKindClient,
  647. Name: name,
  648. ParentSpanId: parentSpanID,
  649. SpanId: spanID,
  650. },
  651. start: time.Now(),
  652. }
  653. if trace.localOptions&optionStack != 0 {
  654. _ = runtime.Callers(1, newSpan.stack[:])
  655. }
  656. return newSpan
  657. }
  658. // TraceID returns the ID of the trace to which s belongs.
  659. func (s *Span) TraceID() string {
  660. if s == nil {
  661. return ""
  662. }
  663. return s.trace.traceID
  664. }
  665. // SetLabel sets the label for the given key to the given value.
  666. // If the value is empty, the label for that key is deleted.
  667. // If a label is given a value automatically and by SetLabel, the
  668. // automatically-set value is used.
  669. // If s is nil, does nothing.
  670. //
  671. // SetLabel shouldn't be called after Finish or FinishWait.
  672. func (s *Span) SetLabel(key, value string) {
  673. if s == nil {
  674. return
  675. }
  676. if !s.Traced() {
  677. return
  678. }
  679. s.spanMu.Lock()
  680. defer s.spanMu.Unlock()
  681. if value == "" {
  682. if s.span.Labels != nil {
  683. delete(s.span.Labels, key)
  684. }
  685. return
  686. }
  687. if s.span.Labels == nil {
  688. s.span.Labels = make(map[string]string)
  689. }
  690. s.span.Labels[key] = value
  691. }
  692. type FinishOption interface {
  693. modifySpan(s *Span)
  694. }
  695. type withResponse struct {
  696. *http.Response
  697. }
  698. // WithResponse returns an option that can be passed to Finish that indicates
  699. // that some labels for the span should be set using the given *http.Response.
  700. func WithResponse(resp *http.Response) FinishOption {
  701. return withResponse{resp}
  702. }
  703. func (u withResponse) modifySpan(s *Span) {
  704. if u.Response != nil {
  705. s.statusCode = u.StatusCode
  706. }
  707. }
  708. // Finish declares that the span has finished.
  709. //
  710. // If s is nil, Finish does nothing and returns nil.
  711. //
  712. // If the option trace.WithResponse(resp) is passed, then some labels are set
  713. // for s using information in the given *http.Response. This is useful when the
  714. // span is for an outgoing http request; s will typically have been created by
  715. // NewRemoteChild in this case.
  716. //
  717. // If s is a root span (one created by SpanFromRequest) then s, and all its
  718. // descendant spans that have finished, are uploaded to the Google Stackdriver
  719. // Trace server asynchronously.
  720. func (s *Span) Finish(opts ...FinishOption) {
  721. if s == nil {
  722. return
  723. }
  724. if !s.Traced() {
  725. return
  726. }
  727. s.trace.finish(s, false, opts...)
  728. }
  729. // FinishWait is like Finish, but if s is a root span, it waits until uploading
  730. // is finished, then returns an error if one occurred.
  731. func (s *Span) FinishWait(opts ...FinishOption) error {
  732. if s == nil {
  733. return nil
  734. }
  735. if !s.Traced() {
  736. return nil
  737. }
  738. return s.trace.finish(s, true, opts...)
  739. }
  740. func spanHeader(traceID string, spanID uint64, options optionFlags) string {
  741. // See https://cloud.google.com/trace/docs/faq for the header format.
  742. return fmt.Sprintf("%s/%d;o=%d", traceID, spanID, options)
  743. }
  744. func (s *Span) setStackLabel() {
  745. var stack stackLabelValue
  746. lastSigPanic, inTraceLibrary := false, true
  747. for _, pc := range s.stack {
  748. if pc == 0 {
  749. break
  750. }
  751. if !lastSigPanic {
  752. pc--
  753. }
  754. fn := runtime.FuncForPC(pc)
  755. file, line := fn.FileLine(pc)
  756. // Name has one of the following forms:
  757. // path/to/package.Foo
  758. // path/to/package.(Type).Foo
  759. // For the first form, we store the whole name in the Method field of the
  760. // stack frame. For the second form, we set the Method field to "Foo" and
  761. // the Class field to "path/to/package.(Type)".
  762. name := fn.Name()
  763. if inTraceLibrary && !strings.HasPrefix(name, "cloud.google.com/go/trace.") {
  764. inTraceLibrary = false
  765. }
  766. var class string
  767. if i := strings.Index(name, ")."); i != -1 {
  768. class, name = name[:i+1], name[i+2:]
  769. }
  770. frame := stackFrame{
  771. Class: class,
  772. Method: name,
  773. Filename: file,
  774. Line: int64(line),
  775. }
  776. if inTraceLibrary && len(stack.Frames) == 1 {
  777. stack.Frames[0] = frame
  778. } else {
  779. stack.Frames = append(stack.Frames, frame)
  780. }
  781. lastSigPanic = fn.Name() == "runtime.sigpanic"
  782. }
  783. if label, err := json.Marshal(stack); err == nil {
  784. s.SetLabel(LabelStackTrace, string(label))
  785. }
  786. }