1// Copyright The OpenTelemetry Authors 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 15package trace // import "go.opentelemetry.io/otel/sdk/trace" 16 17import ( 18 "context" 19 "fmt" 20 "reflect" 21 "sync" 22 "time" 23 24 "go.opentelemetry.io/otel/attribute" 25 "go.opentelemetry.io/otel/codes" 26 "go.opentelemetry.io/otel/semconv" 27 "go.opentelemetry.io/otel/trace" 28 29 "go.opentelemetry.io/otel/sdk/instrumentation" 30 "go.opentelemetry.io/otel/sdk/internal" 31 "go.opentelemetry.io/otel/sdk/resource" 32) 33 34// ReadOnlySpan allows reading information from the data structure underlying a 35// trace.Span. It is used in places where reading information from a span is 36// necessary but changing the span isn't necessary or allowed. 37// TODO: Should we make the methods unexported? The purpose of this interface 38// is controlling access to `span` fields, not having multiple implementations. 39type ReadOnlySpan interface { 40 Name() string 41 SpanContext() trace.SpanContext 42 Parent() trace.SpanContext 43 SpanKind() trace.SpanKind 44 StartTime() time.Time 45 EndTime() time.Time 46 Attributes() []attribute.KeyValue 47 Links() []trace.Link 48 Events() []trace.Event 49 StatusCode() codes.Code 50 StatusMessage() string 51 Tracer() trace.Tracer 52 IsRecording() bool 53 InstrumentationLibrary() instrumentation.Library 54 Resource() *resource.Resource 55 Snapshot() *SpanSnapshot 56 57 // A private method to prevent users implementing the 58 // interface and so future additions to it will not 59 // violate compatibility. 60 private() 61} 62 63// ReadWriteSpan exposes the same methods as trace.Span and in addition allows 64// reading information from the underlying data structure. 65// This interface exposes the union of the methods of trace.Span (which is a 66// "write-only" span) and ReadOnlySpan. New methods for writing or reading span 67// information should be added under trace.Span or ReadOnlySpan, respectively. 68type ReadWriteSpan interface { 69 trace.Span 70 ReadOnlySpan 71} 72 73// span is an implementation of the OpenTelemetry Span API representing the 74// individual component of a trace. 75type span struct { 76 // mu protects the contents of this span. 77 mu sync.Mutex 78 79 // parent holds the parent span of this span as a trace.SpanContext. 80 parent trace.SpanContext 81 82 // spanKind represents the kind of this span as a trace.SpanKind. 83 spanKind trace.SpanKind 84 85 // name is the name of this span. 86 name string 87 88 // startTime is the time at which this span was started. 89 startTime time.Time 90 91 // endTime is the time at which this span was ended. It contains the zero 92 // value of time.Time until the span is ended. 93 endTime time.Time 94 95 // statusCode represents the status of this span as a codes.Code value. 96 statusCode codes.Code 97 98 // statusMessage represents the status of this span as a string. 99 statusMessage string 100 101 // childSpanCount holds the number of child spans created for this span. 102 childSpanCount int 103 104 // resource contains attributes representing an entity that produced this 105 // span. 106 resource *resource.Resource 107 108 // instrumentationLibrary defines the instrumentation library used to 109 // provide instrumentation. 110 instrumentationLibrary instrumentation.Library 111 112 // spanContext holds the SpanContext of this span. 113 spanContext trace.SpanContext 114 115 // attributes are capped at configured limit. When the capacity is reached 116 // an oldest entry is removed to create room for a new entry. 117 attributes *attributesMap 118 119 // messageEvents are stored in FIFO queue capped by configured limit. 120 messageEvents *evictedQueue 121 122 // links are stored in FIFO queue capped by configured limit. 123 links *evictedQueue 124 125 // executionTracerTaskEnd ends the execution tracer span. 126 executionTracerTaskEnd func() 127 128 // tracer is the SDK tracer that created this span. 129 tracer *tracer 130 131 // spanLimits holds the limits to this span. 132 spanLimits SpanLimits 133} 134 135var _ trace.Span = &span{} 136 137// SpanContext returns the SpanContext of this span. 138func (s *span) SpanContext() trace.SpanContext { 139 if s == nil { 140 return trace.SpanContext{} 141 } 142 return s.spanContext 143} 144 145// IsRecording returns if this span is being recorded. If this span has ended 146// this will return false. 147func (s *span) IsRecording() bool { 148 if s == nil { 149 return false 150 } 151 s.mu.Lock() 152 defer s.mu.Unlock() 153 154 return !s.startTime.IsZero() && s.endTime.IsZero() 155} 156 157// SetStatus sets the status of this span in the form of a code and a 158// message. This overrides the existing value of this span's status if one 159// exists. Message will be set only if status is error. If this span is not being 160// recorded than this method does nothing. 161func (s *span) SetStatus(code codes.Code, msg string) { 162 if !s.IsRecording() { 163 return 164 } 165 s.mu.Lock() 166 s.statusCode = code 167 if code == codes.Error { 168 s.statusMessage = msg 169 } 170 s.mu.Unlock() 171} 172 173// SetAttributes sets attributes of this span. 174// 175// If a key from attributes already exists the value associated with that key 176// will be overwritten with the value contained in attributes. 177// 178// If this span is not being recorded than this method does nothing. 179func (s *span) SetAttributes(attributes ...attribute.KeyValue) { 180 if !s.IsRecording() { 181 return 182 } 183 s.copyToCappedAttributes(attributes...) 184} 185 186// End ends the span. This method does nothing if the span is already ended or 187// is not being recorded. 188// 189// The only SpanOption currently supported is WithTimestamp which will set the 190// end time for a Span's life-cycle. 191// 192// If this method is called while panicking an error event is added to the 193// Span before ending it and the panic is continued. 194func (s *span) End(options ...trace.SpanOption) { 195 // Do not start by checking if the span is being recorded which requires 196 // acquiring a lock. Make a minimal check that the span is not nil. 197 if s == nil { 198 return 199 } 200 201 // Store the end time as soon as possible to avoid artificially increasing 202 // the span's duration in case some operation below takes a while. 203 et := internal.MonotonicEndTime(s.startTime) 204 205 // Do relative expensive check now that we have an end time and see if we 206 // need to do any more processing. 207 if !s.IsRecording() { 208 return 209 } 210 211 if recovered := recover(); recovered != nil { 212 // Record but don't stop the panic. 213 defer panic(recovered) 214 s.addEvent( 215 semconv.ExceptionEventName, 216 trace.WithAttributes( 217 semconv.ExceptionTypeKey.String(typeStr(recovered)), 218 semconv.ExceptionMessageKey.String(fmt.Sprint(recovered)), 219 ), 220 ) 221 } 222 223 if s.executionTracerTaskEnd != nil { 224 s.executionTracerTaskEnd() 225 } 226 227 config := trace.NewSpanConfig(options...) 228 229 s.mu.Lock() 230 // Setting endTime to non-zero marks the span as ended and not recording. 231 if config.Timestamp.IsZero() { 232 s.endTime = et 233 } else { 234 s.endTime = config.Timestamp 235 } 236 s.mu.Unlock() 237 238 sps, ok := s.tracer.provider.spanProcessors.Load().(spanProcessorStates) 239 mustExportOrProcess := ok && len(sps) > 0 240 if mustExportOrProcess { 241 for _, sp := range sps { 242 sp.sp.OnEnd(s) 243 } 244 } 245} 246 247// RecordError will record err as a span event for this span. An additional call to 248// SetStatus is required if the Status of the Span should be set to Error, this method 249// does not change the Span status. If this span is not being recorded or err is nil 250// than this method does nothing. 251func (s *span) RecordError(err error, opts ...trace.EventOption) { 252 if s == nil || err == nil || !s.IsRecording() { 253 return 254 } 255 256 opts = append(opts, trace.WithAttributes( 257 semconv.ExceptionTypeKey.String(typeStr(err)), 258 semconv.ExceptionMessageKey.String(err.Error()), 259 )) 260 s.addEvent(semconv.ExceptionEventName, opts...) 261} 262 263func typeStr(i interface{}) string { 264 t := reflect.TypeOf(i) 265 if t.PkgPath() == "" && t.Name() == "" { 266 // Likely a builtin type. 267 return t.String() 268 } 269 return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) 270} 271 272// Tracer returns the Tracer that created this span. 273func (s *span) Tracer() trace.Tracer { 274 return s.tracer 275} 276 277// AddEvent adds an event with the provided name and options. If this span is 278// not being recorded than this method does nothing. 279func (s *span) AddEvent(name string, o ...trace.EventOption) { 280 if !s.IsRecording() { 281 return 282 } 283 s.addEvent(name, o...) 284} 285 286func (s *span) addEvent(name string, o ...trace.EventOption) { 287 c := trace.NewEventConfig(o...) 288 289 // Discard over limited attributes 290 var discarded int 291 if len(c.Attributes) > s.spanLimits.AttributePerEventCountLimit { 292 discarded = len(c.Attributes) - s.spanLimits.AttributePerEventCountLimit 293 c.Attributes = c.Attributes[:s.spanLimits.AttributePerEventCountLimit] 294 } 295 296 s.mu.Lock() 297 defer s.mu.Unlock() 298 s.messageEvents.add(trace.Event{ 299 Name: name, 300 Attributes: c.Attributes, 301 DroppedAttributeCount: discarded, 302 Time: c.Timestamp, 303 }) 304} 305 306// SetName sets the name of this span. If this span is not being recorded than 307// this method does nothing. 308func (s *span) SetName(name string) { 309 if !s.IsRecording() { 310 return 311 } 312 313 s.mu.Lock() 314 defer s.mu.Unlock() 315 s.name = name 316} 317 318// Name returns the name of this span. 319func (s *span) Name() string { 320 s.mu.Lock() 321 defer s.mu.Unlock() 322 return s.name 323} 324 325// Name returns the SpanContext of this span's parent span. 326func (s *span) Parent() trace.SpanContext { 327 s.mu.Lock() 328 defer s.mu.Unlock() 329 return s.parent 330} 331 332// SpanKind returns the SpanKind of this span. 333func (s *span) SpanKind() trace.SpanKind { 334 s.mu.Lock() 335 defer s.mu.Unlock() 336 return s.spanKind 337} 338 339// StartTime returns the time this span started. 340func (s *span) StartTime() time.Time { 341 s.mu.Lock() 342 defer s.mu.Unlock() 343 return s.startTime 344} 345 346// EndTime returns the time this span ended. For spans that have not yet 347// ended, the returned value will be the zero value of time.Time. 348func (s *span) EndTime() time.Time { 349 s.mu.Lock() 350 defer s.mu.Unlock() 351 return s.endTime 352} 353 354// Attributes returns the attributes of this span. 355func (s *span) Attributes() []attribute.KeyValue { 356 s.mu.Lock() 357 defer s.mu.Unlock() 358 if s.attributes.evictList.Len() == 0 { 359 return []attribute.KeyValue{} 360 } 361 return s.attributes.toKeyValue() 362} 363 364// Links returns the links of this span. 365func (s *span) Links() []trace.Link { 366 s.mu.Lock() 367 defer s.mu.Unlock() 368 if len(s.links.queue) == 0 { 369 return []trace.Link{} 370 } 371 return s.interfaceArrayToLinksArray() 372} 373 374// Events returns the events of this span. 375func (s *span) Events() []trace.Event { 376 s.mu.Lock() 377 defer s.mu.Unlock() 378 if len(s.messageEvents.queue) == 0 { 379 return []trace.Event{} 380 } 381 return s.interfaceArrayToMessageEventArray() 382} 383 384// StatusCode returns the status code of this span. 385func (s *span) StatusCode() codes.Code { 386 s.mu.Lock() 387 defer s.mu.Unlock() 388 return s.statusCode 389} 390 391// StatusMessage returns the status message of this span. 392func (s *span) StatusMessage() string { 393 s.mu.Lock() 394 defer s.mu.Unlock() 395 return s.statusMessage 396} 397 398// InstrumentationLibrary returns the instrumentation.Library associated with 399// the Tracer that created this span. 400func (s *span) InstrumentationLibrary() instrumentation.Library { 401 s.mu.Lock() 402 defer s.mu.Unlock() 403 return s.instrumentationLibrary 404} 405 406// Resource returns the Resource associated with the Tracer that created this 407// span. 408func (s *span) Resource() *resource.Resource { 409 s.mu.Lock() 410 defer s.mu.Unlock() 411 return s.resource 412} 413 414func (s *span) addLink(link trace.Link) { 415 if !s.IsRecording() { 416 return 417 } 418 s.mu.Lock() 419 defer s.mu.Unlock() 420 421 // Discard over limited attributes 422 if len(link.Attributes) > s.spanLimits.AttributePerLinkCountLimit { 423 link.DroppedAttributeCount = len(link.Attributes) - s.spanLimits.AttributePerLinkCountLimit 424 link.Attributes = link.Attributes[:s.spanLimits.AttributePerLinkCountLimit] 425 } 426 427 s.links.add(link) 428} 429 430// Snapshot creates a snapshot representing the current state of the span as an 431// export.SpanSnapshot and returns a pointer to it. 432func (s *span) Snapshot() *SpanSnapshot { 433 var sd SpanSnapshot 434 s.mu.Lock() 435 defer s.mu.Unlock() 436 437 sd.ChildSpanCount = s.childSpanCount 438 sd.EndTime = s.endTime 439 sd.InstrumentationLibrary = s.instrumentationLibrary 440 sd.Name = s.name 441 sd.Parent = s.parent 442 sd.Resource = s.resource 443 sd.SpanContext = s.spanContext 444 sd.SpanKind = s.spanKind 445 sd.StartTime = s.startTime 446 sd.StatusCode = s.statusCode 447 sd.StatusMessage = s.statusMessage 448 449 if s.attributes.evictList.Len() > 0 { 450 sd.Attributes = s.attributes.toKeyValue() 451 sd.DroppedAttributeCount = s.attributes.droppedCount 452 } 453 if len(s.messageEvents.queue) > 0 { 454 sd.MessageEvents = s.interfaceArrayToMessageEventArray() 455 sd.DroppedMessageEventCount = s.messageEvents.droppedCount 456 } 457 if len(s.links.queue) > 0 { 458 sd.Links = s.interfaceArrayToLinksArray() 459 sd.DroppedLinkCount = s.links.droppedCount 460 } 461 return &sd 462} 463 464func (s *span) interfaceArrayToLinksArray() []trace.Link { 465 linkArr := make([]trace.Link, 0) 466 for _, value := range s.links.queue { 467 linkArr = append(linkArr, value.(trace.Link)) 468 } 469 return linkArr 470} 471 472func (s *span) interfaceArrayToMessageEventArray() []trace.Event { 473 messageEventArr := make([]trace.Event, 0) 474 for _, value := range s.messageEvents.queue { 475 messageEventArr = append(messageEventArr, value.(trace.Event)) 476 } 477 return messageEventArr 478} 479 480func (s *span) copyToCappedAttributes(attributes ...attribute.KeyValue) { 481 s.mu.Lock() 482 defer s.mu.Unlock() 483 for _, a := range attributes { 484 // Ensure attributes conform to the specification: 485 // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/common/common.md#attributes 486 if a.Valid() { 487 s.attributes.add(a) 488 } 489 } 490} 491 492func (s *span) addChild() { 493 if !s.IsRecording() { 494 return 495 } 496 s.mu.Lock() 497 s.childSpanCount++ 498 s.mu.Unlock() 499} 500 501func (*span) private() {} 502 503func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.SpanConfig) *span { 504 span := &span{} 505 506 provider := tr.provider 507 508 // If told explicitly to make this a new root use a zero value SpanContext 509 // as a parent which contains an invalid trace ID and is not remote. 510 var psc trace.SpanContext 511 if !o.NewRoot { 512 psc = trace.SpanContextFromContext(ctx) 513 } 514 515 // If there is a valid parent trace ID, use it to ensure the continuity of 516 // the trace. Always generate a new span ID so other components can rely 517 // on a unique span ID, even if the Span is non-recording. 518 var tid trace.TraceID 519 var sid trace.SpanID 520 if !psc.TraceID().IsValid() { 521 tid, sid = provider.idGenerator.NewIDs(ctx) 522 } else { 523 tid = psc.TraceID() 524 sid = provider.idGenerator.NewSpanID(ctx, tid) 525 } 526 527 spanLimits := provider.spanLimits 528 span.attributes = newAttributesMap(spanLimits.AttributeCountLimit) 529 span.messageEvents = newEvictedQueue(spanLimits.EventCountLimit) 530 span.links = newEvictedQueue(spanLimits.LinkCountLimit) 531 span.spanLimits = spanLimits 532 533 samplingResult := provider.sampler.ShouldSample(SamplingParameters{ 534 ParentContext: ctx, 535 TraceID: tid, 536 Name: name, 537 Kind: o.SpanKind, 538 Attributes: o.Attributes, 539 Links: o.Links, 540 }) 541 542 scc := trace.SpanContextConfig{ 543 TraceID: tid, 544 SpanID: sid, 545 TraceState: samplingResult.Tracestate, 546 } 547 if isSampled(samplingResult) { 548 scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled 549 } else { 550 scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled 551 } 552 span.spanContext = trace.NewSpanContext(scc) 553 554 if !isRecording(samplingResult) { 555 return span 556 } 557 558 startTime := o.Timestamp 559 if startTime.IsZero() { 560 startTime = time.Now() 561 } 562 span.startTime = startTime 563 564 span.spanKind = trace.ValidateSpanKind(o.SpanKind) 565 span.name = name 566 span.parent = psc 567 span.resource = provider.resource 568 span.instrumentationLibrary = tr.instrumentationLibrary 569 570 span.SetAttributes(samplingResult.Attributes...) 571 572 return span 573} 574 575func isRecording(s SamplingResult) bool { 576 return s.Decision == RecordOnly || s.Decision == RecordAndSample 577} 578 579func isSampled(s SamplingResult) bool { 580 return s.Decision == RecordAndSample 581} 582 583// SpanSnapshot is a snapshot of a span which contains all the information 584// collected by the span. Its main purpose is exporting completed spans. 585// Although SpanSnapshot fields can be accessed and potentially modified, 586// SpanSnapshot should be treated as immutable. Changes to the span from which 587// the SpanSnapshot was created are NOT reflected in the SpanSnapshot. 588type SpanSnapshot struct { 589 SpanContext trace.SpanContext 590 Parent trace.SpanContext 591 SpanKind trace.SpanKind 592 Name string 593 StartTime time.Time 594 // The wall clock time of EndTime will be adjusted to always be offset 595 // from StartTime by the duration of the span. 596 EndTime time.Time 597 Attributes []attribute.KeyValue 598 MessageEvents []trace.Event 599 Links []trace.Link 600 StatusCode codes.Code 601 StatusMessage string 602 603 // DroppedAttributeCount contains dropped attributes for the span itself. 604 DroppedAttributeCount int 605 DroppedMessageEventCount int 606 DroppedLinkCount int 607 608 // ChildSpanCount holds the number of child span created for this span. 609 ChildSpanCount int 610 611 // Resource contains attributes representing an entity that produced this span. 612 Resource *resource.Resource 613 614 // InstrumentationLibrary defines the instrumentation library used to 615 // provide instrumentation. 616 InstrumentationLibrary instrumentation.Library 617} 618