| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You 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. |
| |
| require_relative 'span' |
| require_relative 'entry_span' |
| require_relative 'exit_span' |
| require_relative 'carrier' |
| require_relative '../reporter/report' |
| |
| module Skywalking |
| module Tracing |
| class SpanContext |
| include Log::Logging |
| |
| attr_accessor :segment, :span_id, :correlation, :n_spans, :create_time |
| |
| def initialize |
| @segment = Tracing::Segment.new |
| @span_id = -1 |
| @correlation = {} |
| @n_spans = 0 |
| @create_time = (Process.clock_gettime(Process::CLOCK_REALTIME) * 1000).to_i |
| @primary_endpoint = nil |
| end |
| |
| def cfg |
| @config ||= ::Skywalking::Agent.agent_config |
| end |
| |
| def ignore_check(operation, carrier: nil) |
| if cfg[:re_ignore_operation].match?(operation) || carrier&.suppressed? |
| return Tracing::NoopSpan |
| end |
| |
| nil |
| end |
| |
| def peek |
| spans = ContextManager.spans |
| return spans.last unless spans.empty? |
| |
| nil |
| end |
| |
| def new_span(span_klass, parent, **kwargs) |
| finished = parent && !parent.stack_depth |
| context = finished ? SpanContext.new : self |
| |
| span = span_klass.new( |
| span_id: context.span_id += 1, |
| parent_id: parent && !finished ? parent.span_id : -1, |
| context: context, |
| **kwargs |
| ) |
| |
| if finished |
| carrier = Carrier.new( |
| trace_id: parent.context.segment.related_traces[0], |
| segment_id: parent.context.segment.segment_id, |
| span_id: parent.span_id, |
| service: cfg[:service_name], |
| service_instance: cfg[:instance_name], |
| endpoint: parent.operation, |
| peer: parent.peer, |
| correlation: parent.context.correlation |
| ) |
| span.extract(carrier) |
| end |
| |
| span |
| end |
| |
| def new_entry_span(operation, carrier: nil, inherit: nil) |
| span = ignore_check(operation) |
| return span if span |
| |
| parent = peek |
| debug 'create new entry span' |
| if parent && parent.kind == Kind::Entry && inherit == parent.component |
| span = parent |
| span.operation = operation |
| else |
| span = new_span(EntrySpan, parent, operation: operation) |
| span.extract(carrier) if carrier&.valid? |
| end |
| |
| span |
| end |
| |
| def new_local_span(operation) |
| span = ignore_check(operation) |
| return span if span |
| |
| parent = peek |
| debug 'create new local span' |
| new_span(Span, parent, operation: operation, kind: Kind::Local) |
| end |
| |
| def new_exit_span(operation, peer, component: nil, inherit: nil) |
| span = ignore_check(operation) |
| return span if span |
| |
| parent = peek |
| debug 'create new exit span' |
| |
| if parent && parent.kind == Kind::Exit && inherit == parent.inherit |
| span = parent |
| span.operation = operation |
| span.peer = peer |
| span.component = component |
| else |
| span = new_span(ExitSpan, parent, operation: operation, peer: peer, component: component) |
| end |
| span.inherit = inherit if inherit |
| |
| span |
| end |
| |
| def start(span) |
| @n_spans += 1 |
| spans = ContextManager.spans_dup |
| unless spans.include?(span) |
| spans << span |
| if @primary_endpoint.nil? |
| @primary_endpoint = PrimaryEndpoint.new(span) |
| else |
| @primary_endpoint.set_primary_endpoint(span) |
| end |
| end |
| end |
| |
| def stop?(span) |
| spans = ContextManager.spans_dup |
| span.finish?(@segment) |
| spans.delete(span) |
| @n_spans -= 1 |
| if @n_spans.zero? |
| if (trigger = Agent.instance&.reporter&.trigger) |
| trigger << @segment |
| end |
| return true |
| end |
| |
| false |
| end |
| |
| def active_span |
| peek |
| end |
| |
| class PrimaryEndpoint |
| def initialize(span) |
| @span = span |
| end |
| |
| def set_primary_endpoint(span) |
| if @span.kind != Kind::Entry && span.kind == Kind::Entry |
| @span = span |
| end |
| end |
| |
| def get_name |
| @span.operation |
| end |
| end |
| end |
| |
| class ContextManager |
| class << self |
| def spans |
| Thread.current[:spans] ||= [] |
| end |
| |
| def spans_dup |
| spans_dup = spans.dup |
| Thread.current[:spans] = spans_dup |
| |
| spans_dup |
| end |
| |
| def reset_spans |
| Thread.current[:spans] = [] |
| end |
| |
| def current_context |
| spans_ret = spans |
| if spans_ret.any? |
| spans.last.context |
| else |
| SpanContext.new |
| end |
| end |
| |
| def new_exit_span(operation:, peer: nil, component: nil, inherit: nil, &block) |
| context = current_context |
| span = context.new_exit_span(operation, peer, component: component, inherit: inherit) |
| span&.start |
| |
| begin |
| yield span if block_given? |
| ensure |
| span&.stop? |
| end |
| end |
| |
| def new_entry_span(operation:, carrier: nil, inherit: nil, &block) |
| context = current_context |
| span = context.new_entry_span(operation, carrier: carrier, inherit: inherit) |
| span&.start |
| |
| begin |
| yield span if block_given? |
| ensure |
| span&.stop? |
| end |
| end |
| |
| def new_local_span(operation:, &block) |
| context = current_context |
| span = context.new_local_span(operation) |
| span&.start |
| |
| begin |
| yield span if block_given? |
| ensure |
| span&.stop? |
| end |
| end |
| end |
| end |
| end |
| end |