use crate::{
common::{
system_time::{TimePeriod, fetch_time},
wait_group::WaitGroup,
},
proto::v3::{SpanLayer, SpanObject, SpanType},
trace::trace_context::{SpanStack, SpanUid},
};
use std::{
fmt::{self, Formatter},
mem::take,
sync::{Arc, Weak},
};
pub trait HandleSpanObject {
fn span_object(&self) -> &SpanObject;
fn span_object_mut(&mut self) -> &mut SpanObject;
fn span_id(&self) -> i32 {
self.span_object().span_id
}
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)
}
fn add_tag(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.span_object_mut().add_tag(key, value)
}
}
#[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()
}
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 {
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()
}
}
#[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 {
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::*;
#[allow(dead_code)]
trait AssertSend: Send + 'static {}
impl AssertSend for Span {}
impl AssertSend for AsyncSpan {}
}