auto.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package trace // import "go.opentelemetry.io/otel/trace"
  4. import (
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "math"
  9. "os"
  10. "reflect"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "sync/atomic"
  16. "time"
  17. "unicode/utf8"
  18. "go.opentelemetry.io/otel/attribute"
  19. "go.opentelemetry.io/otel/codes"
  20. semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
  21. "go.opentelemetry.io/otel/trace/embedded"
  22. "go.opentelemetry.io/otel/trace/internal/telemetry"
  23. )
  24. // newAutoTracerProvider returns an auto-instrumentable [trace.TracerProvider].
  25. // If an [go.opentelemetry.io/auto.Instrumentation] is configured to instrument
  26. // the process using the returned TracerProvider, all of the telemetry it
  27. // produces will be processed and handled by that Instrumentation. By default,
  28. // if no Instrumentation instruments the TracerProvider it will not generate
  29. // any trace telemetry.
  30. func newAutoTracerProvider() TracerProvider { return tracerProviderInstance }
  31. var tracerProviderInstance = new(autoTracerProvider)
  32. type autoTracerProvider struct{ embedded.TracerProvider }
  33. var _ TracerProvider = autoTracerProvider{}
  34. func (p autoTracerProvider) Tracer(name string, opts ...TracerOption) Tracer {
  35. cfg := NewTracerConfig(opts...)
  36. return autoTracer{
  37. name: name,
  38. version: cfg.InstrumentationVersion(),
  39. schemaURL: cfg.SchemaURL(),
  40. }
  41. }
  42. type autoTracer struct {
  43. embedded.Tracer
  44. name, schemaURL, version string
  45. }
  46. var _ Tracer = autoTracer{}
  47. func (t autoTracer) Start(ctx context.Context, name string, opts ...SpanStartOption) (context.Context, Span) {
  48. var psc, sc SpanContext
  49. sampled := true
  50. span := new(autoSpan)
  51. // Ask eBPF for sampling decision and span context info.
  52. t.start(ctx, span, &psc, &sampled, &sc)
  53. span.sampled.Store(sampled)
  54. span.spanContext = sc
  55. ctx = ContextWithSpan(ctx, span)
  56. if sampled {
  57. // Only build traces if sampled.
  58. cfg := NewSpanStartConfig(opts...)
  59. span.traces, span.span = t.traces(name, cfg, span.spanContext, psc)
  60. }
  61. return ctx, span
  62. }
  63. // Expected to be implemented in eBPF.
  64. //
  65. //go:noinline
  66. func (t *autoTracer) start(
  67. ctx context.Context,
  68. spanPtr *autoSpan,
  69. psc *SpanContext,
  70. sampled *bool,
  71. sc *SpanContext,
  72. ) {
  73. start(ctx, spanPtr, psc, sampled, sc)
  74. }
  75. // start is used for testing.
  76. var start = func(context.Context, *autoSpan, *SpanContext, *bool, *SpanContext) {}
  77. func (t autoTracer) traces(name string, cfg SpanConfig, sc, psc SpanContext) (*telemetry.Traces, *telemetry.Span) {
  78. span := &telemetry.Span{
  79. TraceID: telemetry.TraceID(sc.TraceID()),
  80. SpanID: telemetry.SpanID(sc.SpanID()),
  81. Flags: uint32(sc.TraceFlags()),
  82. TraceState: sc.TraceState().String(),
  83. ParentSpanID: telemetry.SpanID(psc.SpanID()),
  84. Name: name,
  85. Kind: spanKind(cfg.SpanKind()),
  86. }
  87. span.Attrs, span.DroppedAttrs = convCappedAttrs(maxSpan.Attrs, cfg.Attributes())
  88. links := cfg.Links()
  89. if limit := maxSpan.Links; limit == 0 {
  90. n := int64(len(links))
  91. if n > 0 {
  92. span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
  93. }
  94. } else {
  95. if limit > 0 {
  96. n := int64(max(len(links)-limit, 0))
  97. span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
  98. links = links[n:]
  99. }
  100. span.Links = convLinks(links)
  101. }
  102. if t := cfg.Timestamp(); !t.IsZero() {
  103. span.StartTime = cfg.Timestamp()
  104. } else {
  105. span.StartTime = time.Now()
  106. }
  107. return &telemetry.Traces{
  108. ResourceSpans: []*telemetry.ResourceSpans{
  109. {
  110. ScopeSpans: []*telemetry.ScopeSpans{
  111. {
  112. Scope: &telemetry.Scope{
  113. Name: t.name,
  114. Version: t.version,
  115. },
  116. Spans: []*telemetry.Span{span},
  117. SchemaURL: t.schemaURL,
  118. },
  119. },
  120. },
  121. },
  122. }, span
  123. }
  124. func spanKind(kind SpanKind) telemetry.SpanKind {
  125. switch kind {
  126. case SpanKindInternal:
  127. return telemetry.SpanKindInternal
  128. case SpanKindServer:
  129. return telemetry.SpanKindServer
  130. case SpanKindClient:
  131. return telemetry.SpanKindClient
  132. case SpanKindProducer:
  133. return telemetry.SpanKindProducer
  134. case SpanKindConsumer:
  135. return telemetry.SpanKindConsumer
  136. }
  137. return telemetry.SpanKind(0) // undefined.
  138. }
  139. type autoSpan struct {
  140. embedded.Span
  141. spanContext SpanContext
  142. sampled atomic.Bool
  143. mu sync.Mutex
  144. traces *telemetry.Traces
  145. span *telemetry.Span
  146. }
  147. func (s *autoSpan) SpanContext() SpanContext {
  148. if s == nil {
  149. return SpanContext{}
  150. }
  151. // s.spanContext is immutable, do not acquire lock s.mu.
  152. return s.spanContext
  153. }
  154. func (s *autoSpan) IsRecording() bool {
  155. if s == nil {
  156. return false
  157. }
  158. return s.sampled.Load()
  159. }
  160. func (s *autoSpan) SetStatus(c codes.Code, msg string) {
  161. if s == nil || !s.sampled.Load() {
  162. return
  163. }
  164. s.mu.Lock()
  165. defer s.mu.Unlock()
  166. if s.span.Status == nil {
  167. s.span.Status = new(telemetry.Status)
  168. }
  169. s.span.Status.Message = msg
  170. switch c {
  171. case codes.Unset:
  172. s.span.Status.Code = telemetry.StatusCodeUnset
  173. case codes.Error:
  174. s.span.Status.Code = telemetry.StatusCodeError
  175. case codes.Ok:
  176. s.span.Status.Code = telemetry.StatusCodeOK
  177. }
  178. }
  179. func (s *autoSpan) SetAttributes(attrs ...attribute.KeyValue) {
  180. if s == nil || !s.sampled.Load() {
  181. return
  182. }
  183. s.mu.Lock()
  184. defer s.mu.Unlock()
  185. limit := maxSpan.Attrs
  186. if limit == 0 {
  187. // No attributes allowed.
  188. n := int64(len(attrs))
  189. if n > 0 {
  190. s.span.DroppedAttrs += uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked.
  191. }
  192. return
  193. }
  194. m := make(map[string]int)
  195. for i, a := range s.span.Attrs {
  196. m[a.Key] = i
  197. }
  198. for _, a := range attrs {
  199. val := convAttrValue(a.Value)
  200. if val.Empty() {
  201. s.span.DroppedAttrs++
  202. continue
  203. }
  204. if idx, ok := m[string(a.Key)]; ok {
  205. s.span.Attrs[idx] = telemetry.Attr{
  206. Key: string(a.Key),
  207. Value: val,
  208. }
  209. } else if limit < 0 || len(s.span.Attrs) < limit {
  210. s.span.Attrs = append(s.span.Attrs, telemetry.Attr{
  211. Key: string(a.Key),
  212. Value: val,
  213. })
  214. m[string(a.Key)] = len(s.span.Attrs) - 1
  215. } else {
  216. s.span.DroppedAttrs++
  217. }
  218. }
  219. }
  220. // convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The
  221. // number of dropped attributes is also returned.
  222. func convCappedAttrs(limit int, attrs []attribute.KeyValue) ([]telemetry.Attr, uint32) {
  223. n := len(attrs)
  224. if limit == 0 {
  225. var out uint32
  226. if n > 0 {
  227. out = uint32(min(int64(n), math.MaxUint32)) // nolint: gosec // Bounds checked.
  228. }
  229. return nil, out
  230. }
  231. if limit < 0 {
  232. // Unlimited.
  233. return convAttrs(attrs), 0
  234. }
  235. if n < 0 {
  236. n = 0
  237. }
  238. limit = min(n, limit)
  239. return convAttrs(attrs[:limit]), uint32(n - limit) // nolint: gosec // Bounds checked.
  240. }
  241. func convAttrs(attrs []attribute.KeyValue) []telemetry.Attr {
  242. if len(attrs) == 0 {
  243. // Avoid allocations if not necessary.
  244. return nil
  245. }
  246. out := make([]telemetry.Attr, 0, len(attrs))
  247. for _, attr := range attrs {
  248. key := string(attr.Key)
  249. val := convAttrValue(attr.Value)
  250. if val.Empty() {
  251. continue
  252. }
  253. out = append(out, telemetry.Attr{Key: key, Value: val})
  254. }
  255. return out
  256. }
  257. func convAttrValue(value attribute.Value) telemetry.Value {
  258. switch value.Type() {
  259. case attribute.BOOL:
  260. return telemetry.BoolValue(value.AsBool())
  261. case attribute.INT64:
  262. return telemetry.Int64Value(value.AsInt64())
  263. case attribute.FLOAT64:
  264. return telemetry.Float64Value(value.AsFloat64())
  265. case attribute.STRING:
  266. v := truncate(maxSpan.AttrValueLen, value.AsString())
  267. return telemetry.StringValue(v)
  268. case attribute.BOOLSLICE:
  269. slice := value.AsBoolSlice()
  270. out := make([]telemetry.Value, 0, len(slice))
  271. for _, v := range slice {
  272. out = append(out, telemetry.BoolValue(v))
  273. }
  274. return telemetry.SliceValue(out...)
  275. case attribute.INT64SLICE:
  276. slice := value.AsInt64Slice()
  277. out := make([]telemetry.Value, 0, len(slice))
  278. for _, v := range slice {
  279. out = append(out, telemetry.Int64Value(v))
  280. }
  281. return telemetry.SliceValue(out...)
  282. case attribute.FLOAT64SLICE:
  283. slice := value.AsFloat64Slice()
  284. out := make([]telemetry.Value, 0, len(slice))
  285. for _, v := range slice {
  286. out = append(out, telemetry.Float64Value(v))
  287. }
  288. return telemetry.SliceValue(out...)
  289. case attribute.STRINGSLICE:
  290. slice := value.AsStringSlice()
  291. out := make([]telemetry.Value, 0, len(slice))
  292. for _, v := range slice {
  293. v = truncate(maxSpan.AttrValueLen, v)
  294. out = append(out, telemetry.StringValue(v))
  295. }
  296. return telemetry.SliceValue(out...)
  297. }
  298. return telemetry.Value{}
  299. }
  300. // truncate returns a truncated version of s such that it contains less than
  301. // the limit number of characters. Truncation is applied by returning the limit
  302. // number of valid characters contained in s.
  303. //
  304. // If limit is negative, it returns the original string.
  305. //
  306. // UTF-8 is supported. When truncating, all invalid characters are dropped
  307. // before applying truncation.
  308. //
  309. // If s already contains less than the limit number of bytes, it is returned
  310. // unchanged. No invalid characters are removed.
  311. func truncate(limit int, s string) string {
  312. // This prioritize performance in the following order based on the most
  313. // common expected use-cases.
  314. //
  315. // - Short values less than the default limit (128).
  316. // - Strings with valid encodings that exceed the limit.
  317. // - No limit.
  318. // - Strings with invalid encodings that exceed the limit.
  319. if limit < 0 || len(s) <= limit {
  320. return s
  321. }
  322. // Optimistically, assume all valid UTF-8.
  323. var b strings.Builder
  324. count := 0
  325. for i, c := range s {
  326. if c != utf8.RuneError {
  327. count++
  328. if count > limit {
  329. return s[:i]
  330. }
  331. continue
  332. }
  333. _, size := utf8.DecodeRuneInString(s[i:])
  334. if size == 1 {
  335. // Invalid encoding.
  336. b.Grow(len(s) - 1)
  337. _, _ = b.WriteString(s[:i])
  338. s = s[i:]
  339. break
  340. }
  341. }
  342. // Fast-path, no invalid input.
  343. if b.Cap() == 0 {
  344. return s
  345. }
  346. // Truncate while validating UTF-8.
  347. for i := 0; i < len(s) && count < limit; {
  348. c := s[i]
  349. if c < utf8.RuneSelf {
  350. // Optimization for single byte runes (common case).
  351. _ = b.WriteByte(c)
  352. i++
  353. count++
  354. continue
  355. }
  356. _, size := utf8.DecodeRuneInString(s[i:])
  357. if size == 1 {
  358. // We checked for all 1-byte runes above, this is a RuneError.
  359. i++
  360. continue
  361. }
  362. _, _ = b.WriteString(s[i : i+size])
  363. i += size
  364. count++
  365. }
  366. return b.String()
  367. }
  368. func (s *autoSpan) End(opts ...SpanEndOption) {
  369. if s == nil || !s.sampled.Swap(false) {
  370. return
  371. }
  372. // s.end exists so the lock (s.mu) is not held while s.ended is called.
  373. s.ended(s.end(opts))
  374. }
  375. func (s *autoSpan) end(opts []SpanEndOption) []byte {
  376. s.mu.Lock()
  377. defer s.mu.Unlock()
  378. cfg := NewSpanEndConfig(opts...)
  379. if t := cfg.Timestamp(); !t.IsZero() {
  380. s.span.EndTime = cfg.Timestamp()
  381. } else {
  382. s.span.EndTime = time.Now()
  383. }
  384. b, _ := json.Marshal(s.traces) // TODO: do not ignore this error.
  385. return b
  386. }
  387. // Expected to be implemented in eBPF.
  388. //
  389. //go:noinline
  390. func (*autoSpan) ended(buf []byte) { ended(buf) }
  391. // ended is used for testing.
  392. var ended = func([]byte) {}
  393. func (s *autoSpan) RecordError(err error, opts ...EventOption) {
  394. if s == nil || err == nil || !s.sampled.Load() {
  395. return
  396. }
  397. cfg := NewEventConfig(opts...)
  398. attrs := cfg.Attributes()
  399. attrs = append(attrs,
  400. semconv.ExceptionType(typeStr(err)),
  401. semconv.ExceptionMessage(err.Error()),
  402. )
  403. if cfg.StackTrace() {
  404. buf := make([]byte, 2048)
  405. n := runtime.Stack(buf, false)
  406. attrs = append(attrs, semconv.ExceptionStacktrace(string(buf[0:n])))
  407. }
  408. s.mu.Lock()
  409. defer s.mu.Unlock()
  410. s.addEvent(semconv.ExceptionEventName, cfg.Timestamp(), attrs)
  411. }
  412. func typeStr(i any) string {
  413. t := reflect.TypeOf(i)
  414. if t.PkgPath() == "" && t.Name() == "" {
  415. // Likely a builtin type.
  416. return t.String()
  417. }
  418. return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
  419. }
  420. func (s *autoSpan) AddEvent(name string, opts ...EventOption) {
  421. if s == nil || !s.sampled.Load() {
  422. return
  423. }
  424. cfg := NewEventConfig(opts...)
  425. s.mu.Lock()
  426. defer s.mu.Unlock()
  427. s.addEvent(name, cfg.Timestamp(), cfg.Attributes())
  428. }
  429. // addEvent adds an event with name and attrs at tStamp to the span. The span
  430. // lock (s.mu) needs to be held by the caller.
  431. func (s *autoSpan) addEvent(name string, tStamp time.Time, attrs []attribute.KeyValue) {
  432. limit := maxSpan.Events
  433. if limit == 0 {
  434. s.span.DroppedEvents++
  435. return
  436. }
  437. if limit > 0 && len(s.span.Events) == limit {
  438. // Drop head while avoiding allocation of more capacity.
  439. copy(s.span.Events[:limit-1], s.span.Events[1:])
  440. s.span.Events = s.span.Events[:limit-1]
  441. s.span.DroppedEvents++
  442. }
  443. e := &telemetry.SpanEvent{Time: tStamp, Name: name}
  444. e.Attrs, e.DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, attrs)
  445. s.span.Events = append(s.span.Events, e)
  446. }
  447. func (s *autoSpan) AddLink(link Link) {
  448. if s == nil || !s.sampled.Load() {
  449. return
  450. }
  451. l := maxSpan.Links
  452. s.mu.Lock()
  453. defer s.mu.Unlock()
  454. if l == 0 {
  455. s.span.DroppedLinks++
  456. return
  457. }
  458. if l > 0 && len(s.span.Links) == l {
  459. // Drop head while avoiding allocation of more capacity.
  460. copy(s.span.Links[:l-1], s.span.Links[1:])
  461. s.span.Links = s.span.Links[:l-1]
  462. s.span.DroppedLinks++
  463. }
  464. s.span.Links = append(s.span.Links, convLink(link))
  465. }
  466. func convLinks(links []Link) []*telemetry.SpanLink {
  467. out := make([]*telemetry.SpanLink, 0, len(links))
  468. for _, link := range links {
  469. out = append(out, convLink(link))
  470. }
  471. return out
  472. }
  473. func convLink(link Link) *telemetry.SpanLink {
  474. l := &telemetry.SpanLink{
  475. TraceID: telemetry.TraceID(link.SpanContext.TraceID()),
  476. SpanID: telemetry.SpanID(link.SpanContext.SpanID()),
  477. TraceState: link.SpanContext.TraceState().String(),
  478. Flags: uint32(link.SpanContext.TraceFlags()),
  479. }
  480. l.Attrs, l.DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, link.Attributes)
  481. return l
  482. }
  483. func (s *autoSpan) SetName(name string) {
  484. if s == nil || !s.sampled.Load() {
  485. return
  486. }
  487. s.mu.Lock()
  488. defer s.mu.Unlock()
  489. s.span.Name = name
  490. }
  491. func (*autoSpan) TracerProvider() TracerProvider { return newAutoTracerProvider() }
  492. // maxSpan are the span limits resolved during startup.
  493. var maxSpan = newSpanLimits()
  494. type spanLimits struct {
  495. // Attrs is the number of allowed attributes for a span.
  496. //
  497. // This is resolved from the environment variable value for the
  498. // OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the
  499. // environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT, or 128 if
  500. // that is not set, is used.
  501. Attrs int
  502. // AttrValueLen is the maximum attribute value length allowed for a span.
  503. //
  504. // This is resolved from the environment variable value for the
  505. // OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the
  506. // environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, or -1
  507. // if that is not set, is used.
  508. AttrValueLen int
  509. // Events is the number of allowed events for a span.
  510. //
  511. // This is resolved from the environment variable value for the
  512. // OTEL_SPAN_EVENT_COUNT_LIMIT key, or 128 is used if that is not set.
  513. Events int
  514. // EventAttrs is the number of allowed attributes for a span event.
  515. //
  516. // The is resolved from the environment variable value for the
  517. // OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key, or 128 is used if that is not set.
  518. EventAttrs int
  519. // Links is the number of allowed Links for a span.
  520. //
  521. // This is resolved from the environment variable value for the
  522. // OTEL_SPAN_LINK_COUNT_LIMIT, or 128 is used if that is not set.
  523. Links int
  524. // LinkAttrs is the number of allowed attributes for a span link.
  525. //
  526. // This is resolved from the environment variable value for the
  527. // OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, or 128 is used if that is not set.
  528. LinkAttrs int
  529. }
  530. func newSpanLimits() spanLimits {
  531. return spanLimits{
  532. Attrs: firstEnv(
  533. 128,
  534. "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
  535. "OTEL_ATTRIBUTE_COUNT_LIMIT",
  536. ),
  537. AttrValueLen: firstEnv(
  538. -1, // Unlimited.
  539. "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
  540. "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT",
  541. ),
  542. Events: firstEnv(128, "OTEL_SPAN_EVENT_COUNT_LIMIT"),
  543. EventAttrs: firstEnv(128, "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"),
  544. Links: firstEnv(128, "OTEL_SPAN_LINK_COUNT_LIMIT"),
  545. LinkAttrs: firstEnv(128, "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"),
  546. }
  547. }
  548. // firstEnv returns the parsed integer value of the first matching environment
  549. // variable from keys. The defaultVal is returned if the value is not an
  550. // integer or no match is found.
  551. func firstEnv(defaultVal int, keys ...string) int {
  552. for _, key := range keys {
  553. strV := os.Getenv(key)
  554. if strV == "" {
  555. continue
  556. }
  557. v, err := strconv.Atoi(strV)
  558. if err == nil {
  559. return v
  560. }
  561. // Ignore invalid environment variable.
  562. }
  563. return defaultVal
  564. }