import config from '../../config/AgentConfig' ; import Context from '../../trace/context/Context' ; import Span from '../../trace/span/Span' ; import DummySpan from '../../trace/span/DummySpan' ; import Segment from '../../trace/context/Segment' ; import EntrySpan from '../../trace/span/EntrySpan' ; import ExitSpan from '../../trace/span/ExitSpan' ; import LocalSpan from '../../trace/span/LocalSpan' ; import SegmentRef from './SegmentRef' ; import ContextManager from './ContextManager' ; import Tag from '../../Tag' ; import { Component } from '../Component' ; import { createLogger } from '../../logging' ; import { ContextCarrier } from './ContextCarrier' ; import { SpanType } from '../../proto/language-agent/Tracing_pb' ; import { emitter } from '../../lib/EventEmitter' ; const logger = createLogger ( __filename ) ; emitter . on ( 'segments-sent' , ( ) => { SpanContext . nActiveSegments = 0 ; } ) ; export default class SpanContext implements Context { static nActiveSegments = 0 ; spanId = 0 ; nSpans = 0 ; finished = false ; segment : Segment = new Segment ( ) ; ignoreCheck ( operation , type , carrier ? ) { if ( operation . match ( config . reIgnoreOperation ) || ( carrier && ! carrier . isValid ( ) ) ) return DummySpan . create ( ) ; return undefined ; } spanCheck ( spanType , operation , carrier ? ) { const span = this . ignoreCheck ( operation , SpanType . ENTRY , carrier ) ; if ( span ) return [ span ] ; const spans = ContextManager . spans ; const parent = spans [ spans . length - 1 ] ; if ( parent instanceof DummySpan ) return [ parent ] ; return [ null , parent ] ; } newSpan ( spanClass , parent , operation ) { const context = ! this . finished ? this : new SpanContext ( ) ; const span = new spanClass ( { id : context . spanId ++ , parentId : this . finished ? - 1 : parent ?. id ?? - 1 , context , operation , } ) ; if ( this . finished && parent ) { const carrier = new ContextCarrier ( parent . context . segment . relatedTraces [ 0 ] , parent . context . segment . segmentId , parent . id , config . serviceName , config . serviceInstance , parent . operation , parent . peer , [ ] ) ; const ref = SegmentRef . fromCarrier ( carrier ) ; context . segment . relate ( carrier . traceId ! ) ; span . refer ( ref ) ; } return span ; } newEntrySpan ( operation , carrier ? , inherit ? ) { let [ span , parent ] = this . spanCheck ( SpanType . ENTRY , operation , carrier ) ; if ( span ) return span ; if ( logger . _isDebugEnabled ) { logger . debug ( 'Creating entry span' , { parent , } ) ; } if ( ! this . finished && parent ?. type === SpanType . ENTRY && inherit && inherit === parent . component ) { span = parent ; parent . operation = operation ; } else { span = this . newSpan ( EntrySpan , parent ! , operation ) ; if ( carrier && carrier . isValid ( ) ) span . extract ( carrier ) ; } return span ; } newExitSpan ( operation , component , inherit ? ) { let [ span , parent ] = this . spanCheck ( SpanType . EXIT , operation ) ; if ( span ) return span ; if ( logger . _isDebugEnabled ) { logger . debug ( 'Creating exit span' , { operation , parent , } ) ; } if ( ! this . finished && parent ?. type === SpanType . EXIT && component === parent . inherit ) span = parent ; else span = this . newSpan ( ExitSpan , parent ! , operation ) ; if ( inherit ) span . inherit = inherit ; return span ; } newLocalSpan ( operation ) { const [ span , parent ] = this . spanCheck ( SpanType . LOCAL , operation ) ; if ( span ) return span ; if ( logger . _isDebugEnabled ) { logger . debug ( 'Creating local span' , { parentId : parent ?. id ?? - 1 , } ) ; } return this . newSpan ( LocalSpan , parent ! , operation ) ; } start ( span ) { const spans = ContextManager . spansDup ( ) ; logger . debug ( `Starting span ${ span . operation }` , { span , spans , nSpans : this . nSpans , } ) ; if ( ! this . nSpans ++ ) { SpanContext . nActiveSegments += 1 ; span . isCold = ContextManager . checkCold ( ) ; if ( span . isCold ) span . tag ( Tag . coldStart ( ) , true ) ; } if ( spans . indexOf ( span ) === - 1 ) spans . push ( span ) ; return this ; } stop ( span ) { logger . debug ( `Stopping span ${ span . operation }` , { span , spans : ContextManager . spans , nSpans : this . nSpans , } ) ; span . finish ( this . segment ) ; ContextManager . clear ( span ) ; if ( -- this . nSpans === 0 ) { this . finished = true ; emitter . emit ( 'segment-finished' , this . segment ) ; return true ; } return false ; } async ( span ) { logger . debug ( `Async span ${ span . operation }` , { span , spans : ContextManager . spans , nSpans : this . nSpans , } ) ; ContextManager . clear ( span ) ; } resync ( span ) { logger . debug ( `Resync span ${ span . operation }` , { span , spans : ContextManager . spans , nSpans : this . nSpans , } ) ; ContextManager . restore ( span ) ; } traceId ( ) { if ( ! this . segment . relatedTraces ) { return 'N/A' ; } return this . segment . relatedTraces [ 0 ] . toString ( ) ; } }