blob: c6c5e18fdd9931da8442f7533c2341a77011ae5c [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.
//
use crate::skywalking_proto::v3::{KeyStringValuePair, Log, SpanLayer, SpanObject, SpanType};
use std::fmt::Formatter;
use super::{
system_time::{fetch_time, TimePeriod},
trace_context::{TracingContext, WeakTracingContext},
};
/// 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::context::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 {
index: usize,
context: WeakTracingContext,
}
impl std::fmt::Debug for Span {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("Span");
match self.context.upgrade() {
Some(context) => {
let op = context.try_with_active_span_stack(|stack| {
d.field("data", &stack.get(self.index));
});
if op.is_none() {
d.field("data", &format_args!("<locked>"));
}
}
None => {
d.field("context", &format_args!("<dropped>"));
}
}
d.finish()
}
}
const SKYWALKING_RUST_COMPONENT_ID: i32 = 11000;
impl Span {
pub(crate) fn new(index: usize, context: WeakTracingContext) -> Self {
Self { index, context }
}
#[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 upgrade_context(&self) -> TracingContext {
self.context.upgrade().expect("Context has dropped")
}
// Notice: Perhaps in the future, `RwLock` can be used instead of `Mutex`, so `with_*` can be nested.
// (Although I can't find the meaning of such use at present.)
pub fn with_span_object<T>(&self, f: impl FnOnce(&SpanObject) -> T) -> T {
self.upgrade_context()
.with_active_span_stack(|stack| f(&stack[self.index]))
}
pub fn with_span_object_mut<T>(&mut self, f: impl FnOnce(&mut SpanObject) -> T) -> T {
self.upgrade_context()
.with_active_span_stack_mut(|stack| f(&mut stack[self.index]))
}
pub fn span_id(&self) -> i32 {
self.with_span_object(|span| span.span_id)
}
/// Add logs to the span.
pub fn add_log<K, V, I>(&mut self, message: I)
where
K: ToString,
V: ToString,
I: IntoIterator<Item = (K, V)>,
{
let log = Log {
time: fetch_time(TimePeriod::Log),
data: message
.into_iter()
.map(|v| {
let (key, value) = v;
KeyStringValuePair {
key: key.to_string(),
value: value.to_string(),
}
})
.collect(),
};
self.with_span_object_mut(|span| span.logs.push(log));
}
/// Add tag to the span.
pub fn add_tag(&mut self, key: impl ToString, value: impl ToString) {
self.with_span_object_mut(|span| {
span.tags.push(KeyStringValuePair {
key: key.to_string(),
value: value.to_string(),
})
})
}
}
impl Drop for Span {
/// Set the end time as current time, pop from context active span stack,
/// and push to context spans.
///
/// # Panics
///
/// Panic if context is dropped or this span isn't the active span.
fn drop(&mut self) {
if self.upgrade_context().finalize_span(self.index).is_err() {
panic!("Dropped span isn't the active span");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
trait AssertSend: Send {}
impl AssertSend for Span {}
}