123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // Copyright (c) 2017-2018 Uber Technologies, Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package jaeger
- import (
- "sync"
- "time"
- "github.com/opentracing/opentracing-go"
- "github.com/opentracing/opentracing-go/ext"
- "github.com/opentracing/opentracing-go/log"
- )
- // Span implements opentracing.Span
- type Span struct {
- sync.RWMutex
- tracer *Tracer
- context SpanContext
- // The name of the "operation" this span is an instance of.
- // Known as a "span name" in some implementations.
- operationName string
- // firstInProcess, if true, indicates that this span is the root of the (sub)tree
- // of spans in the current process. In other words it's true for the root spans,
- // and the ingress spans when the process joins another trace.
- firstInProcess bool
- // startTime is the timestamp indicating when the span began, with microseconds precision.
- startTime time.Time
- // duration returns duration of the span with microseconds precision.
- // Zero value means duration is unknown.
- duration time.Duration
- // tags attached to this span
- tags []Tag
- // The span's "micro-log"
- logs []opentracing.LogRecord
- // references for this span
- references []Reference
- observer ContribSpanObserver
- }
- // Tag is a simple key value wrapper.
- // TODO deprecate in the next major release, use opentracing.Tag instead.
- type Tag struct {
- key string
- value interface{}
- }
- // SetOperationName sets or changes the operation name.
- func (s *Span) SetOperationName(operationName string) opentracing.Span {
- s.Lock()
- defer s.Unlock()
- if s.context.IsSampled() {
- s.operationName = operationName
- }
- s.observer.OnSetOperationName(operationName)
- return s
- }
- // SetTag implements SetTag() of opentracing.Span
- func (s *Span) SetTag(key string, value interface{}) opentracing.Span {
- s.observer.OnSetTag(key, value)
- if key == string(ext.SamplingPriority) && !setSamplingPriority(s, value) {
- return s
- }
- s.Lock()
- defer s.Unlock()
- if s.context.IsSampled() {
- s.setTagNoLocking(key, value)
- }
- return s
- }
- func (s *Span) setTagNoLocking(key string, value interface{}) {
- s.tags = append(s.tags, Tag{key: key, value: value})
- }
- // LogFields implements opentracing.Span API
- func (s *Span) LogFields(fields ...log.Field) {
- s.Lock()
- defer s.Unlock()
- if !s.context.IsSampled() {
- return
- }
- s.logFieldsNoLocking(fields...)
- }
- // this function should only be called while holding a Write lock
- func (s *Span) logFieldsNoLocking(fields ...log.Field) {
- lr := opentracing.LogRecord{
- Fields: fields,
- Timestamp: time.Now(),
- }
- s.appendLog(lr)
- }
- // LogKV implements opentracing.Span API
- func (s *Span) LogKV(alternatingKeyValues ...interface{}) {
- s.RLock()
- sampled := s.context.IsSampled()
- s.RUnlock()
- if !sampled {
- return
- }
- fields, err := log.InterleavedKVToFields(alternatingKeyValues...)
- if err != nil {
- s.LogFields(log.Error(err), log.String("function", "LogKV"))
- return
- }
- s.LogFields(fields...)
- }
- // LogEvent implements opentracing.Span API
- func (s *Span) LogEvent(event string) {
- s.Log(opentracing.LogData{Event: event})
- }
- // LogEventWithPayload implements opentracing.Span API
- func (s *Span) LogEventWithPayload(event string, payload interface{}) {
- s.Log(opentracing.LogData{Event: event, Payload: payload})
- }
- // Log implements opentracing.Span API
- func (s *Span) Log(ld opentracing.LogData) {
- s.Lock()
- defer s.Unlock()
- if s.context.IsSampled() {
- if ld.Timestamp.IsZero() {
- ld.Timestamp = s.tracer.timeNow()
- }
- s.appendLog(ld.ToLogRecord())
- }
- }
- // this function should only be called while holding a Write lock
- func (s *Span) appendLog(lr opentracing.LogRecord) {
- // TODO add logic to limit number of logs per span (issue #46)
- s.logs = append(s.logs, lr)
- }
- // SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext
- func (s *Span) SetBaggageItem(key, value string) opentracing.Span {
- s.Lock()
- defer s.Unlock()
- s.tracer.setBaggage(s, key, value)
- return s
- }
- // BaggageItem implements BaggageItem() of opentracing.SpanContext
- func (s *Span) BaggageItem(key string) string {
- s.RLock()
- defer s.RUnlock()
- return s.context.baggage[key]
- }
- // Finish implements opentracing.Span API
- func (s *Span) Finish() {
- s.FinishWithOptions(opentracing.FinishOptions{})
- }
- // FinishWithOptions implements opentracing.Span API
- func (s *Span) FinishWithOptions(options opentracing.FinishOptions) {
- if options.FinishTime.IsZero() {
- options.FinishTime = s.tracer.timeNow()
- }
- s.observer.OnFinish(options)
- s.Lock()
- if s.context.IsSampled() {
- s.duration = options.FinishTime.Sub(s.startTime)
- // Note: bulk logs are not subject to maxLogsPerSpan limit
- if options.LogRecords != nil {
- s.logs = append(s.logs, options.LogRecords...)
- }
- for _, ld := range options.BulkLogData {
- s.logs = append(s.logs, ld.ToLogRecord())
- }
- }
- s.Unlock()
- // call reportSpan even for non-sampled traces, to return span to the pool
- s.tracer.reportSpan(s)
- }
- // Context implements opentracing.Span API
- func (s *Span) Context() opentracing.SpanContext {
- s.Lock()
- defer s.Unlock()
- return s.context
- }
- // Tracer implements opentracing.Span API
- func (s *Span) Tracer() opentracing.Tracer {
- return s.tracer
- }
- func (s *Span) String() string {
- s.RLock()
- defer s.RUnlock()
- return s.context.String()
- }
- // OperationName allows retrieving current operation name.
- func (s *Span) OperationName() string {
- s.RLock()
- defer s.RUnlock()
- return s.operationName
- }
- func (s *Span) serviceName() string {
- return s.tracer.serviceName
- }
- // setSamplingPriority returns true if the flag was updated successfully, false otherwise.
- func setSamplingPriority(s *Span, value interface{}) bool {
- s.Lock()
- defer s.Unlock()
- val, ok := value.(uint16)
- if !ok {
- return false
- }
- if val == 0 {
- s.context.flags = s.context.flags & (^flagSampled)
- return true
- }
- if s.tracer.isDebugAllowed(s.operationName) {
- s.context.flags = s.context.flags | flagDebug | flagSampled
- return true
- }
- return false
- }
|