tracer.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. // Unless explicitly stated otherwise all files in this repository are licensed
  2. // under the Apache License Version 2.0.
  3. // This product includes software developed at Datadog (https://www.datadoghq.com/).
  4. // Copyright 2016 Datadog, Inc.
  5. package tracer
  6. import (
  7. gocontext "context"
  8. "os"
  9. "runtime/pprof"
  10. rt "runtime/trace"
  11. "strconv"
  12. "sync"
  13. "sync/atomic"
  14. "time"
  15. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
  16. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
  17. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
  18. "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec"
  19. "gopkg.in/DataDog/dd-trace-go.v1/internal/log"
  20. "gopkg.in/DataDog/dd-trace-go.v1/internal/remoteconfig"
  21. "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof"
  22. "github.com/DataDog/datadog-agent/pkg/obfuscate"
  23. )
  24. var _ ddtrace.Tracer = (*tracer)(nil)
  25. // tracer creates, buffers and submits Spans which are used to time blocks of
  26. // computation. They are accumulated and streamed into an internal payload,
  27. // which is flushed to the agent whenever its size exceeds a specific threshold
  28. // or when a certain interval of time has passed, whichever happens first.
  29. //
  30. // tracer operates based on a worker loop which responds to various request
  31. // channels. It additionally holds two buffers which accumulates error and trace
  32. // queues to be processed by the payload encoder.
  33. type tracer struct {
  34. config *config
  35. // stats specifies the concentrator used to compute statistics, when client-side
  36. // stats are enabled.
  37. stats *concentrator
  38. // traceWriter is responsible for sending finished traces to their
  39. // destination, such as the Trace Agent or Datadog Forwarder.
  40. traceWriter traceWriter
  41. // out receives finishedTrace with spans to be added to the payload.
  42. out chan *finishedTrace
  43. // flush receives a channel onto which it will confirm after a flush has been
  44. // triggered and completed.
  45. flush chan chan<- struct{}
  46. // stop causes the tracer to shut down when closed.
  47. stop chan struct{}
  48. // stopOnce ensures the tracer is stopped exactly once.
  49. stopOnce sync.Once
  50. // wg waits for all goroutines to exit when stopping.
  51. wg sync.WaitGroup
  52. // prioritySampling holds an instance of the priority sampler.
  53. prioritySampling *prioritySampler
  54. // pid of the process
  55. pid int
  56. // These integers track metrics about spans and traces as they are started,
  57. // finished, and dropped
  58. spansStarted, spansFinished, tracesDropped uint32
  59. // Records the number of dropped P0 traces and spans.
  60. droppedP0Traces, droppedP0Spans uint32
  61. // partialTrace the number of partially dropped traces.
  62. partialTraces uint32
  63. // rulesSampling holds an instance of the rules sampler used to apply either trace sampling,
  64. // or single span sampling rules on spans. These are user-defined
  65. // rules for applying a sampling rate to spans that match the designated service
  66. // or operation name.
  67. rulesSampling *rulesSampler
  68. // obfuscator holds the obfuscator used to obfuscate resources in aggregated stats.
  69. // obfuscator may be nil if disabled.
  70. obfuscator *obfuscate.Obfuscator
  71. // statsd is used for tracking metrics associated with the runtime and the tracer.
  72. statsd statsdClient
  73. }
  74. const (
  75. // flushInterval is the interval at which the payload contents will be flushed
  76. // to the transport.
  77. flushInterval = 2 * time.Second
  78. // payloadMaxLimit is the maximum payload size allowed and should indicate the
  79. // maximum size of the package that the agent can receive.
  80. payloadMaxLimit = 9.5 * 1024 * 1024 // 9.5 MB
  81. // payloadSizeLimit specifies the maximum allowed size of the payload before
  82. // it will trigger a flush to the transport.
  83. payloadSizeLimit = payloadMaxLimit / 2
  84. // concurrentConnectionLimit specifies the maximum number of concurrent outgoing
  85. // connections allowed.
  86. concurrentConnectionLimit = 100
  87. )
  88. // statsInterval is the interval at which health metrics will be sent with the
  89. // statsd client; replaced in tests.
  90. var statsInterval = 10 * time.Second
  91. // Start starts the tracer with the given set of options. It will stop and replace
  92. // any running tracer, meaning that calling it several times will result in a restart
  93. // of the tracer by replacing the current instance with a new one.
  94. func Start(opts ...StartOption) {
  95. if internal.Testing {
  96. return // mock tracer active
  97. }
  98. t := newTracer(opts...)
  99. if !t.config.enabled {
  100. return
  101. }
  102. internal.SetGlobalTracer(t)
  103. if t.config.logStartup {
  104. logStartup(t)
  105. }
  106. // Start AppSec with remote configuration
  107. cfg := remoteconfig.DefaultClientConfig()
  108. cfg.AgentURL = t.config.agentURL
  109. cfg.AppVersion = t.config.version
  110. cfg.Env = t.config.env
  111. cfg.HTTP = t.config.httpClient
  112. cfg.ServiceName = t.config.serviceName
  113. appsec.Start(appsec.WithRCConfig(cfg))
  114. }
  115. // Stop stops the started tracer. Subsequent calls are valid but become no-op.
  116. func Stop() {
  117. internal.SetGlobalTracer(&internal.NoopTracer{})
  118. log.Flush()
  119. }
  120. // Span is an alias for ddtrace.Span. It is here to allow godoc to group methods returning
  121. // ddtrace.Span. It is recommended and is considered more correct to refer to this type as
  122. // ddtrace.Span instead.
  123. type Span = ddtrace.Span
  124. // StartSpan starts a new span with the given operation name and set of options.
  125. // If the tracer is not started, calling this function is a no-op.
  126. func StartSpan(operationName string, opts ...StartSpanOption) Span {
  127. return internal.GetGlobalTracer().StartSpan(operationName, opts...)
  128. }
  129. // Extract extracts a SpanContext from the carrier. The carrier is expected
  130. // to implement TextMapReader, otherwise an error is returned.
  131. // If the tracer is not started, calling this function is a no-op.
  132. func Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  133. return internal.GetGlobalTracer().Extract(carrier)
  134. }
  135. // Inject injects the given SpanContext into the carrier. The carrier is
  136. // expected to implement TextMapWriter, otherwise an error is returned.
  137. // If the tracer is not started, calling this function is a no-op.
  138. func Inject(ctx ddtrace.SpanContext, carrier interface{}) error {
  139. return internal.GetGlobalTracer().Inject(ctx, carrier)
  140. }
  141. // SetUser associates user information to the current trace which the
  142. // provided span belongs to. The options can be used to tune which user
  143. // bit of information gets monitored. In case of distributed traces,
  144. // the user id can be propagated across traces using the WithPropagation() option.
  145. // See https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/?tab=set_user#add-user-information-to-traces
  146. func SetUser(s Span, id string, opts ...UserMonitoringOption) {
  147. if s == nil {
  148. return
  149. }
  150. sp, ok := s.(interface {
  151. SetUser(string, ...UserMonitoringOption)
  152. })
  153. if !ok {
  154. return
  155. }
  156. sp.SetUser(id, opts...)
  157. }
  158. // payloadQueueSize is the buffer size of the trace channel.
  159. const payloadQueueSize = 1000
  160. func newUnstartedTracer(opts ...StartOption) *tracer {
  161. c := newConfig(opts...)
  162. sampler := newPrioritySampler()
  163. statsd, err := newStatsdClient(c)
  164. if err != nil {
  165. log.Warn("Runtime and health metrics disabled: %v", err)
  166. }
  167. var writer traceWriter
  168. if c.logToStdout {
  169. writer = newLogTraceWriter(c, statsd)
  170. } else {
  171. writer = newAgentTraceWriter(c, sampler, statsd)
  172. }
  173. traces, spans, err := samplingRulesFromEnv()
  174. if err != nil {
  175. log.Warn("DIAGNOSTICS Error(s) parsing sampling rules: found errors:%s", err)
  176. }
  177. if traces != nil {
  178. c.traceRules = traces
  179. }
  180. if spans != nil {
  181. c.spanRules = spans
  182. }
  183. t := &tracer{
  184. config: c,
  185. traceWriter: writer,
  186. out: make(chan *finishedTrace, payloadQueueSize),
  187. stop: make(chan struct{}),
  188. flush: make(chan chan<- struct{}),
  189. rulesSampling: newRulesSampler(c.traceRules, c.spanRules),
  190. prioritySampling: sampler,
  191. pid: os.Getpid(),
  192. stats: newConcentrator(c, defaultStatsBucketSize),
  193. obfuscator: obfuscate.NewObfuscator(obfuscate.Config{
  194. SQL: obfuscate.SQLConfig{
  195. TableNames: c.agent.HasFlag("table_names"),
  196. ReplaceDigits: c.agent.HasFlag("quantize_sql_tables") || c.agent.HasFlag("replace_sql_digits"),
  197. KeepSQLAlias: c.agent.HasFlag("keep_sql_alias"),
  198. DollarQuotedFunc: c.agent.HasFlag("dollar_quoted_func"),
  199. Cache: c.agent.HasFlag("sql_cache"),
  200. },
  201. }),
  202. statsd: statsd,
  203. }
  204. return t
  205. }
  206. func newTracer(opts ...StartOption) *tracer {
  207. t := newUnstartedTracer(opts...)
  208. c := t.config
  209. t.statsd.Incr("datadog.tracer.started", nil, 1)
  210. if c.runtimeMetrics {
  211. log.Debug("Runtime metrics enabled.")
  212. t.wg.Add(1)
  213. go func() {
  214. defer t.wg.Done()
  215. t.reportRuntimeMetrics(defaultMetricsReportInterval)
  216. }()
  217. }
  218. t.wg.Add(1)
  219. go func() {
  220. defer t.wg.Done()
  221. tick := t.config.tickChan
  222. if tick == nil {
  223. ticker := time.NewTicker(flushInterval)
  224. defer ticker.Stop()
  225. tick = ticker.C
  226. }
  227. t.worker(tick)
  228. }()
  229. t.wg.Add(1)
  230. go func() {
  231. defer t.wg.Done()
  232. t.reportHealthMetrics(statsInterval)
  233. }()
  234. t.stats.Start()
  235. return t
  236. }
  237. // Flush flushes any buffered traces. Flush is in effect only if a tracer
  238. // is started. Users do not have to call Flush in order to ensure that
  239. // traces reach Datadog. It is a convenience method dedicated to a specific
  240. // use case described below.
  241. //
  242. // Flush is of use in Lambda environments, where starting and stopping
  243. // the tracer on each invokation may create too much latency. In this
  244. // scenario, a tracer may be started and stopped by the parent process
  245. // whereas the invokation can make use of Flush to ensure any created spans
  246. // reach the agent.
  247. func Flush() {
  248. if t, ok := internal.GetGlobalTracer().(*tracer); ok {
  249. t.flushSync()
  250. }
  251. }
  252. // flushSync triggers a flush and waits for it to complete.
  253. func (t *tracer) flushSync() {
  254. done := make(chan struct{})
  255. t.flush <- done
  256. <-done
  257. }
  258. // worker receives finished traces to be added into the payload, as well
  259. // as periodically flushes traces to the transport.
  260. func (t *tracer) worker(tick <-chan time.Time) {
  261. for {
  262. select {
  263. case trace := <-t.out:
  264. t.sampleFinishedTrace(trace)
  265. if len(trace.spans) != 0 {
  266. t.traceWriter.add(trace.spans)
  267. }
  268. case <-tick:
  269. t.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:scheduled"}, 1)
  270. t.traceWriter.flush()
  271. case done := <-t.flush:
  272. t.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:invoked"}, 1)
  273. t.traceWriter.flush()
  274. t.statsd.Flush()
  275. t.stats.flushAndSend(time.Now(), withCurrentBucket)
  276. // TODO(x): In reality, the traceWriter.flush() call is not synchronous
  277. // when using the agent traceWriter. However, this functionnality is used
  278. // in Lambda so for that purpose this mechanism should suffice.
  279. done <- struct{}{}
  280. case <-t.stop:
  281. loop:
  282. // the loop ensures that the payload channel is fully drained
  283. // before the final flush to ensure no traces are lost (see #526)
  284. for {
  285. select {
  286. case trace := <-t.out:
  287. t.sampleFinishedTrace(trace)
  288. if len(trace.spans) != 0 {
  289. t.traceWriter.add(trace.spans)
  290. }
  291. default:
  292. break loop
  293. }
  294. }
  295. return
  296. }
  297. }
  298. }
  299. // finishedTrace holds information about a trace that has finished, including its spans.
  300. type finishedTrace struct {
  301. spans []*span
  302. willSend bool // willSend indicates whether the trace will be sent to the agent.
  303. }
  304. // sampleFinishedTrace applies single-span sampling to the provided trace, which is considered to be finished.
  305. func (t *tracer) sampleFinishedTrace(info *finishedTrace) {
  306. if len(info.spans) > 0 {
  307. if p, ok := info.spans[0].context.samplingPriority(); ok && p > 0 {
  308. // The trace is kept, no need to run single span sampling rules.
  309. return
  310. }
  311. }
  312. var kept []*span
  313. if t.rulesSampling.HasSpanRules() {
  314. // Apply sampling rules to individual spans in the trace.
  315. for _, span := range info.spans {
  316. if t.rulesSampling.SampleSpan(span) {
  317. kept = append(kept, span)
  318. }
  319. }
  320. if len(kept) > 0 && len(kept) < len(info.spans) {
  321. // Some spans in the trace were kept, so a partial trace will be sent.
  322. atomic.AddUint32(&t.partialTraces, 1)
  323. }
  324. }
  325. if len(kept) == 0 {
  326. atomic.AddUint32(&t.droppedP0Traces, 1)
  327. }
  328. atomic.AddUint32(&t.droppedP0Spans, uint32(len(info.spans)-len(kept)))
  329. if !info.willSend {
  330. info.spans = kept
  331. }
  332. }
  333. func (t *tracer) pushTrace(trace *finishedTrace) {
  334. select {
  335. case <-t.stop:
  336. return
  337. default:
  338. }
  339. select {
  340. case t.out <- trace:
  341. default:
  342. log.Error("payload queue full, dropping %d traces", len(trace.spans))
  343. }
  344. }
  345. // StartSpan creates, starts, and returns a new Span with the given `operationName`.
  346. func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOption) ddtrace.Span {
  347. var opts ddtrace.StartSpanConfig
  348. for _, fn := range options {
  349. fn(&opts)
  350. }
  351. var startTime int64
  352. if opts.StartTime.IsZero() {
  353. startTime = now()
  354. } else {
  355. startTime = opts.StartTime.UnixNano()
  356. }
  357. var context *spanContext
  358. // The default pprof context is taken from the start options and is
  359. // not nil when using StartSpanFromContext()
  360. pprofContext := opts.Context
  361. if opts.Parent != nil {
  362. if ctx, ok := opts.Parent.(*spanContext); ok {
  363. context = ctx
  364. if pprofContext == nil && ctx.span != nil {
  365. // Inherit the context.Context from parent span if it was propagated
  366. // using ChildOf() rather than StartSpanFromContext(), see
  367. // applyPPROFLabels() below.
  368. pprofContext = ctx.span.pprofCtxActive
  369. }
  370. }
  371. }
  372. if pprofContext == nil {
  373. // For root span's without context, there is no pprofContext, but we need
  374. // one to avoid a panic() in pprof.WithLabels(). Using context.Background()
  375. // is not ideal here, as it will cause us to remove all labels from the
  376. // goroutine when the span finishes. However, the alternatives of not
  377. // applying labels for such spans or to leave the endpoint/hotspot labels
  378. // on the goroutine after it finishes are even less appealing. We'll have
  379. // to properly document this for users.
  380. pprofContext = gocontext.Background()
  381. }
  382. id := opts.SpanID
  383. if id == 0 {
  384. id = generateSpanID(startTime)
  385. }
  386. // span defaults
  387. span := &span{
  388. Name: operationName,
  389. Service: t.config.serviceName,
  390. Resource: operationName,
  391. SpanID: id,
  392. TraceID: id,
  393. Start: startTime,
  394. noDebugStack: t.config.noDebugStack,
  395. }
  396. if t.config.hostname != "" {
  397. span.setMeta(keyHostname, t.config.hostname)
  398. }
  399. if context != nil {
  400. // this is a child span
  401. span.TraceID = context.traceID
  402. span.ParentID = context.spanID
  403. if p, ok := context.samplingPriority(); ok {
  404. span.setMetric(keySamplingPriority, float64(p))
  405. }
  406. if context.span != nil {
  407. // local parent, inherit service
  408. context.span.RLock()
  409. span.Service = context.span.Service
  410. context.span.RUnlock()
  411. } else {
  412. // remote parent
  413. if context.origin != "" {
  414. // mark origin
  415. span.setMeta(keyOrigin, context.origin)
  416. }
  417. }
  418. }
  419. span.context = newSpanContext(span, context)
  420. span.setMetric(ext.Pid, float64(t.pid))
  421. span.setMeta("language", "go")
  422. // add tags from options
  423. for k, v := range opts.Tags {
  424. span.SetTag(k, v)
  425. }
  426. // add global tags
  427. for k, v := range t.config.globalTags {
  428. span.SetTag(k, v)
  429. }
  430. if t.config.serviceMappings != nil {
  431. if newSvc, ok := t.config.serviceMappings[span.Service]; ok {
  432. span.Service = newSvc
  433. }
  434. }
  435. if context == nil || context.span == nil || context.span.Service != span.Service {
  436. span.setMetric(keyTopLevel, 1)
  437. // all top level spans are measured. So the measured tag is redundant.
  438. delete(span.Metrics, keyMeasured)
  439. }
  440. if t.config.version != "" {
  441. if t.config.universalVersion || (!t.config.universalVersion && span.Service == t.config.serviceName) {
  442. span.setMeta(ext.Version, t.config.version)
  443. }
  444. }
  445. if t.config.env != "" {
  446. span.setMeta(ext.Environment, t.config.env)
  447. }
  448. if _, ok := span.context.samplingPriority(); !ok {
  449. // if not already sampled or a brand new trace, sample it
  450. t.sample(span)
  451. }
  452. pprofContext, span.taskEnd = startExecutionTracerTask(pprofContext, span)
  453. if t.config.profilerHotspots || t.config.profilerEndpoints {
  454. t.applyPPROFLabels(pprofContext, span)
  455. }
  456. if t.config.serviceMappings != nil {
  457. if newSvc, ok := t.config.serviceMappings[span.Service]; ok {
  458. span.Service = newSvc
  459. }
  460. }
  461. if log.DebugEnabled() {
  462. // avoid allocating the ...interface{} argument if debug logging is disabled
  463. log.Debug("Started Span: %v, Operation: %s, Resource: %s, Tags: %v, %v",
  464. span, span.Name, span.Resource, span.Meta, span.Metrics)
  465. }
  466. return span
  467. }
  468. // generateSpanID returns a random uint64 that has been XORd with the startTime.
  469. // This is done to get around the 32-bit random seed limitation that may create collisions if there is a large number
  470. // of go services all generating spans.
  471. func generateSpanID(startTime int64) uint64 {
  472. return random.Uint64() ^ uint64(startTime)
  473. }
  474. // applyPPROFLabels applies pprof labels for the profiler's code hotspots and
  475. // endpoint filtering feature to span. When span finishes, any pprof labels
  476. // found in ctx are restored. Additionally this func informs the profiler how
  477. // many times each endpoint is called.
  478. func (t *tracer) applyPPROFLabels(ctx gocontext.Context, span *span) {
  479. var labels []string
  480. if t.config.profilerHotspots {
  481. // allocate the max-length slice to avoid growing it later
  482. labels = make([]string, 0, 6)
  483. labels = append(labels, traceprof.SpanID, strconv.FormatUint(span.SpanID, 10))
  484. }
  485. // nil checks might not be needed, but better be safe than sorry
  486. if localRootSpan := span.root(); localRootSpan != nil {
  487. if t.config.profilerHotspots {
  488. labels = append(labels, traceprof.LocalRootSpanID, strconv.FormatUint(localRootSpan.SpanID, 10))
  489. }
  490. if t.config.profilerEndpoints && spanResourcePIISafe(localRootSpan) {
  491. labels = append(labels, traceprof.TraceEndpoint, localRootSpan.Resource)
  492. if span == localRootSpan {
  493. // Inform the profiler of endpoint hits. This is used for the unit of
  494. // work feature. We can't use APM stats for this since the stats don't
  495. // have enough cardinality (e.g. runtime-id tags are missing).
  496. traceprof.GlobalEndpointCounter().Inc(localRootSpan.Resource)
  497. }
  498. }
  499. }
  500. if len(labels) > 0 {
  501. span.pprofCtxRestore = ctx
  502. span.pprofCtxActive = pprof.WithLabels(ctx, pprof.Labels(labels...))
  503. pprof.SetGoroutineLabels(span.pprofCtxActive)
  504. }
  505. }
  506. // spanResourcePIISafe returns true if s.Resource can be considered to not
  507. // include PII with reasonable confidence. E.g. SQL queries may contain PII,
  508. // but http, rpc or custom (s.Type == "") span resource names generally do not.
  509. func spanResourcePIISafe(s *span) bool {
  510. return s.Type == ext.SpanTypeWeb || s.Type == ext.AppTypeRPC || s.Type == ""
  511. }
  512. // Stop stops the tracer.
  513. func (t *tracer) Stop() {
  514. t.stopOnce.Do(func() {
  515. close(t.stop)
  516. t.statsd.Incr("datadog.tracer.stopped", nil, 1)
  517. })
  518. t.stats.Stop()
  519. t.wg.Wait()
  520. t.traceWriter.stop()
  521. t.statsd.Close()
  522. appsec.Stop()
  523. }
  524. // Inject uses the configured or default TextMap Propagator.
  525. func (t *tracer) Inject(ctx ddtrace.SpanContext, carrier interface{}) error {
  526. return t.config.propagator.Inject(ctx, carrier)
  527. }
  528. // Extract uses the configured or default TextMap Propagator.
  529. func (t *tracer) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  530. return t.config.propagator.Extract(carrier)
  531. }
  532. // sampleRateMetricKey is the metric key holding the applied sample rate. Has to be the same as the Agent.
  533. const sampleRateMetricKey = "_sample_rate"
  534. // Sample samples a span with the internal sampler.
  535. func (t *tracer) sample(span *span) {
  536. if _, ok := span.context.samplingPriority(); ok {
  537. // sampling decision was already made
  538. return
  539. }
  540. sampler := t.config.sampler
  541. if !sampler.Sample(span) {
  542. span.context.trace.drop()
  543. return
  544. }
  545. if rs, ok := sampler.(RateSampler); ok && rs.Rate() < 1 {
  546. span.setMetric(sampleRateMetricKey, rs.Rate())
  547. }
  548. if t.rulesSampling.SampleTrace(span) {
  549. return
  550. }
  551. t.prioritySampling.apply(span)
  552. }
  553. func startExecutionTracerTask(ctx gocontext.Context, span *span) (gocontext.Context, func()) {
  554. if !rt.IsEnabled() {
  555. return ctx, func() {}
  556. }
  557. // Task name is the resource (operationName) of the span, e.g.
  558. // "POST /foo/bar" (http) or "/foo/pkg.Method" (grpc).
  559. taskName := span.Resource
  560. // If the resource could contain PII (e.g. SQL query that's not using bind
  561. // arguments), play it safe and just use the span type as the taskName,
  562. // e.g. "sql".
  563. if !spanResourcePIISafe(span) {
  564. taskName = span.Type
  565. }
  566. ctx, task := rt.NewTask(ctx, taskName)
  567. rt.Log(ctx, "span id", strconv.FormatUint(span.SpanID, 10))
  568. return ctx, task.End
  569. }