option.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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. "context"
  8. "encoding/json"
  9. "fmt"
  10. "math"
  11. "net"
  12. "net/http"
  13. "os"
  14. "path/filepath"
  15. "runtime"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
  20. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
  21. "gopkg.in/DataDog/dd-trace-go.v1/internal"
  22. "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
  23. "gopkg.in/DataDog/dd-trace-go.v1/internal/log"
  24. "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof"
  25. "gopkg.in/DataDog/dd-trace-go.v1/internal/version"
  26. "github.com/DataDog/datadog-go/v5/statsd"
  27. )
  28. var (
  29. // defaultSocketAPM specifies the socket path to use for connecting to the trace-agent.
  30. // Replaced in tests
  31. defaultSocketAPM = "/var/run/datadog/apm.socket"
  32. // defaultSocketDSD specifies the socket path to use for connecting to the statsd server.
  33. // Replaced in tests
  34. defaultSocketDSD = "/var/run/datadog/dsd.socket"
  35. // defaultMaxTagsHeaderLen specifies the default maximum length of the X-Datadog-Tags header value.
  36. defaultMaxTagsHeaderLen = 128
  37. )
  38. // config holds the tracer configuration.
  39. type config struct {
  40. // debug, when true, writes details to logs.
  41. debug bool
  42. // agent holds the capabilities of the agent and determines some
  43. // of the behaviour of the tracer.
  44. agent agentFeatures
  45. // featureFlags specifies any enabled feature flags.
  46. featureFlags map[string]struct{}
  47. // logToStdout reports whether we should log all traces to the standard
  48. // output instead of using the agent. This is used in Lambda environments.
  49. logToStdout bool
  50. // logStartup, when true, causes various startup info to be written
  51. // when the tracer starts.
  52. logStartup bool
  53. // serviceName specifies the name of this application.
  54. serviceName string
  55. // universalVersion, reports whether span service name and config service name
  56. // should match to set application version tag. False by default
  57. universalVersion bool
  58. // version specifies the version of this application
  59. version string
  60. // env contains the environment that this application will run under.
  61. env string
  62. // sampler specifies the sampler that will be used for sampling traces.
  63. sampler Sampler
  64. // agentURL is the agent URL that receives traces from the tracer.
  65. agentURL string
  66. // serviceMappings holds a set of service mappings to dynamically rename services
  67. serviceMappings map[string]string
  68. // globalTags holds a set of tags that will be automatically applied to
  69. // all spans.
  70. globalTags map[string]interface{}
  71. // transport specifies the Transport interface which will be used to send data to the agent.
  72. transport transport
  73. // propagator propagates span context cross-process
  74. propagator Propagator
  75. // httpClient specifies the HTTP client to be used by the agent's transport.
  76. httpClient *http.Client
  77. // hostname is automatically assigned when the DD_TRACE_REPORT_HOSTNAME is set to true,
  78. // and is added as a special tag to the root span of traces.
  79. hostname string
  80. // logger specifies the logger to use when printing errors. If not specified, the "log" package
  81. // will be used.
  82. logger ddtrace.Logger
  83. // runtimeMetrics specifies whether collection of runtime metrics is enabled.
  84. runtimeMetrics bool
  85. // dogstatsdAddr specifies the address to connect for sending metrics to the
  86. // Datadog Agent. If not set, it defaults to "localhost:8125" or to the
  87. // combination of the environment variables DD_AGENT_HOST and DD_DOGSTATSD_PORT.
  88. dogstatsdAddr string
  89. // statsdClient is set when a user provides a custom statsd client for tracking metrics
  90. // associated with the runtime and the tracer.
  91. statsdClient statsdClient
  92. // spanRules contains user-defined rules to determine the sampling rate to apply
  93. // to trace spans.
  94. spanRules []SamplingRule
  95. // traceRules contains user-defined rules to determine the sampling rate to apply
  96. // to individual spans.
  97. traceRules []SamplingRule
  98. // tickChan specifies a channel which will receive the time every time the tracer must flush.
  99. // It defaults to time.Ticker; replaced in tests.
  100. tickChan <-chan time.Time
  101. // noDebugStack disables the collection of debug stack traces globally. No traces reporting
  102. // errors will record a stack trace when this option is set.
  103. noDebugStack bool
  104. // profilerHotspots specifies whether profiler Code Hotspots is enabled.
  105. profilerHotspots bool
  106. // profilerEndpoints specifies whether profiler endpoint filtering is enabled.
  107. profilerEndpoints bool
  108. // enabled reports whether tracing is enabled.
  109. enabled bool
  110. }
  111. // HasFeature reports whether feature f is enabled.
  112. func (c *config) HasFeature(f string) bool {
  113. _, ok := c.featureFlags[strings.TrimSpace(f)]
  114. return ok
  115. }
  116. // StartOption represents a function that can be provided as a parameter to Start.
  117. type StartOption func(*config)
  118. // forEachStringTag runs fn on every key:val pair encountered in str.
  119. // str may contain multiple key:val pairs separated by either space
  120. // or comma (but not a mixture of both).
  121. func forEachStringTag(str string, fn func(key string, val string)) {
  122. sep := " "
  123. if strings.Index(str, ",") > -1 {
  124. // falling back to comma as separator
  125. sep = ","
  126. }
  127. for _, tag := range strings.Split(str, sep) {
  128. tag = strings.TrimSpace(tag)
  129. if tag == "" {
  130. continue
  131. }
  132. kv := strings.SplitN(tag, ":", 2)
  133. key := strings.TrimSpace(kv[0])
  134. if key == "" {
  135. continue
  136. }
  137. var val string
  138. if len(kv) == 2 {
  139. val = strings.TrimSpace(kv[1])
  140. }
  141. fn(key, val)
  142. }
  143. }
  144. // maxPropagatedTagsLength limits the size of DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH to prevent HTTP 413 responses.
  145. const maxPropagatedTagsLength = 512
  146. // newConfig renders the tracer configuration based on defaults, environment variables
  147. // and passed user opts.
  148. func newConfig(opts ...StartOption) *config {
  149. c := new(config)
  150. c.sampler = NewAllSampler()
  151. c.agentURL = "http://" + resolveAgentAddr()
  152. c.httpClient = defaultHTTPClient()
  153. if url := internal.AgentURLFromEnv(); url != nil {
  154. if url.Scheme == "unix" {
  155. c.httpClient = udsClient(url.Path)
  156. } else {
  157. c.agentURL = url.String()
  158. }
  159. }
  160. if internal.BoolEnv("DD_TRACE_ANALYTICS_ENABLED", false) {
  161. globalconfig.SetAnalyticsRate(1.0)
  162. }
  163. if os.Getenv("DD_TRACE_REPORT_HOSTNAME") == "true" {
  164. var err error
  165. c.hostname, err = os.Hostname()
  166. if err != nil {
  167. log.Warn("unable to look up hostname: %v", err)
  168. }
  169. }
  170. if v := os.Getenv("DD_TRACE_SOURCE_HOSTNAME"); v != "" {
  171. c.hostname = v
  172. }
  173. if v := os.Getenv("DD_ENV"); v != "" {
  174. c.env = v
  175. }
  176. if v := os.Getenv("DD_TRACE_FEATURES"); v != "" {
  177. WithFeatureFlags(strings.FieldsFunc(v, func(r rune) bool {
  178. return r == ',' || r == ' '
  179. })...)(c)
  180. }
  181. if v := os.Getenv("DD_SERVICE"); v != "" {
  182. c.serviceName = v
  183. globalconfig.SetServiceName(v)
  184. }
  185. if ver := os.Getenv("DD_VERSION"); ver != "" {
  186. c.version = ver
  187. }
  188. if v := os.Getenv("DD_SERVICE_MAPPING"); v != "" {
  189. forEachStringTag(v, func(key, val string) { WithServiceMapping(key, val)(c) })
  190. }
  191. if v := os.Getenv("DD_TAGS"); v != "" {
  192. forEachStringTag(v, func(key, val string) { WithGlobalTag(key, val)(c) })
  193. }
  194. if _, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME"); ok {
  195. // AWS_LAMBDA_FUNCTION_NAME being set indicates that we're running in an AWS Lambda environment.
  196. // See: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
  197. c.logToStdout = true
  198. }
  199. c.logStartup = internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true)
  200. c.runtimeMetrics = internal.BoolEnv("DD_RUNTIME_METRICS_ENABLED", false)
  201. c.debug = internal.BoolEnv("DD_TRACE_DEBUG", false)
  202. c.enabled = internal.BoolEnv("DD_TRACE_ENABLED", true)
  203. c.profilerEndpoints = internal.BoolEnv(traceprof.EndpointEnvVar, true)
  204. c.profilerHotspots = internal.BoolEnv(traceprof.CodeHotspotsEnvVar, true)
  205. for _, fn := range opts {
  206. fn(c)
  207. }
  208. WithGlobalTag(ext.RuntimeID, globalconfig.RuntimeID())(c)
  209. if c.env == "" {
  210. if v, ok := c.globalTags["env"]; ok {
  211. if e, ok := v.(string); ok {
  212. c.env = e
  213. }
  214. }
  215. }
  216. if c.version == "" {
  217. if v, ok := c.globalTags["version"]; ok {
  218. if ver, ok := v.(string); ok {
  219. c.version = ver
  220. }
  221. }
  222. }
  223. if c.serviceName == "" {
  224. if v, ok := c.globalTags["service"]; ok {
  225. if s, ok := v.(string); ok {
  226. c.serviceName = s
  227. globalconfig.SetServiceName(s)
  228. }
  229. } else {
  230. c.serviceName = filepath.Base(os.Args[0])
  231. }
  232. }
  233. if c.transport == nil {
  234. c.transport = newHTTPTransport(c.agentURL, c.httpClient)
  235. }
  236. if c.propagator == nil {
  237. envKey := "DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH"
  238. max := internal.IntEnv(envKey, defaultMaxTagsHeaderLen)
  239. if max < 0 {
  240. log.Warn("Invalid value %d for %s. Setting to 0.", max, envKey)
  241. max = 0
  242. }
  243. if max > maxPropagatedTagsLength {
  244. log.Warn("Invalid value %d for %s. Maximum allowed is %d. Setting to %d.", max, envKey, maxPropagatedTagsLength, maxPropagatedTagsLength)
  245. max = maxPropagatedTagsLength
  246. }
  247. c.propagator = NewPropagator(&PropagatorConfig{
  248. MaxTagsHeaderLen: max,
  249. })
  250. }
  251. if c.logger != nil {
  252. log.UseLogger(c.logger)
  253. }
  254. if c.debug {
  255. log.SetLevel(log.LevelDebug)
  256. }
  257. c.loadAgentFeatures()
  258. if c.statsdClient == nil {
  259. // configure statsd client
  260. addr := c.dogstatsdAddr
  261. if addr == "" {
  262. // no config defined address; use defaults
  263. addr = defaultDogstatsdAddr()
  264. }
  265. if agentport := c.agent.StatsdPort; agentport > 0 {
  266. // the agent reported a non-standard port
  267. host, _, err := net.SplitHostPort(addr)
  268. if err == nil {
  269. // we have a valid host:port address; replace the port because
  270. // the agent knows better
  271. if host == "" {
  272. host = defaultHostname
  273. }
  274. addr = net.JoinHostPort(host, strconv.Itoa(agentport))
  275. }
  276. // not a valid TCP address, leave it as it is (could be a socket connection)
  277. }
  278. c.dogstatsdAddr = addr
  279. }
  280. return c
  281. }
  282. func newStatsdClient(c *config) (statsdClient, error) {
  283. if c.statsdClient != nil {
  284. return c.statsdClient, nil
  285. }
  286. client, err := statsd.New(c.dogstatsdAddr, statsd.WithMaxMessagesPerPayload(40), statsd.WithTags(statsTags(c)))
  287. if err != nil {
  288. return &statsd.NoOpClient{}, err
  289. }
  290. return client, nil
  291. }
  292. // defaultHTTPClient returns the default http.Client to start the tracer with.
  293. func defaultHTTPClient() *http.Client {
  294. if _, err := os.Stat(defaultSocketAPM); err == nil {
  295. // we have the UDS socket file, use it
  296. return udsClient(defaultSocketAPM)
  297. }
  298. return defaultClient
  299. }
  300. // udsClient returns a new http.Client which connects using the given UDS socket path.
  301. func udsClient(socketPath string) *http.Client {
  302. return &http.Client{
  303. Transport: &http.Transport{
  304. Proxy: http.ProxyFromEnvironment,
  305. DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
  306. return defaultDialer.DialContext(ctx, "unix", (&net.UnixAddr{
  307. Name: socketPath,
  308. Net: "unix",
  309. }).String())
  310. },
  311. MaxIdleConns: 100,
  312. IdleConnTimeout: 90 * time.Second,
  313. TLSHandshakeTimeout: 10 * time.Second,
  314. ExpectContinueTimeout: 1 * time.Second,
  315. },
  316. Timeout: defaultHTTPTimeout,
  317. }
  318. }
  319. // defaultDogstatsdAddr returns the default connection address for Dogstatsd.
  320. func defaultDogstatsdAddr() string {
  321. envHost, envPort := os.Getenv("DD_AGENT_HOST"), os.Getenv("DD_DOGSTATSD_PORT")
  322. if _, err := os.Stat(defaultSocketDSD); err == nil && envHost == "" && envPort == "" {
  323. // socket exists and user didn't specify otherwise via env vars
  324. return "unix://" + defaultSocketDSD
  325. }
  326. host, port := defaultHostname, "8125"
  327. if envHost != "" {
  328. host = envHost
  329. }
  330. if envPort != "" {
  331. port = envPort
  332. }
  333. return net.JoinHostPort(host, port)
  334. }
  335. // agentFeatures holds information about the trace-agent's capabilities.
  336. // When running WithLambdaMode, a zero-value of this struct will be used
  337. // as features.
  338. type agentFeatures struct {
  339. // DropP0s reports whether it's ok for the tracer to not send any
  340. // P0 traces to the agent.
  341. DropP0s bool
  342. // Stats reports whether the agent can receive client-computed stats on
  343. // the /v0.6/stats endpoint.
  344. Stats bool
  345. // StatsdPort specifies the Dogstatsd port as provided by the agent.
  346. // If it's the default, it will be 0, which means 8125.
  347. StatsdPort int
  348. // featureFlags specifies all the feature flags reported by the trace-agent.
  349. featureFlags map[string]struct{}
  350. }
  351. // HasFlag reports whether the agent has set the feat feature flag.
  352. func (a *agentFeatures) HasFlag(feat string) bool {
  353. _, ok := a.featureFlags[feat]
  354. return ok
  355. }
  356. // loadAgentFeatures queries the trace-agent for its capabilities and updates
  357. // the tracer's behaviour.
  358. func (c *config) loadAgentFeatures() {
  359. c.agent = agentFeatures{}
  360. if c.logToStdout {
  361. // there is no agent; all features off
  362. return
  363. }
  364. resp, err := c.httpClient.Get(fmt.Sprintf("%s/info", c.agentURL))
  365. if err != nil {
  366. log.Error("Loading features: %v", err)
  367. return
  368. }
  369. if resp.StatusCode == http.StatusNotFound {
  370. // agent is older than 7.28.0, features not discoverable
  371. return
  372. }
  373. defer resp.Body.Close()
  374. type infoResponse struct {
  375. Endpoints []string `json:"endpoints"`
  376. ClientDropP0s bool `json:"client_drop_p0s"`
  377. StatsdPort int `json:"statsd_port"`
  378. FeatureFlags []string `json:"feature_flags"`
  379. }
  380. var info infoResponse
  381. if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
  382. log.Error("Decoding features: %v", err)
  383. return
  384. }
  385. c.agent.DropP0s = info.ClientDropP0s
  386. c.agent.StatsdPort = info.StatsdPort
  387. for _, endpoint := range info.Endpoints {
  388. switch endpoint {
  389. case "/v0.6/stats":
  390. c.agent.Stats = true
  391. }
  392. }
  393. c.agent.featureFlags = make(map[string]struct{}, len(info.FeatureFlags))
  394. for _, flag := range info.FeatureFlags {
  395. c.agent.featureFlags[flag] = struct{}{}
  396. }
  397. }
  398. func (c *config) canComputeStats() bool {
  399. return c.agent.Stats && c.HasFeature("discovery")
  400. }
  401. func (c *config) canDropP0s() bool {
  402. return c.canComputeStats() && c.agent.DropP0s
  403. }
  404. func statsTags(c *config) []string {
  405. tags := []string{
  406. "lang:go",
  407. "version:" + version.Tag,
  408. "lang_version:" + runtime.Version(),
  409. }
  410. if c.serviceName != "" {
  411. tags = append(tags, "service:"+c.serviceName)
  412. }
  413. if c.env != "" {
  414. tags = append(tags, "env:"+c.env)
  415. }
  416. if c.hostname != "" {
  417. tags = append(tags, "host:"+c.hostname)
  418. }
  419. for k, v := range c.globalTags {
  420. if vstr, ok := v.(string); ok {
  421. tags = append(tags, k+":"+vstr)
  422. }
  423. }
  424. return tags
  425. }
  426. // withNoopStats is used for testing to disable statsd client
  427. func withNoopStats() StartOption {
  428. return func(c *config) {
  429. c.statsdClient = &statsd.NoOpClient{}
  430. }
  431. }
  432. // WithFeatureFlags specifies a set of feature flags to enable. Please take into account
  433. // that most, if not all features flags are considered to be experimental and result in
  434. // unexpected bugs.
  435. func WithFeatureFlags(feats ...string) StartOption {
  436. return func(c *config) {
  437. if c.featureFlags == nil {
  438. c.featureFlags = make(map[string]struct{}, len(feats))
  439. }
  440. for _, f := range feats {
  441. c.featureFlags[strings.TrimSpace(f)] = struct{}{}
  442. }
  443. log.Info("FEATURES enabled: %v", feats)
  444. }
  445. }
  446. // WithLogger sets logger as the tracer's error printer.
  447. func WithLogger(logger ddtrace.Logger) StartOption {
  448. return func(c *config) {
  449. c.logger = logger
  450. }
  451. }
  452. // WithPrioritySampling is deprecated, and priority sampling is enabled by default.
  453. // When using distributed tracing, the priority sampling value is propagated in order to
  454. // get all the parts of a distributed trace sampled.
  455. // To learn more about priority sampling, please visit:
  456. // https://docs.datadoghq.com/tracing/getting_further/trace_sampling_and_storage/#priority-sampling-for-distributed-tracing
  457. func WithPrioritySampling() StartOption {
  458. return func(c *config) {
  459. // This is now enabled by default.
  460. }
  461. }
  462. // WithDebugStack can be used to globally enable or disable the collection of stack traces when
  463. // spans finish with errors. It is enabled by default. This is a global version of the NoDebugStack
  464. // FinishOption.
  465. func WithDebugStack(enabled bool) StartOption {
  466. return func(c *config) {
  467. c.noDebugStack = !enabled
  468. }
  469. }
  470. // WithDebugMode enables debug mode on the tracer, resulting in more verbose logging.
  471. func WithDebugMode(enabled bool) StartOption {
  472. return func(c *config) {
  473. c.debug = enabled
  474. }
  475. }
  476. // WithLambdaMode enables lambda mode on the tracer, for use with AWS Lambda.
  477. func WithLambdaMode(enabled bool) StartOption {
  478. return func(c *config) {
  479. c.logToStdout = enabled
  480. }
  481. }
  482. // WithPropagator sets an alternative propagator to be used by the tracer.
  483. func WithPropagator(p Propagator) StartOption {
  484. return func(c *config) {
  485. c.propagator = p
  486. }
  487. }
  488. // WithServiceName is deprecated. Please use WithService.
  489. // If you are using an older version and you are upgrading from WithServiceName
  490. // to WithService, please note that WithService will determine the service name of
  491. // server and framework integrations.
  492. func WithServiceName(name string) StartOption {
  493. return func(c *config) {
  494. c.serviceName = name
  495. if globalconfig.ServiceName() != "" {
  496. log.Warn("ddtrace/tracer: deprecated config WithServiceName should not be used " +
  497. "with `WithService` or `DD_SERVICE`; integration service name will not be set.")
  498. }
  499. globalconfig.SetServiceName("")
  500. }
  501. }
  502. // WithService sets the default service name for the program.
  503. func WithService(name string) StartOption {
  504. return func(c *config) {
  505. c.serviceName = name
  506. globalconfig.SetServiceName(c.serviceName)
  507. }
  508. }
  509. // WithAgentAddr sets the address where the agent is located. The default is
  510. // localhost:8126. It should contain both host and port.
  511. func WithAgentAddr(addr string) StartOption {
  512. return func(c *config) {
  513. c.agentURL = "http://" + addr
  514. }
  515. }
  516. // WithEnv sets the environment to which all traces started by the tracer will be submitted.
  517. // The default value is the environment variable DD_ENV, if it is set.
  518. func WithEnv(env string) StartOption {
  519. return func(c *config) {
  520. c.env = env
  521. }
  522. }
  523. // WithServiceMapping determines service "from" to be renamed to service "to".
  524. // This option is is case sensitive and can be used multiple times.
  525. func WithServiceMapping(from, to string) StartOption {
  526. return func(c *config) {
  527. if c.serviceMappings == nil {
  528. c.serviceMappings = make(map[string]string)
  529. }
  530. c.serviceMappings[from] = to
  531. }
  532. }
  533. // WithGlobalTag sets a key/value pair which will be set as a tag on all spans
  534. // created by tracer. This option may be used multiple times.
  535. func WithGlobalTag(k string, v interface{}) StartOption {
  536. return func(c *config) {
  537. if c.globalTags == nil {
  538. c.globalTags = make(map[string]interface{})
  539. }
  540. c.globalTags[k] = v
  541. }
  542. }
  543. // WithSampler sets the given sampler to be used with the tracer. By default
  544. // an all-permissive sampler is used.
  545. func WithSampler(s Sampler) StartOption {
  546. return func(c *config) {
  547. c.sampler = s
  548. }
  549. }
  550. // WithHTTPRoundTripper is deprecated. Please consider using WithHTTPClient instead.
  551. // The function allows customizing the underlying HTTP transport for emitting spans.
  552. func WithHTTPRoundTripper(r http.RoundTripper) StartOption {
  553. return WithHTTPClient(&http.Client{
  554. Transport: r,
  555. Timeout: defaultHTTPTimeout,
  556. })
  557. }
  558. // WithHTTPClient specifies the HTTP client to use when emitting spans to the agent.
  559. func WithHTTPClient(client *http.Client) StartOption {
  560. return func(c *config) {
  561. c.httpClient = client
  562. }
  563. }
  564. // WithUDS configures the HTTP client to dial the Datadog Agent via the specified Unix Domain Socket path.
  565. func WithUDS(socketPath string) StartOption {
  566. return WithHTTPClient(udsClient(socketPath))
  567. }
  568. // WithAnalytics allows specifying whether Trace Search & Analytics should be enabled
  569. // for integrations.
  570. func WithAnalytics(on bool) StartOption {
  571. return func(cfg *config) {
  572. if on {
  573. globalconfig.SetAnalyticsRate(1.0)
  574. } else {
  575. globalconfig.SetAnalyticsRate(math.NaN())
  576. }
  577. }
  578. }
  579. // WithAnalyticsRate sets the global sampling rate for sampling APM events.
  580. func WithAnalyticsRate(rate float64) StartOption {
  581. return func(_ *config) {
  582. if rate >= 0.0 && rate <= 1.0 {
  583. globalconfig.SetAnalyticsRate(rate)
  584. } else {
  585. globalconfig.SetAnalyticsRate(math.NaN())
  586. }
  587. }
  588. }
  589. // WithRuntimeMetrics enables automatic collection of runtime metrics every 10 seconds.
  590. func WithRuntimeMetrics() StartOption {
  591. return func(cfg *config) {
  592. cfg.runtimeMetrics = true
  593. }
  594. }
  595. // WithDogstatsdAddress specifies the address to connect to for sending metrics to the Datadog
  596. // Agent. It should be a "host:port" string, or the path to a unix domain socket.If not set, it
  597. // attempts to determine the address of the statsd service according to the following rules:
  598. // 1. Look for /var/run/datadog/dsd.socket and use it if present. IF NOT, continue to #2.
  599. // 2. The host is determined by DD_AGENT_HOST, and defaults to "localhost"
  600. // 3. The port is retrieved from the agent. If not present, it is determined by DD_DOGSTATSD_PORT, and defaults to 8125
  601. //
  602. // This option is in effect when WithRuntimeMetrics is enabled.
  603. func WithDogstatsdAddress(addr string) StartOption {
  604. return func(cfg *config) {
  605. cfg.dogstatsdAddr = addr
  606. }
  607. }
  608. // WithSamplingRules specifies the sampling rates to apply to spans based on the
  609. // provided rules.
  610. func WithSamplingRules(rules []SamplingRule) StartOption {
  611. return func(cfg *config) {
  612. for _, rule := range rules {
  613. if rule.ruleType == SamplingRuleSpan {
  614. cfg.spanRules = append(cfg.spanRules, rule)
  615. } else {
  616. cfg.traceRules = append(cfg.traceRules, rule)
  617. }
  618. }
  619. }
  620. }
  621. // WithServiceVersion specifies the version of the service that is running. This will
  622. // be included in spans from this service in the "version" tag, provided that
  623. // span service name and config service name match. Do NOT use with WithUniversalVersion.
  624. func WithServiceVersion(version string) StartOption {
  625. return func(cfg *config) {
  626. cfg.version = version
  627. cfg.universalVersion = false
  628. }
  629. }
  630. // WithUniversalVersion specifies the version of the service that is running, and will be applied to all spans,
  631. // regardless of whether span service name and config service name match.
  632. // See: WithService, WithServiceVersion. Do NOT use with WithServiceVersion.
  633. func WithUniversalVersion(version string) StartOption {
  634. return func(c *config) {
  635. c.version = version
  636. c.universalVersion = true
  637. }
  638. }
  639. // WithHostname allows specifying the hostname with which to mark outgoing traces.
  640. func WithHostname(name string) StartOption {
  641. return func(c *config) {
  642. c.hostname = name
  643. }
  644. }
  645. // WithTraceEnabled allows specifying whether tracing will be enabled
  646. func WithTraceEnabled(enabled bool) StartOption {
  647. return func(c *config) {
  648. c.enabled = enabled
  649. }
  650. }
  651. // WithLogStartup allows enabling or disabling the startup log.
  652. func WithLogStartup(enabled bool) StartOption {
  653. return func(c *config) {
  654. c.logStartup = enabled
  655. }
  656. }
  657. // WithProfilerCodeHotspots enables the code hotspots integration between the
  658. // tracer and profiler. This is done by automatically attaching pprof labels
  659. // called "span id" and "local root span id" when new spans are created. You
  660. // should not use these label names in your own code when this is enabled. The
  661. // enabled value defaults to the value of the
  662. // DD_PROFILING_CODE_HOTSPOTS_COLLECTION_ENABLED env variable or true.
  663. func WithProfilerCodeHotspots(enabled bool) StartOption {
  664. return func(c *config) {
  665. c.profilerHotspots = enabled
  666. }
  667. }
  668. // WithProfilerEndpoints enables the endpoints integration between the tracer
  669. // and profiler. This is done by automatically attaching a pprof label called
  670. // "trace endpoint" holding the resource name of the top-level service span if
  671. // its type is "http", "rpc" or "" (default). You should not use this label
  672. // name in your own code when this is enabled. The enabled value defaults to
  673. // the value of the DD_PROFILING_ENDPOINT_COLLECTION_ENABLED env variable or
  674. // true.
  675. func WithProfilerEndpoints(enabled bool) StartOption {
  676. return func(c *config) {
  677. c.profilerEndpoints = enabled
  678. }
  679. }
  680. // StartSpanOption is a configuration option for StartSpan. It is aliased in order
  681. // to help godoc group all the functions returning it together. It is considered
  682. // more correct to refer to it as the type as the origin, ddtrace.StartSpanOption.
  683. type StartSpanOption = ddtrace.StartSpanOption
  684. // Tag sets the given key/value pair as a tag on the started Span.
  685. func Tag(k string, v interface{}) StartSpanOption {
  686. return func(cfg *ddtrace.StartSpanConfig) {
  687. if cfg.Tags == nil {
  688. cfg.Tags = map[string]interface{}{}
  689. }
  690. cfg.Tags[k] = v
  691. }
  692. }
  693. // ServiceName sets the given service name on the started span. For example "http.server".
  694. func ServiceName(name string) StartSpanOption {
  695. return Tag(ext.ServiceName, name)
  696. }
  697. // ResourceName sets the given resource name on the started span. A resource could
  698. // be an SQL query, a URL, an RPC method or something else.
  699. func ResourceName(name string) StartSpanOption {
  700. return Tag(ext.ResourceName, name)
  701. }
  702. // SpanType sets the given span type on the started span. Some examples in the case of
  703. // the Datadog APM product could be "web", "db" or "cache".
  704. func SpanType(name string) StartSpanOption {
  705. return Tag(ext.SpanType, name)
  706. }
  707. var measuredTag = Tag(keyMeasured, 1)
  708. // Measured marks this span to be measured for metrics and stats calculations.
  709. func Measured() StartSpanOption {
  710. // cache a global instance of this tag: saves one alloc/call
  711. return measuredTag
  712. }
  713. // WithSpanID sets the SpanID on the started span, instead of using a random number.
  714. // If there is no parent Span (eg from ChildOf), then the TraceID will also be set to the
  715. // value given here.
  716. func WithSpanID(id uint64) StartSpanOption {
  717. return func(cfg *ddtrace.StartSpanConfig) {
  718. cfg.SpanID = id
  719. }
  720. }
  721. // ChildOf tells StartSpan to use the given span context as a parent for the
  722. // created span.
  723. func ChildOf(ctx ddtrace.SpanContext) StartSpanOption {
  724. return func(cfg *ddtrace.StartSpanConfig) {
  725. cfg.Parent = ctx
  726. }
  727. }
  728. // withContext associates the ctx with the span.
  729. func withContext(ctx context.Context) StartSpanOption {
  730. return func(cfg *ddtrace.StartSpanConfig) {
  731. cfg.Context = ctx
  732. }
  733. }
  734. // StartTime sets a custom time as the start time for the created span. By
  735. // default a span is started using the creation time.
  736. func StartTime(t time.Time) StartSpanOption {
  737. return func(cfg *ddtrace.StartSpanConfig) {
  738. cfg.StartTime = t
  739. }
  740. }
  741. // AnalyticsRate sets a custom analytics rate for a span. It decides the percentage
  742. // of events that will be picked up by the App Analytics product. It's represents a
  743. // float64 between 0 and 1 where 0.5 would represent 50% of events.
  744. func AnalyticsRate(rate float64) StartSpanOption {
  745. if math.IsNaN(rate) {
  746. return func(cfg *ddtrace.StartSpanConfig) {}
  747. }
  748. return Tag(ext.EventSampleRate, rate)
  749. }
  750. // FinishOption is a configuration option for FinishSpan. It is aliased in order
  751. // to help godoc group all the functions returning it together. It is considered
  752. // more correct to refer to it as the type as the origin, ddtrace.FinishOption.
  753. type FinishOption = ddtrace.FinishOption
  754. // FinishTime sets the given time as the finishing time for the span. By default,
  755. // the current time is used.
  756. func FinishTime(t time.Time) FinishOption {
  757. return func(cfg *ddtrace.FinishConfig) {
  758. cfg.FinishTime = t
  759. }
  760. }
  761. // WithError marks the span as having had an error. It uses the information from
  762. // err to set tags such as the error message, error type and stack trace. It has
  763. // no effect if the error is nil.
  764. func WithError(err error) FinishOption {
  765. return func(cfg *ddtrace.FinishConfig) {
  766. cfg.Error = err
  767. }
  768. }
  769. // NoDebugStack prevents any error presented using the WithError finishing option
  770. // from generating a stack trace. This is useful in situations where errors are frequent
  771. // and performance is critical.
  772. func NoDebugStack() FinishOption {
  773. return func(cfg *ddtrace.FinishConfig) {
  774. cfg.NoDebugStack = true
  775. }
  776. }
  777. // StackFrames limits the number of stack frames included into erroneous spans to n, starting from skip.
  778. func StackFrames(n, skip uint) FinishOption {
  779. if n == 0 {
  780. return NoDebugStack()
  781. }
  782. return func(cfg *ddtrace.FinishConfig) {
  783. cfg.StackFrames = n
  784. cfg.SkipStackFrames = skip
  785. }
  786. }
  787. // UserMonitoringConfig is used to configure what is used to identify a user.
  788. // This configuration can be set by combining one or several UserMonitoringOption with a call to SetUser().
  789. type UserMonitoringConfig struct {
  790. PropagateID bool
  791. Email string
  792. Name string
  793. Role string
  794. SessionID string
  795. Scope string
  796. }
  797. // UserMonitoringOption represents a function that can be provided as a parameter to SetUser.
  798. type UserMonitoringOption func(*UserMonitoringConfig)
  799. // WithUserEmail returns the option setting the email of the authenticated user.
  800. func WithUserEmail(email string) UserMonitoringOption {
  801. return func(cfg *UserMonitoringConfig) {
  802. cfg.Email = email
  803. }
  804. }
  805. // WithUserName returns the option setting the name of the authenticated user.
  806. func WithUserName(name string) UserMonitoringOption {
  807. return func(cfg *UserMonitoringConfig) {
  808. cfg.Name = name
  809. }
  810. }
  811. // WithUserSessionID returns the option setting the session ID of the authenticated user.
  812. func WithUserSessionID(sessionID string) UserMonitoringOption {
  813. return func(cfg *UserMonitoringConfig) {
  814. cfg.SessionID = sessionID
  815. }
  816. }
  817. // WithUserRole returns the option setting the role of the authenticated user.
  818. func WithUserRole(role string) UserMonitoringOption {
  819. return func(cfg *UserMonitoringConfig) {
  820. cfg.Role = role
  821. }
  822. }
  823. // WithUserScope returns the option setting the scope (authorizations) of the authenticated user.
  824. func WithUserScope(scope string) UserMonitoringOption {
  825. return func(cfg *UserMonitoringConfig) {
  826. cfg.Scope = scope
  827. }
  828. }
  829. // WithPropagation returns the option allowing the user id to be propagated through distributed traces.
  830. // The user id is base64 encoded and added to the datadog propagated tags header.
  831. // This option should only be used if you are certain that the user id passed to `SetUser()` does not contain any
  832. // personal identifiable information or any kind of sensitive data, as it will be leaked to other services.
  833. func WithPropagation() UserMonitoringOption {
  834. return func(cfg *UserMonitoringConfig) {
  835. cfg.PropagateID = true
  836. }
  837. }