blob: 50e5cb72b87639ecdfa22a25cf6953602a92683c [file] [log] [blame]
// 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.
//
//! Span is an important and common concept in distributed tracing system. Learn
//! Span from Google Dapper Paper.
use crate::{
common::{
system_time::{fetch_time, TimePeriod},
wait_group::WaitGroup,
},
proto::v3::{SpanLayer, SpanObject, SpanType},
trace::trace_context::{SpanStack, SpanUid},
};
use std::{
fmt::{self, Formatter},
mem::take,
sync::{Arc, Weak},
};
/// [HandleSpanObject] contains methods to handle [SpanObject].
pub trait HandleSpanObject {
/// Get immutable span object reference.
fn span_object(&self) -> &SpanObject;
/// Mutable with inner span object.
fn span_object_mut(&mut self) -> &mut SpanObject;
/// Get span id.
fn span_id(&self) -> i32 {
self.span_object().span_id
}
/// Add logs to the span.
fn add_log<K, V, I>(&mut self, message: I)
where
K: Into<String>,
V: Into<String>,
I: IntoIterator<Item = (K, V)>,
{
self.span_object_mut().add_log(message)
}
/// Add tag to the span.
fn add_tag(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.span_object_mut().add_tag(key, value)
}
}
/// Span is a concept that represents trace information for a single RPC.
/// The Rust SDK supports Entry Span to represent inbound to a service
/// and Exit Span to represent outbound from a service.
///
/// # Example
///
/// ```
/// use skywalking::trace::tracer::Tracer;
///
/// async fn handle_request(tracer: Tracer) {
/// let mut ctx = tracer.create_trace_context();
///
/// {
/// // Generate an Entry Span when a request is received.
/// // An Entry Span is generated only once per context.
/// // Assign a variable name to guard the span not to be dropped immediately.
/// let _span = ctx.create_entry_span("op1");
///
/// // Something...
///
/// {
/// // Generates an Exit Span when executing an RPC.
/// let _span2 = ctx.create_exit_span("op2", "remote_peer");
///
/// // Something...
///
/// // Auto close span2 when dropped.
/// }
///
/// // Auto close span when dropped.
/// }
///
/// // Auto report ctx when dropped.
/// }
/// ```
#[must_use = "assign a variable name to guard the span not be dropped immediately."]
pub struct Span {
uid: SpanUid,
obj: Option<SpanObject>,
wg: WaitGroup,
stack: Arc<SpanStack>,
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Span")
.field(
"data",
match self.obj {
Some(ref obj) => obj,
None => &"<none>",
},
)
.finish()
}
}
const SKYWALKING_RUST_COMPONENT_ID: i32 = 11000;
impl Span {
pub(crate) fn new(uid: SpanUid, obj: SpanObject, wg: WaitGroup, stack: Arc<SpanStack>) -> Self {
Self {
uid,
obj: Some(obj),
wg,
stack,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new_obj(
span_id: i32,
parent_span_id: i32,
operation_name: String,
remote_peer: String,
span_type: SpanType,
span_layer: SpanLayer,
skip_analysis: bool,
) -> SpanObject {
SpanObject {
span_id,
parent_span_id,
start_time: fetch_time(TimePeriod::Start),
operation_name,
peer: remote_peer,
span_type: span_type as i32,
span_layer: span_layer as i32,
component_id: SKYWALKING_RUST_COMPONENT_ID,
skip_analysis,
..Default::default()
}
}
fn is_active_span(&self) -> bool {
let active_spans = &*self.stack.active();
active_spans
.last()
.map(|span| span.uid() == self.uid)
.unwrap_or_default()
}
/// The [Span] finish at current tracing context, but the current span is
/// still alive, until [AsyncSpan] dropped.
///
/// This method must be called:
///
/// 1. In original thread (tracing context).
/// 2. Current span is active span.
///
/// During alive, tags, logs and attributes of the span could be changed, in
/// any thread.
///
/// # Panics
///
/// Current span could by active span.
pub fn prepare_for_async(mut self) -> AsyncSpan {
if !self.is_active_span() {
panic!("current span isn't active span");
}
self.wg.add(1);
AsyncSpan {
uid: self.uid,
wg: self.wg.clone(),
obj: take(&mut self.obj),
stack: Arc::downgrade(&self.stack),
}
}
}
impl Drop for Span {
/// Set the end time as current time, pop from context active span stack,
/// and push to context spans.
fn drop(&mut self) {
self.stack.finalize_span(self.uid, take(&mut self.obj));
}
}
impl HandleSpanObject for Span {
#[inline]
fn span_object(&self) -> &SpanObject {
self.obj.as_ref().unwrap()
}
#[inline]
fn span_object_mut(&mut self) -> &mut SpanObject {
self.obj.as_mut().unwrap()
}
}
/// Generated by [Span::prepare_for_async], tags, logs and attributes of the
/// span could be changed, in any thread.
///
/// It could be finished when dropped.
#[must_use = "assign a variable name to guard the active span not be dropped immediately."]
pub struct AsyncSpan {
uid: SpanUid,
obj: Option<SpanObject>,
wg: WaitGroup,
stack: Weak<SpanStack>,
}
impl fmt::Debug for AsyncSpan {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("AsyncSpan")
.field(
"data",
match self.obj {
Some(ref obj) => obj,
None => &"<none>",
},
)
.finish()
}
}
impl Drop for AsyncSpan {
/// Set the end time as current time.
fn drop(&mut self) {
self.stack
.upgrade()
.expect("TracingContext has dropped")
.finalize_async_span(self.uid, take(&mut self.obj).unwrap());
self.wg.done();
}
}
impl HandleSpanObject for AsyncSpan {
#[inline]
fn span_object(&self) -> &SpanObject {
self.obj.as_ref().unwrap()
}
#[inline]
fn span_object_mut(&mut self) -> &mut SpanObject {
self.obj.as_mut().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
trait AssertSend: Send + 'static {}
impl AssertSend for Span {}
impl AssertSend for AsyncSpan {}
}